Some squishy balls


Last night I encountered the work of Karsten Schmidt, and was inspired to reimplement a tiny part of the merest superficial sample of his huge project. It doesn't actually implement any flocking dynamics yet, and I'm also not implementing Karsten's tasteful color gradients. To my surprise, it's quite snappy even on a large screen to quadratically scan all balls for each ball's neighbors.

function normalize(p)
  local m = math.sqrt(p.x^2 + p.y^2)
  return {x=p.x/m, y=p.y/m}
end
-- create a vector with a random orientation
function randoriented()
  local n = 100
  return {x=rand(-n,n)/n, y=rand(-n,n)/n}
end
function randNorm2() return normalize(randoriented()) end
PAD = 10
function randpoint()
  return {x=rand(PAD, Safe_width-PAD), y=rand(PAD, Safe_width-PAD)}
end
function norm(x, a, b)
  if a == b then return 0 end
  return (x-a)/(b-a)
end
function clamp01(x) return max(0, min(1, x)) end
function fitClamped(x, a, b, c, d)
  return c + (d-c)*clamp01(norm(x, a, b))
end
NUM = fitClamped(Safe_width*Safe_height, 640*480, 1440*810, 500, 1000)
boids = {}
for i=1,NUM do
  table.insert(boids, {pos=randpoint(), vel=randNorm2(), color={rand(), rand(), rand()}})
end
MAX_RADIUS = 50
function neighbors(boid, d)
  local result = {}
  for i,b in ipairs(boids) do
    if dist2(boid.pos, b.pos) < d^2 then
      table.insert(result, i)
    end
  end
  return result
end
function car.update(dt)
  for i,b in ipairs(boids) do
    b.pos.x = b.pos.x + dt*b.vel.x*10
    b.pos.y = b.pos.y + dt*b.vel.y*10
  end
  for s,b in ipairs(boids) do
    b.d = MAX_RADIUS
    local neighbors = neighbors(b, MAX_RADIUS)
    local minD, closest
    for _,n in ipairs(neighbors, b.radius) do
      if n ~= s then
        local d = dist2(b.pos, boids[n].pos)
        if minD == nil or d < minD then
          closest = n
          minD = d
        end
      end
    end
    if minD then b.d = math.sqrt(minD) end
  end
end
function car.draw()
  for _,b in ipairs(boids) do
    color(unpack(b.color))
    circle('fill', b.pos.x, b.pos.y, b.d/2)
  end
end
function dist2(a, b) return (a.x-b.x)^2 + (a.y-b.y)^2 end

If you try pasting any of these programs into Lua Carousel, remember to first run the abbreviations on one of the example screens. Or if you've deleted that screen, here are the abbreviations I used in this post:

g = love.graphics
circle = g.circle
color = g.setColor
min, max = math.min, math.max
rand = math.random

Get Lua Carousel

Leave a comment

Log in with itch.io to leave a comment.