Howdy, Stranger!

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

Codea is a perfect match for this...

Jmv38Jmv38 Mod
in Suggestions Posts: 3,297

who wants to try this first?

Dave?

Comments

  • dave1707dave1707 Mod
    edited January 2019 Posts: 9,091

    @Jmv38 Nice little demo. I’ll watch the whole thing when I have more time.

  • Jmv38Jmv38 Mod
    edited January 2019 Posts: 3,297

    @dave1707 the video is not from me. And i have not written the code, i just noticed it is very easy to implement with Codea.

  • dave1707dave1707 Mod
    Posts: 9,091

    @Jmv38 After watching more of the video, I realized that it was a video by someone else. I’m kind of working on this, but I’m not sure what some the variable are in the formula do/dt=a+BNtsign(Rt-Lt). a is the angle, Nt is the number of objects within a radius of an object. sign(Rt-Lt) is the sign (+1 or -1) of the number of items to the right or left of an object depending on its orientation. I’m assuming do/dt is the angle change that gets added to the angle a. I’m not sure what B is. So far I have a bunch of circles moving around the screen based on their angle and I’m counting the number of circles within a distance of each circle. Next step is to determine how many objects are on the right or left of the circles orientation.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    great!
    I thought of using sensor and collisions to have fast built-in acces to close objects.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    B is a constant that characterize the physics of this world. At the end of the video they show which couples of A,B values generate interesting worlds.

  • dave1707dave1707 Mod
    edited January 2019 Posts: 9,091

    @Jmv38 Here’s what I have so far. It doesn’t look like the video, but it does eventually start to clump. Apparently I’m not doing something right, but I not sure where and I’m tired of working on it for now. I don’t know if I need to do all the calculations before I change the positions of each point. Sort of like the game of life code. I’ll try that eventually.

    PS. I modified the original code to make it faster, starts from random points, and color based on it distance to a nrighbor.

    displayMode(FULLSCREEN)
    
    function setup()
        A1,B1=1,1
        cnt=0
        rad=40  -- radius
        tab={}
        for z=1,400 do
            table.insert(tab,{x=math.random(80,WIDTH-80),y=math.random(80,HEIGHT-80),
            ang=math.random(360),lft=0,rgt=0,col=color(0,0,0)} )
        end
    end
    
    function draw()
        background(40, 40, 50)
        for a,b in pairs(tab) do
            fill(b.col)        
            b.x=b.x+math.cos(math.rad(b.ang))
            b.y=b.y+math.sin(math.rad(b.ang))
            n=b.lft+b.rgt
            s=sign(b.lft,b.rgt)
            b.ang=(b.ang+(A1+B1*n*s)%360)%360
            ellipse(b.x,b.y,6)
        end
        dist()
        cnt=cnt+1
        fill(255)
        text(cnt,WIDTH/2,HEIGHT-25)
    end
    
    -- determine if the sign is positive of negative 
    function sign(l,r)
        if r>l then return -1 end
        if l>r then return 1 end
        return 0
    end
    
    -- determine if a point is within the radius of another point
    function dist()
        for a,b in pairs(tab) do
            b.lft,b.rgt=0,0
            b.col=color(0,255,0)
        end
        for z=1,#tab do
            v=vec2(tab[z].x,tab[z].y)
            for y=z+1,#tab do
                d=v:dist(vec2(tab[y].x,tab[y].y))
                if d<rad then
                    pos(tab[z],tab[y],z,y)
                    pos(tab[y],tab[z],z,y)
                    setColor(d,z,y)
                end
            end        
        end
    end
    
    function setColor(d,z,y)
        if d>rad*.6 then
            tab[z].col=color(0,0,255)
            tab[y].col=color(0,0,255)
        elseif d>rad*.4 then
            tab[z].col=color(255,255,0)
            tab[y].col=color(255,255,0)
        elseif d>rad*.2 then
            tab[z].col=color(255,0,255)
            tab[y].col=color(255,0,255)
        else
            tab[z].col=color(255,0,0)
            tab[y].col=color(255,0,0)
        end
    end
    
    -- determine how many points are on the right or left side of the point axis
    function pos(orig,next)
        ang1=math.deg(math.atan(next.y-orig.y,next.x-orig.x))
        if ang1<0 then ang1=360+ang1 end    
        if orig.ang>=180 then
            if ang1<orig.ang and ang1>orig.ang-180 then
                orig.rgt=orig.rgt+1
            else
                orig.lft=orig.lft+1
            end
        else
            if ang1>=orig.ang and ang1<orig.ang+180 then
                orig.lft=orig.lft+1
            else
                orig.rgt=orig.rgt+1
            end
        end    
    end
    
  • SimeonSimeon Admin Mod
    Posts: 5,581

    That's pretty cool, you end up with little neighbourhoods around step 250

  • dave1707dave1707 Mod
    Posts: 9,091

    @Simeon I modified the above code. It’s a little faster, starts with random points, and color based on it distance to a neighbor.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    @dave1707 i’ve restarted from your project, here is my version.


    --# Grid Grid = class() -- this is just a tool to find neighbours faster (improves fps) local rad, halfRad local w,h,imax,jmax local grid = {} local floor = math.floor function Grid:init(R) rad = R halfRad = R/2 w,h = WIDTH, HEIGHT imax,jmax = self:get_ij(w,h) for i=1,imax do local col = {} grid[i] = col for j=1,jmax do col[j] = {} end end end function Grid:update(a) local i0,j0 = a.i,a.j local i,j = Grid:get_ij(a.x,a.y) if (i~=i0) or (j~=j0) then self:reset(i0-1,j0-1,a) self:reset(i0-1,j0 ,a) self:reset(i0-1,j0+1,a) self:reset(i0 ,j0-1,a) self:reset(i0 ,j0 ,a) self:reset(i0 ,j0+1,a) self:reset(i0+1,j0-1,a) self:reset(i0+1,j0 ,a) self:reset(i0+1,j0+1,a) self:set(i-1,j-1,a) self:set(i-1,j ,a) self:set(i-1,j+1,a) self:set(i ,j-1,a) self:set(i ,j ,a) self:set(i ,j+1,a) self:set(i+1,j-1,a) self:set(i+1,j ,a) self:set(i+1,j+1,a) a.i,a.j = i,j end end function Grid:neighbours(a) local i,j = a.i, a.j i = (i-1)%imax + 1 j = (j-1)%jmax + 1 return grid[i][j] end function Grid:set(i,j,a) i = (i-1)%imax + 1 j = (j-1)%jmax + 1 grid[i][j][a] = a end function Grid:reset(i,j,a) i = (i-1)%imax + 1 j = (j-1)%jmax + 1 grid[i][j][a] = nil end function Grid:get_ij(x,y) local i,j = floor( (x + halfRad)/rad )+1, floor( (y + halfRad)/rad )+1 return i,j end --# Object Object = class() -- these values completely defines the objects local A1, B1, R, S = 180, 17, 30, 3 local A1, B1, R, S = 180, 17, 30, 2 local A1, B1, R, S = 180, 17, 30, 3 -- the number of objects / size of the initial ground are important too nbrObjects = 500 size = 380 local grid = false function Object:init(w,h) if not grid then -- init the grid just once grid = Grid(R) end -- random position and angle within specified bounds self.x=math.random((WIDTH-w)/2,(WIDTH+w)/2) self.y=math.random((HEIGHT-h)/2,(HEIGHT+h)/2) self.i = 1 self.j = 1 self.ang=math.random(360) -- init speed self.speed = S self.dx = math.cos(math.rad(self.ang))*self.speed self.dy = math.sin(math.rad(self.ang))*self.speed -- init sensing radius self.dotRadius = 6 self.colorRadius = 10 self.sensingRadius = R self.col=color(0,255,0) end local mod = math.fmod local min = math.min function Object:update() -- move according current direction and speed local x,y x = self.x + self.dx y = self.y + self.dy -- make the world a torus x = mod(x +WIDTH , WIDTH ) y = mod(y +HEIGHT, HEIGHT) -- now update positions self.x = x self.y = y grid:update(self) -- manage neighbour impact local A = vec2(self.x,self.y) local nbClose = 0 local side = 0 local d,n,lft,rgt = 0,0,0,0 local B = vec2(0,0) for b,b in pairs( grid:neighbours(self) ) do if b~=self then B.x, B.y = b.x, b.y d = A:dist(B) if d<self.sensingRadius then if d < self.colorRadius then nbClose = nbClose + 1 end side = -self.dy*(b.x-self.x) + self.dx*(b.y-self.y) if side > 0 then lft = lft + 1 else rgt = rgt + 1 end end end end -- turn according to neighbours n = lft + rgt local sgn = 0 if lft < rgt then sgn = -1 else sgn = 1 end self.ang=(self.ang+(A1+B1*n*sgn))%360 self.dx = math.cos(math.rad(self.ang))*self.speed self.dy = math.sin(math.rad(self.ang))*self.speed -- define color according to nb of close neighbours local col if nbClose < 1 then col = color(0,255,0) elseif nbClose < 2 then col = color(0,92,255) elseif nbClose < 4 then col = color(255,255,0) elseif nbClose < 8 then col = color(255,0,255) else col=color(255,0,0) end self.col=col end function Object:draw() fill(self.col) ellipse(self.x, self.y, self.dotRadius) end --# Main displayMode(FULLSCREEN) function setup() cnt=0 tab={} for z=1,nbrObjects do table.insert(tab, Object(size,size) ) end end function draw() background(40, 40, 50) for a,b in pairs(tab) do b:draw() b:update() end cnt=cnt+1 fill(255) text(cnt,WIDTH/2,HEIGHT-25) end
  • dave1707dave1707 Mod
    Posts: 9,091

    @Jmv38 You’re code is working the way it should. I’ll have to look thru it to see what I’m doing wrong in my code. Apparently I don’t understand what’s really happening.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    i dont think you are doing anything wrong.
    Starting from your code was simpler for me, thanks!
    I just introduced a faster way to locate the neighbours via the ‘grid’.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    a more interactive version


    --# Object Object = class() -- these values completely defines the objects local A1, B1, R, S = 180, 17, 30, 3 local A1, B1, R, S = 180, 17, 30, 2 local A1, B1, R, S = 180, 17, 35, 4 -- the number of objects / size of the initial ground are important too nbrObjects = 700 size = 500 local grid = false function Object:init(w,h,x,y) if not grid then -- init the grid just once grid = Grid(R) end -- random position and angle within specified bounds if x == nil then self.x=math.random((WIDTH-w)/2,(WIDTH+w)/2) self.y=math.random((HEIGHT-h)/2,(HEIGHT+h)/2) else self.x = x self.y = y end self.i = 1 self.j = 1 self.ang=math.random(360) -- init speed self.speed = S self.dx = math.cos(math.rad(self.ang))*self.speed self.dy = math.sin(math.rad(self.ang))*self.speed -- init sensing radius self.dotRadius = 6 self.colorRadius = 10 self.sensingRadius = R self.col=color(0,255,0) end local mod = math.fmod local min = math.min function Object:update() -- move according current direction and speed local x,y x = self.x + self.dx y = self.y + self.dy -- make the world a torus x = mod(x +WIDTH , WIDTH ) y = mod(y +HEIGHT, HEIGHT) -- now update positions self.x = x self.y = y grid:update(self) -- manage neighbour impact local A = vec2(self.x,self.y) local nbClose = 0 local side = 0 local d,n,lft,rgt = 0,0,0,0 local B = vec2(0,0) for b,b in pairs( grid:neighbours(self) ) do if b~=self then B.x, B.y = b.x, b.y d = A:dist(B) if d<self.sensingRadius then if d < self.colorRadius then nbClose = nbClose + 1 end side = -self.dy*(b.x-self.x) + self.dx*(b.y-self.y) if side > 0 then lft = lft + 1 else rgt = rgt + 1 end end end end -- turn according to neighbours n = lft + rgt local sgn = 0 if lft < rgt then sgn = -1 else sgn = 1 end self.ang=(self.ang+(A1+B1*n*sgn))%360 self.dx = math.cos(math.rad(self.ang))*self.speed self.dy = math.sin(math.rad(self.ang))*self.speed -- define color according to nb of close neighbours local col if nbClose < 1 then col = color(0,255,0) elseif nbClose < 8 then col = color(0,92,255) elseif nbClose < 12 then col = color(255,255,0) elseif nbClose < 16 then col = color(255,0,255) else col=color(255,0,0) end self.col=col end function Object:draw() fill(self.col) ellipse(self.x, self.y, self.dotRadius) end --# Grid Grid = class() -- this is just a tool to find neighbours faster (improves fps) local rad, halfRad local w,h,imax,jmax local grid = {} local floor = math.floor function Grid:init(R) rad = R halfRad = R/2 w,h = WIDTH, HEIGHT imax,jmax = self:get_ij(w,h) for i=1,imax do local col = {} grid[i] = col for j=1,jmax do col[j] = {} end end end function Grid:update(a) local i0,j0 = a.i,a.j local i,j = Grid:get_ij(a.x,a.y) if (i~=i0) or (j~=j0) then for di =-1,1 do for dj =-1,1 do self:reset(i0+di,j0+dj,a) end end for di =-1,1 do for dj =-1,1 do self:set(i+di,j+dj,a) end end a.i,a.j = i,j end end function Grid:neighbours(a) local i,j = a.i, a.j i = (i-1)%imax + 1 j = (j-1)%jmax + 1 return grid[i][j] end function Grid:set(i,j,a) i = (i-1)%imax + 1 j = (j-1)%jmax + 1 grid[i][j][a] = a end function Grid:reset(i,j,a) i = (i-1)%imax + 1 j = (j-1)%jmax + 1 grid[i][j][a] = nil end function Grid:get_ij(x,y) local i,j = floor( (x + halfRad)/rad )+1, floor( (y + halfRad)/rad )+1 return i,j end --# Main displayMode(FULLSCREEN) local t function setup() cnt=0 tab={} t=0 for z=1,nbrObjects do -- table.insert(tab, Object(size,size) ) end showText = "swipe the screen to add objects" end floor = math.floor function draw() background(40, 40, 50) for a,b in pairs(tab) do b:draw() b:update() end if false and floor(ElapsedTime) > t then t = floor(ElapsedTime) +1 table.insert(tab, Object(size,size) ) end if showText then fill(255) text(showText,WIDTH/2,HEIGHT/2) else cnt=cnt+1 fill(255) text(cnt,WIDTH/2,HEIGHT-25) end end function touched(touch) showText = nil table.insert(tab, Object(size,size,touch.x,touch.y) ) end
  • dave1707dave1707 Mod
    Posts: 9,091

    @Jmv38 This new version works great. I made a slight change to it for myself. I added width and height parameters so I could squeeze the size of the working area. I would fill the screen with green dots and then start reducing the width and height. It was like I was increasing the pressure inside the area with more and more dots turning to red. I also found out what I was doing wrong with my code. When I changed A1 to 180 and B1 to 17, it starting acting right. I also wasn’t limiting the size of the working area. So it was just increasing in size off screen and not forcing the dots to interact that much.

  • Ha! That’s so great! Well done both!

  • Posts: 921

    @dave1707 would you be willing to paste in your squeezy version here?

  • dave1707dave1707 Mod
    Posts: 9,091

    @UberGoober I tried searching for it, but I guess I never saved it or else I deleted it.

  • Posts: 921
    Aw rats, it seemed like a neat mod. The original is still darn cool though.
  • dave1707dave1707 Mod
    Posts: 9,091

    @UberGoober Here’s the above code with changes to force the dots into a smaller area. Move the parameter slider. Depending on how fast you move it, you’ll miss some and have to expand it and redo it. It’s not that great which is probably why I didn’t post or save it.

    viewer.mode=STANDARD
    
    local t
    
    function setup()
        parameter.integer("wid",0,500)    
        cnt=0
        tab={}
        t=0
        for z=1,nbrObjects do
            -- table.insert(tab, Object(size,size) )
        end
        showText = "swipe the screen to add objects"
    end
    
    floor = math.floor
    
    function draw()
        background(40, 40, 50)
        for a,b in pairs(tab) do
            b:draw()
            b:update()
        end
        if false and floor(ElapsedTime) > t then
            t = floor(ElapsedTime) +1
            table.insert(tab, Object(size,size) )
        end
        if showText then
            fill(255)
            text(showText,WIDTH/2,HEIGHT/2)
        else
            cnt=cnt+1
            fill(255)
            text(cnt,WIDTH/2,HEIGHT-25)
        end
    
        stroke(255)
        strokeWidth(2)
        line(wid,0,wid,HEIGHT)
        line(WIDTH-wid,0,WIDTH-wid,HEIGHT)
        line(0,wid,WIDTH,wid)
        line(0,HEIGHT-wid,WIDTH,HEIGHT-wid)
        text("FPS  "..1//DeltaTime,WIDTH/2,HEIGHT-50)
        text("Nbr  "..#tab,WIDTH/2,HEIGHT-75)
    
    end
    
    function touched(touch)
        showText = nil
        table.insert(tab, Object(size,size,touch.x,touch.y) )
    end
    
    --# Object
    Object = class()
    -- these values completely defines the objects
    local A1, B1, R, S = 180, 17, 30, 3
    local A1, B1, R, S = 180, 17, 30, 2
    local A1, B1, R, S = 180, 17, 35, 4
    -- the number of objects / size of the initial ground are important too
    nbrObjects = 700
    size = 200
    
    local grid = false
    
    function Object:init(w,h,x,y)
        if not grid then -- init the grid just once
            grid = Grid(R)
        end
        -- random position and angle within specified bounds
        if x == nil then
            self.x=math.random((WIDTH-w)/2,(WIDTH+w)/2)
            self.y=math.random((HEIGHT-h)/2,(HEIGHT+h)/2)
        else
            self.x = x
            self.y = y
        end
    
        self.i = 1
        self.j = 1
        self.ang=math.random(360)
        -- init speed
        self.speed = S
        self.dx = math.cos(math.rad(self.ang))*self.speed
        self.dy = math.sin(math.rad(self.ang))*self.speed
        -- init sensing radius
        self.dotRadius = 6
        self.colorRadius = 10
        self.sensingRadius = R
    
        self.col=color(0,255,0)
    end
    
    local mod = math.fmod
    local min = math.min
    
    function Object:update()
        -- move according current direction and speed
        local x,y
    
        if self.x+self.dx>WIDTH-wid or self.x+self.dx<1+wid then
            x=self.x-self.dx
        else
            x = self.x + self.dx
        end
        if self.y+self.dy>HEIGHT-wid or self.y+self.dy<1+wid then
            y=self.y-self.dy
        else
            y = self.y + self.dy
        end
        -- make the world a torus
        --x = mod(x +WIDTH , WIDTH )
        --y = mod(y +HEIGHT, HEIGHT)
        -- now update positions
        self.x = x
        self.y = y
        grid:update(self)
        -- manage neighbour impact
        local A = vec2(self.x,self.y)
        local nbClose = 0
        local side = 0
        local d,n,lft,rgt = 0,0,0,0
        local B = vec2(0,0)
        for b,b in pairs( grid:neighbours(self) ) do
            if b~=self then
                B.x, B.y = b.x, b.y
                d = A:dist(B)
                if d<self.sensingRadius then
                    if d < self.colorRadius then nbClose = nbClose + 1 end
                    side = -self.dy*(b.x-self.x) + self.dx*(b.y-self.y)
                    if side > 0 then
                        lft = lft + 1
                    else
                        rgt = rgt + 1
                    end
                end
            end
        end
        -- turn according to neighbours
        n = lft + rgt
        local sgn = 0
        if lft < rgt then sgn = -1 else sgn = 1 end
        self.ang=(self.ang+(A1+B1*n*sgn))%360
        self.dx = math.cos(math.rad(self.ang))*self.speed
        self.dy = math.sin(math.rad(self.ang))*self.speed
        -- define color according to nb of close neighbours
        local col
        if     nbClose < 1   then col = color(0,255,0)
        elseif nbClose < 8 then col = color(0,92,255)
        elseif nbClose < 12 then col = color(255,255,0)
        elseif nbClose < 16 then col = color(255,0,255)
        else col=color(255,0,0)
        end
        self.col=col
    end
    
    function Object:draw()
        fill(self.col)
        ellipse(self.x, self.y, self.dotRadius)
    end
    
    --# Grid
    Grid = class()
    -- this is just a tool to find neighbours faster (improves fps)
    
    local rad, halfRad
    local w,h,imax,jmax
    local grid = {}
    local floor = math.floor
    
    function Grid:init(R)
        rad = R
        halfRad = R/2
        w,h = WIDTH, HEIGHT
        imax,jmax = self:get_ij(w,h)
    
        for i=1,imax do
            local col = {}
            grid[i] = col
            for j=1,jmax do
                col[j] = {}
            end
        end
    end
    
    function Grid:update(a)
        local i0,j0 = a.i,a.j
        local i,j = Grid:get_ij(a.x,a.y)
        if (i~=i0) or (j~=j0) then
            for di =-1,1 do
                for dj =-1,1 do
                    self:reset(i0+di,j0+dj,a)
                end
            end
            for di =-1,1 do
                for dj =-1,1 do
                    self:set(i+di,j+dj,a)
                end
            end
            a.i,a.j = i,j
        end
    end
    
    function Grid:neighbours(a)
        local i,j = a.i, a.j
        i = (i-1)%imax + 1
        j = (j-1)%jmax + 1
        return grid[i][j]
    end
    
    function Grid:set(i,j,a)
        i = (i-1)%imax + 1
        j = (j-1)%jmax + 1
        grid[i][j][a] = a
    end
    
    function Grid:reset(i,j,a)
        i = (i-1)%imax + 1
        j = (j-1)%jmax + 1
        grid[i][j][a] = nil
    end
    
    function Grid:get_ij(x,y)
        local i,j = floor( (x + halfRad)/rad )+1, floor( (y + halfRad)/rad )+1
        return i,j
    end
    
  • Posts: 921
    Actually I quite like that if you move fast it leaves some of the dots outside. It makes for a wider variety of scenarios, like adding stuff back in after taking it out, etc.
  • Posts: 921
    I happened onto a cool result, I think.

    I set it to spawn 120 nutrients per tap.

    It turns out that 120 nutrients spawned in a single location becomes a small, infinitely stable red dot that oscillates between a doughnut shape and a solid shape.
  • Posts: 921
    As I recall, the folks here are good at doing pretty amazing things with shaders.

    I barely understand how shaders work; is this something a shader could do on its own?
  • dave1707dave1707 Mod
    Posts: 9,091

    @UberGoober It should be doable. I don’t know if you’re familiar with the Game of Life demo, but I was able to do that with shaders. I’m not that familiar with the code that does this demo or the calculations, so I’m not 100% sure if it can be done or not.

  • Posts: 921
    I did see that demo, and the huge disagreement I got in with @LoopSpace notwithstanding, it was exactly what this brought to mind.
  • edited April 3 Posts: 115

    @UberGoober hey, I’m not sure if coroutines would help here. I use it in my project to render the ‘booting’ and ‘loading’ screens. Coroutines allow me to use Codea to process data while also drawing an animated screen at 60fps. I’m not sure if they can be used to improve the number of drawn entities because the draw loop is one thread in itself. Maybe if there was a way to split the Main.draw function into coroutines that would work but I don’t know enough about it yet.

  • Posts: 2,104
    @skar - curiosity, how do you set up booting/loading screens. I tend to use a timer to display intro screens. Do you mean the gadgets for showing loading progress?
  • Posts: 115

    @Bri_G i think you already have it but check out my physics project demo. First I set up a load listener in the draw function. When the listener is triggered it sets up a loading screen which is just a rect and text. Then it also creates a coroutine Thread and this thread does the calculations while the loading screen plays. The thread is also inside the draw function. The booting screen is almost the same except it gets triggered in the main setup function.

  • Posts: 2,104
    @skar - thanks for the reply and description. Yes I did load your phsics demo, very impressive. Could it be extended to fluid movement/flow?

    I'll go back over your code and your prompts above. Thanks again.
Sign In or Register to comment.