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,295

who wants to try this first?

Dave?

Comments

  • dave1707dave1707 Mod
    edited January 24 Posts: 7,515

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

  • Jmv38Jmv38 Mod
    edited January 24 Posts: 3,295

    @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: 7,515

    @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,295

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

  • Jmv38Jmv38 Mod
    Posts: 3,295

    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 26 Posts: 7,515

    @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: 4,882

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

  • dave1707dave1707 Mod
    Posts: 7,515

    @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,295

    @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: 7,515

    @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,295

    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,295

    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: 7,515

    @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!

Sign In or Register to comment.