#### 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...

Mod
Posts: 3,297

who wants to try this first?

Dave?

• Mod
edited January 2019 Posts: 9,989

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

• 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.

• Mod
Posts: 9,989

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

• Mod
Posts: 3,297

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

• 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.

• Mod
edited January 2019 Posts: 9,989

@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
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)
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))
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)
tab[z].col=color(0,0,255)
tab[y].col=color(0,0,255)
tab[z].col=color(255,255,0)
tab[y].col=color(255,255,0)
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
``````
Posts: 5,778

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

• Mod
Posts: 9,989

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

• 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 w,h,imax,jmax
local grid = {}
local floor = math.floor

function Grid:init(R)
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)
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.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.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
-- 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)
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

``````
• Mod
Posts: 9,989

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

• 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’.

• 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.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.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
-- 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)
end

--# Grid
Grid = class()
-- this is just a tool to find neighbours faster (improves fps)

local w,h,imax,jmax
local grid = {}
local floor = math.floor

function Grid:init(R)
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)
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
``````
• Mod
Posts: 9,989

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

• Posts: 13

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

• Posts: 1,799

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

• Mod
Posts: 9,989

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

• Posts: 1,799
Aw rats, it seemed like a neat mod. The original is still darn cool though.
• Mod
Posts: 9,989

@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.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.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
-- 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)
end

--# Grid
Grid = class()
-- this is just a tool to find neighbours faster (improves fps)

local w,h,imax,jmax
local grid = {}
local floor = math.floor

function Grid:init(R)
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)
return i,j
end
``````
• Posts: 1,799
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: 1,799
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: 1,799
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?
• Mod
Posts: 9,989

@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: 1,799
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 2021 Posts: 315

@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,690
@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: 315

@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,690
@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.