Infinite Scrolling

I’ve been working on a random path generator using Perlin noise, and it works pretty well, but I’ve been having problems trying to test its to create extremely long paths; right now I’m just plotting the path on a really big image, but it runs out of space pretty quickly. Is there any better way to do this?

For the record, here’s my current source:

--# Main
-- Trails

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    
    alg = 2
    size = 1/100
    speed = 1
    rsize = 0.1
    
    rot = 1
    
    fspeed = 100
    
    cp = vec2(WIDTH/2/rsize-100, HEIGHT/4/rsize+100)

    background(255)
    ---[[
    im = image(WIDTH*2, HEIGHT*2)
    
    spriteMode(CORNER)
    
    xmin = 9^99
    xmax = -1
    ymin = 9^99
    ymax = -1
    
    --points = {}
    --backingMode(RETAINED)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(255)
    
    setContext(im)
    
    if ElapsedTime < 0.5 then background(255) end -- Sometimes needed, idk
    strokeWidth(1)
    resetMatrix()

    -- Do your drawing here
    for n = 1, fspeed do
        local p = cp
        cp = algs[alg](cp, size, rot)
        
        stroke(0, 0, 0, 255)
        fill(0, 0, 0, 255)
        
        line(p.x * rsize, p.y * rsize, cp.x * rsize, cp.y * rsize)
    end
    
    setContext()
    
    translate(math.floor((WIDTH / 2) - cp.x*rsize), math.floor((HEIGHT - 50) - cp.y*rsize))
    sprite(im, 0,0, im.width, im.height)
    
    resetMatrix()
    
end


--# Algs
algs = {}

algs[1] = function(np, s)
    np.x = np.x + noise(np.x*s)*speed
    np.y = np.y + noise(np.y*s)*speed
    return np
end

algs[2] = function(np, s, rot)
    local r = noise(np.x*s, np.y*s)*speed
    
    np.x = np.x + math.sin(r*6*math.pi*rot) * speed
    np.y = np.y + math.cos(r*6*math.pi*rot) * speed
    return np
end

algs[3] = function(np, s)
    local x = noise(np.x*s)*speed
    local y = noise(np.y*s)*speed
    
    if x == 0 then x = 0.05 end
    if y == 0 then y = 0.05 end
    
    x, y = x / (x + y), y / (x + y)
    
    np.x = np.x + x * speed
    np.y = np.y + y * speed
    return np
end

```

@FLCode What do you mean it runs out of space. What I see is it is drawing beyond the edges of the image im. It’s still going, but you can’t see where it’s plotting. I’m going to try something else.

@FLCode Try this. I stripped out code I wasn’t using just to keep it small. Use your finger to scroll the image area around to follow the line. Once it goes off the image area, you can stop the code. Change the values in the line cp=vec2(6000,195) to cause different paths.


--# Main
-- Trails

displayMode(FULLSCREEN) 

function setup()
    spriteMode(CORNER)
    dx,dy=0,0
    alg = 2
    size = 1/100
    speed = 1
    rsize = 0.1 
    rot = 1 
    fspeed = 100
    cp=vec2(6000,195)    
    im=image(WIDTH*2,HEIGHT*2)
    setContext(im)
    background(255)   
    setContext()
    stroke(0)
    strokeWidth(1)
end

function draw()
    background(0)
    setContext(im) 
    for n = 1, fspeed do
        local p = cp
        cp = algs(cp, size, rot) 
        line(p.x * rsize, p.y * rsize, cp.x * rsize, cp.y * rsize)
    end 
    setContext() 
    sprite(im, dx,dy, im.width, im.height) 
end

function touched(t)
    if t.state==MOVING then
        dx=dx+t.deltaX
        dy=dy+t.deltaY
    end
end

function algs(np, s, rot)
    local r = noise(np.x*s, np.y*s)*speed 
    np.x = np.x + math.sin(r*6*math.pi*rot) * speed
    np.y = np.y + math.cos(r*6*math.pi*rot) * speed
    return np
end

@FLCode this is completely off topic, but how did you get your code to format so nicely in your first post?

@yojimbo2000

< pre lang=“lua” >

code

< /pre >

but remove the space after < and before >

@dave1707 great, thanks! And apologies for thread hijacking

Here is a slightly modified version of @dave1707’s code which has a second image area directly above it (I’ve coloured it differently so you can see the difference) which gives you double the area.

Making it infinite is going to be difficult as you are going to need to store an infinite number of these images. However you could use this approach to detect if you were near the edge of an image and generate a new one at that edge to progress into.



--# Main
-- Trails

displayMode(FULLSCREEN)

function setup()
    spriteMode(CORNER)
    dx,dy=0,0
    alg = 2
    size = 1/100
    speed = 1
    rsize = 0.1
    rot = 1
    fspeed = 100
    cp=vec2(6000,195)
    im=image(WIDTH*2,HEIGHT*2)
    setContext(im)
    background(255)
    setContext()
    im2=image(WIDTH*2,HEIGHT*2)
    setContext(im2)
    background(221, 232, 82, 255)
    setContext()
    stroke(0)
    strokeWidth(1)
end

function draw()
    background(0)
    for n = 1, fspeed do
        local p = cp
        cp = algs(cp, size, rot)
        if p.y*rsize>HEIGHT*2 then
            setContext(im2)
            line(p.x * rsize, p.y * rsize-2*HEIGHT, cp.x * rsize, cp.y * rsize-2*HEIGHT)
            setContext()
        else
            setContext(im)
            line(p.x * rsize, p.y * rsize, cp.x * rsize, cp.y * rsize)
            setContext()
        end
    end
    sprite(im, dx,dy, im.width, im.height)
    sprite(im2, dx,dy+2*HEIGHT, im.width, im.height)
end

function touched(t)
    if t.state==MOVING then
        dx=dx+t.deltaX
        dy=dy+t.deltaY
    end
end

function algs(np, s, rot)
    local r = noise(np.x*s, np.y*s)*speed
    np.x = np.x + math.sin(r*6*math.pi*rot) * speed
    np.y = np.y + math.cos(r*6*math.pi*rot) * speed
    return np
end

Problem with @West approach is because you can always scroll back you need to ultimately keep an infinite number of images.

Back to the original where it was walking, I’ve done something which I believe achieves the goal, except, for some reason there’s something with sprite scaling going on I don’t understand, it causes the trail to slowly blur behind you. Also, it does eventually stop, but I suspect this is the noise function blowing out a variable perhaps…

Also, note if it doubles back far enough it’ll find it’s trail had been trimmed.

--# Main
-- Trails
 
-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
 
    alg = 2
    size = 1/100
    speed = 1
    rsize = 0.1
 
    rot = 1
 
    fspeed = 100
 
    cp = vec2(WIDTH/2/rsize-100, HEIGHT/4/rsize+100)
 
    background(255)
    
    im1 = image(WIDTH*2, HEIGHT*2)
    im2 = image(WIDTH*2, HEIGHT*2)
 
    setContext(im1)
    background(255)
    setContext(im2)
    background(255)
 
    spriteMode(CORNER)
 
    xmin = 9^99
    xmax = -1
    ymin = 9^99
    ymax = -1
 
    curIm = 1
    --points = {}
    --backingMode(RETAINED)
end
 
-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(255)
 
    if curIm == 1 then
        im = im1
        previm = im2
        curIm = 2
    else
        im = im2
        previm = im1
        curIm = 1
    end
    
    setContext(im)
    
    strokeWidth(1)
 
    translate(WIDTH, HEIGHT*3/2 - 50)

    local pOrigin = vec2(cp.x,cp.y)
    output.clear()
    print(pOrigin)
    for n = 1, fspeed do
        local p = vec2(cp.x, cp.y)
        cp = algs[alg](cp, size, rot)
 
        stroke(0, 0, 0, 255)
        fill(0, 0, 0, 255)
 
        line(p.x * rsize - pOrigin.x * rsize, p.y * rsize - pOrigin.y * rsize, cp.x * rsize - pOrigin.x * rsize, cp.y * rsize - pOrigin.y * rsize)

    end
 
    setContext(previm)
    background(255)
    resetMatrix()
    translate(pOrigin.x * rsize - cp.x * rsize, pOrigin.y * rsize - cp.y * rsize)
    
    sprite(im , 0, 0, im.width, im.height)
 
    setContext()
    resetMatrix()
    scale(2)
    --sprite(im, 0,0,WIDTH, HEIGHT)
    sprite(im, -WIDTH/2,-HEIGHT/2, im.width, im.height)
 
end
 
 
--# Algs
algs = {}
 
algs[1] = function(np, s)
    np.x = np.x + noise(np.x*s)*speed
    np.y = np.y + noise(np.y*s)*speed
    return np
end
 
algs[2] = function(np, s, rot)
    local r = noise(np.x*s, np.y*s)*speed
 
    np.x = np.x + math.sin(r*6*math.pi*rot) * speed
    np.y = np.y + math.cos(r*6*math.pi*rot) * speed
    return np
end
 
algs[3] = function(np, s)
    local x = noise(np.x*s)*speed
    local y = noise(np.y*s)*speed
 
    if x == 0 then x = 0.05 end
    if y == 0 then y = 0.05 end
 
    x, y = x / (x + y), y / (x + y)
 
    np.x = np.x + x * speed
    np.y = np.y + y * speed
    return np
end

Actually, playing with it a little more, I think it “stops” not because a variable broke, but because the noise is deterministic on the coordinate. I think it hit’s points where it will just oscillate between two points forever. Maybe trying getting ElapsedTime into the noise somewhere or trying some other noise function… The below tweaked version seems to run for a long time, if not forever…

Also fixed the fading… it was a smooth (anti aliasing) on the spriting building up over many cycles. This is also fixed below.

--# Main

-- Trails

 

-- Use this function to perform your initial setup

function setup()
    displayMode(FULLSCREEN)

    alg = 2
    size = 1/100
    speed = 1
    rsize = 0.1

    rot = 1

    fspeed = 100

    cp = vec2(15000,15000)
    --cp = vec2(WIDTH/2/rsize-100, HEIGHT/4/rsize+100)

    background(255)

    im1 = image(WIDTH*2, HEIGHT*2)
    im2 = image(WIDTH*2, HEIGHT*2)

    setContext(im1)
    background(255)
    setContext(im2)
    background(255)

    spriteMode(CORNER)

    xmin = 9^99
    xmax = -1
    ymin = 9^99
    ymax = -1
    
    curIm = 1

end

 

-- This function gets called once every frame

function draw()

    -- This sets a dark background color 

    background(255)

    if curIm == 1 then
        im = im1
        previm = im2
        curIm = 2
    else
        im = im2
        previm = im1
        curIm = 1
    end

    setContext(im)

    strokeWidth(1)
    smooth()
    translate(WIDTH, HEIGHT*3/2 - 50)

    local pOrigin = vec2(cp.x,cp.y)

    for n = 1, fspeed do
        local p = vec2(cp.x, cp.y)
        cp = algs[alg](cp, size, rot)
        stroke(0, 0, 0, 255)
        fill(0, 0, 0, 255)

        line(p.x * rsize - pOrigin.x * rsize, p.y * rsize - pOrigin.y * rsize, cp.x * rsize - pOrigin.x * rsize, cp.y * rsize - pOrigin.y * rsize)
    end
    noSmooth()
    --recenter in secondary buffer (I think this is where the blurring leaks in...)
    setContext(previm)
    background(255)
    resetMatrix()
    translate(pOrigin.x * rsize - cp.x * rsize, pOrigin.y * rsize - cp.y * rsize)
    sprite(im , 0, 0, im.width, im.height)

    --draw to screen
    setContext()
    resetMatrix()
    scale(2)
    sprite(im, -WIDTH/2,-HEIGHT/2, im.width, im.height)
end

--# Algs
algs = {}

algs[1] = function(np, s)
    np.x = np.x + noise(np.x*s)*speed
    np.y = np.y + noise(np.y*s)*speed
    return np
end

algs[2] = function(np, s, rot)
    local r = noise(np.x*s, np.y*s, ElapsedTime * 0.1)*speed
    np.x = np.x + math.sin(r*6*math.pi*rot) * speed
    np.y = np.y + math.cos(r*6*math.pi*rot) * speed
    return np
end

algs[3] = function(np, s)
    local x = noise(np.x*s)*speed
    local y = noise(np.y*s)*speed
    if x == 0 then x = 0.05 end
    if y == 0 then y = 0.05 end
    x, y = x / (x + y), y / (x + y)

    np.x = np.x + x * speed
    np.y = np.y + y * speed
    return np
end

@spacemonkey is correct. I altered my code so it went to 4 different images and when it stopped, I printed out the values. They just kept flipping between the same 2 values.