Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Line to nearest neighbor

dave1707dave1707 Mod
edited January 11 in Code Sharing Posts: 6,828

Here’s another useless program. Each circle draws a fat line to its nearest neighbor. Tap the screen to add 5 more circles. I had nothing better to do.

displayMode(FULLSCREEN)

function setup()
    tab={}
    FPS=60
    create()
end

function create()
    for z=1,5 do
        local sp=2
        local t={}
        t.x=math.random(WIDTH)
        t.y=math.random(HEIGHT)
        t.xv=math.random(-sp,sp)
        t.yv=math.random(-sp,sp)
        t.dist=0    -- nearest neighbor distance
        t.x1=0      -- nearest neighbor x,y
        t.y1=0
        t.col=color(math.random(255),math.random(255),math.random(255))
        table.insert(tab,t)
    end
end

function draw()
    background(80, 80, 80, 54)
    fill(255)
    text("Tap screen for 5 more circles",WIDTH/2,HEIGHT-20)
    text("Circles "..#tab,WIDTH/2,HEIGHT-40)
    text("FPS "..FPS//1,WIDTH/2,HEIGHT-60)
    -- find nearest neighbor
    for a,b in pairs(tab) do
        local v1=vec2(b.x,b.y)
        b.dist=9999 
        for c,d in pairs(tab) do
            local v2=vec2(d.x,d.y)
            local dst=v1:dist(v2)
            if dst>0 and dst<b.dist then
                b.dist=dst
                b.x1=d.x
                b.y1=d.y
            end            
        end
    end
    -- draw lines
    for a,b in pairs(tab) do
        stroke(b.col)
        strokeWidth(40)
        line(b.x,b.y,b.x1,b.y1)
    end
    -- draw circles over lines
    for a,b in pairs(tab) do
        fill(0)
        noStroke()
        ellipse(b.x,b.y,10)
        stroke(0)
        strokeWidth(5)
        line(b.x,b.y,b.x-(b.x-b.x1)/4,b.y-(b.y-b.y1)/4)
    end
    -- change position and reverse direction at edges
    for a,b in pairs(tab) do
        b.x=b.x+b.xv
        b.y=b.y+b.yv
        if b.x<0 or b.x>WIDTH then
            b.xv=-b.xv
        end
        if b.y<0 or b.y>HEIGHT then
            b.yv=-b.yv
        end        
    end
    FPS = FPS * 0.9 + 0.1 / DeltaTime
end

function touched(t)
    if t.state==BEGAN then
        create()
    end
end

Comments

  • Posts: 402

    That's quite fun.

    I took the liberty of vecing it.

    -- NeighbourLines
    
    displayMode(FULLSCREEN)
    
    function setup()
        tab={}
        FPS=60
        create()
    end
    
    function create()
        local t,sp
        sp=2
        for z=1,5 do
            t={}
            t.p=vec2(math.random(WIDTH),math.random(HEIGHT))
            t.v=vec2(math.random(-sp,sp),math.random(-sp,sp))
            t.col=randomColour()
            table.insert(tab,t)
        end
    end
    
    function randomColour()
        local h = math.random()*6
        local x = 255*(1 - math.abs(h%2 - 1))
        local r,g,b
        if h < 1 then
            r,g,b = 255,x,0
        elseif h < 2 then
            r,g,b = x,255,0
        elseif h < 3 then
            r,g,b = 0,255,x
        elseif h < 4 then
            r,g,b = 0,x,255
        elseif h < 5 then
            r,g,b = x,0,255
        else
            r,g,b = 255,0,x
        end
        return color(r,g,b)
    end
    
    function draw()
        background(80, 80, 80, 54)
        fill(255)
        text("Tap screen for 5 more circles",WIDTH/2,HEIGHT-20)
        text("Circles "..#tab,WIDTH/2,HEIGHT-40)
        text("FPS "..FPS//1,WIDTH/2,HEIGHT-60)
    
        local dists = {}
        local d
        -- work out all the distances in one fell swoop
        for k,v in ipairs(tab) do
            dists[k] = {}
            for l=1,k-1 do
                d = v.p:dist(tab[l].p)
                dists[k][l] = {d,tab[l]}
                dists[l][k] = {d,tab[k]}
            end
            -- ensure that the closest point is not itself
            dists[k][k] = {math.max(WIDTH,HEIGHT),v}
        end
        -- find closest neighbour by sorting the rows
        for k,v in ipairs(dists) do
            table.sort(v,function(a,b) return a[1] < b[1] end)
        end
        -- draw lines
        strokeWidth(40)
        local u
        for k,v in ipairs(tab) do
            stroke(v.col)
            u = dists[k][1][2]
            line(v.p.x,v.p.y,u.p.x,u.p.y)
        end
        -- draw circles over lines
        fill(0)
        noStroke()
        for k,v in ipairs(tab) do
            ellipse(v.p.x,v.p.y,10)
        end
        -- change position and reverse direction at edges
        for a,b in pairs(tab) do
            b.p = b.p + b.v
            if b.p.x<0 or b.p.x>WIDTH then
                b.v.x=-b.v.x
            end
            if b.p.y<0 or b.p.y>HEIGHT then
                b.v.y=-b.v.y
            end        
        end
        FPS = FPS * 0.9 + 0.1 / DeltaTime
    end
    
    function touched(t)
        if t.state==BEGAN then
            create()
        end
    end
    
  • dave1707dave1707 Mod
    edited January 11 Posts: 6,828

    Made changes to my code at the top so the circles point to its nearest neighbor. That helps to see who the closest neighbor is. @LoopSpace I’m still trying to figure out why the vectors work in the calcP3 function of the other program. I printed the different values in the calcP3 function, but why they work I haven’t figured out yet.

  • Posts: 402

    To make the same change in my code:

    u = dists[k][1][2].p/4 + 3* v.p/4
    line(v.p.x,v.p.y,u.x,u.y)
    

    I'll write up an explanation of the code in the calcP3 function in a bit.

  • Posts: 402

    I thought that the shorter lines behaved like springs, so I put in forces and acceleration.

    -- NeighbourLines
    
    displayMode(FULLSCREEN)
    
    function setup()
        tab={}
        FPS=60
        create()
    end
    
    function create()
        local t,sp
        sp=2
        for z=1,5 do
            t={}
            t.p=vec2(math.random(WIDTH),math.random(HEIGHT))
            t.v=vec2(0,0)
            t.a=vec2(0,0)
            t.col=randomColour()
            table.insert(tab,t)
        end
    end
    
    function randomColour()
        local h = math.random()*6
        local x = 255*(1 - math.abs(h%2 - 1))
        local r,g,b
        if h < 1 then
            r,g,b = 255,x,0
        elseif h < 2 then
            r,g,b = x,255,0
        elseif h < 3 then
            r,g,b = 0,255,x
        elseif h < 4 then
            r,g,b = 0,x,255
        elseif h < 5 then
            r,g,b = x,0,255
        else
            r,g,b = 255,0,x
        end
        return color(r,g,b)
    end
    
    function draw()
        background(80, 80, 80, 54)
        fill(255)
        text("Tap screen for 5 more circles",WIDTH/2,HEIGHT-20)
        text("Circles "..#tab,WIDTH/2,HEIGHT-40)
        text("FPS "..FPS//1,WIDTH/2,HEIGHT-60)
    
        local dists = {}
        local d
        -- work out all the distances in one fell swoop
        for k,v in ipairs(tab) do
            dists[k] = {}
            for l=1,k-1 do
                d = v.p:dist(tab[l].p)
                dists[k][l] = {d,tab[l]}
                dists[l][k] = {d,tab[k]}
            end
            -- ensure that the closest point is not itself
            dists[k][k] = {math.max(WIDTH,HEIGHT),v}
        end
        -- find closest neighbour by sorting the rows
        for k,v in ipairs(dists) do
            table.sort(v,function(a,b) return a[1] < b[1] end)
        end
        -- draw lines
        strokeWidth(40)
        local u
        for k,v in ipairs(tab) do
            stroke(v.col)
            u = dists[k][1][2].p/4 + 3*v.p/4
            line(v.p.x,v.p.y,u.x,u.y)
        end
        -- draw circles over lines
        fill(0)
        noStroke()
        for k,v in ipairs(tab) do
            ellipse(v.p.x,v.p.y,10)
        end
        -- change position and reverse direction at edges
        for a,b in pairs(tab) do
            b.p = b.p + DeltaTime * b.v
            b.v = b.v + DeltaTime * b.a
            b.a = (dists[a][1][2].p - b.p)
            b.a = b.a*(b.a:len() - 150)/3000
            if b.p.x<0 then
                b.v.x = math.abs(b.v.x)
                b.p.x = math.abs(b.p.x)
            end
            if b.p.x>WIDTH then
                b.v.x = -math.abs(b.v.x)
                b.p.x = 2*WIDTH - b.p.x
            end
            if b.p.y<0 then
                b.v.y = math.abs(b.v.y)
                b.p.y = math.abs(b.p.y)
            end
            if b.p.y>HEIGHT then
                b.v.y = -math.abs(b.v.y)
                b.p.y = 2*HEIGHT - b.p.y
            end        
        end
        FPS = FPS * 0.9 + 0.1 / DeltaTime
    end
    
    function touched(t)
        if t.state==BEGAN then
            create()
        end
    end
    
  • dave1707dave1707 Mod
    Posts: 6,828

    @LoopSpace Interesting changes. Kind of reminds me of dodgem cars.

Sign In or Register to comment.