Skip to content

Instantly share code, notes, and snippets.

@treytomes
Last active February 5, 2024 16:57
Show Gist options
  • Save treytomes/3f15d6e93b2448d05a1c1e5fc0125225 to your computer and use it in GitHub Desktop.
Save treytomes/3f15d6e93b2448d05a1c1e5fc0125225 to your computer and use it in GitHub Desktop.
// Metaballs / Marching Squares Demo
// https://jamie-wong.com/2014/08/19/metaballs-and-marching-squares/
// https://jurasic.dev/marching_squares/
import "tc"
GRID_RESOLUTION = 24
PRINT_SAMPLES = false
SHOW_GRID = false
DRAW_CIRCLES = true
FILL_RECTS = false
SHOW_CORNERS = false
NUM_BLOBS = 10
MIN_RADIUS = 40
MAX_RADIUS = 80
INTERPOLATED = true
Blob = {}
Blob.init = function(x, y, r)
self.x = x
self.y = y
self.radius = r
self.color = color.red
self.penSize = 1
self.vx = (rnd * 3 - 2) * rnd * 8 + 8
self.vy = (rnd * 3 - 2) * rnd * 8 + 8
return self
end function
Blob.make = function(x, y, r)
return (new Blob).init(x, y, r)
end function
Blob.draw = function(dt)
self.x += self.vx * dt
self.y += self.vy * dt
if self.left < 0 or self.right >= gfx.width then self.vx = -self.vx
if self.bottom < 0 or self.top >= gfx.height then self.vy = -self.vy
if DRAW_CIRCLES then gfx.drawEllipse self.left, self.bottom, self.width, self.height, self.color, self.penSize
end function
Blob.left = function
return self.x - self.radius
end function
Blob.right = function
return self.left + self.width
end function
Blob.top = function
return self.bottom + self.height
end function
Blob.bottom = function
return self.y - self.radius
end function
Blob.width = function
return self.radius * 2
end function
Blob.height = function
return self.radius * 2
end function
Blob.makeRandom = function
r = floor(rnd * (MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS)
x = floor(rnd * (gfx.width - r * 2) + r)
y = floor(rnd * (gfx.height - r * 2) + r)
return Blob.make(x, y, r)
end function
resetSamples = function
globals.samples = []
for y in range(floor(gfx.height / GRID_RESOLUTION))
row = []
for x in range(floor(gfx.width / GRID_RESOLUTION))
row.push null
end for
samples.push row
end for
end function
calculateSample = function(sx, sy)
if samples[sy][sx] != null then return samples[sy][sx]
x = sx * GRID_RESOLUTION
y = sy * GRID_RESOLUTION
sample = 0
for b in blobs
sample += b.radius^2 / ((x - b.x)^2 + (y - b.y)^2)
end for
samples[sy][sx] = sample
return sample
end function
line = function(from, to)
gfx.line from[0], from[1], to[0], to[1], color.green, 3
end function
lerp = function(x, x0, x1, y0 = 0, y1 = 1)
if x0 == x1 then return null
return y0 + ((y1 - y0) * (x - x0)) / (x1 - x0)
end function
blobs = []
for n in range(NUM_BLOBS)
blobs.push Blob.makeRandom
end for
lastFrameTime = 0
while true
deltaTime = time - lastFrameTime
lastFrameTime = time
resetSamples
flc
// Draw the grid.
if SHOW_GRID then
for x in range(0, gfx.width, GRID_RESOLUTION)
gfx.line x, 0, x, gfx.height, color.gray, 1
end for
for y in range(0, gfx.height, GRID_RESOLUTION)
gfx.line 0, y, gfx.width, y, color.gray, 1
end for
end if
// Render the line cases.
c = color.green
for sy in range(samples.len - 2)
for sx in range(samples[sy].len - 2)
if PRINT_SAMPLES then
x = sx * GRID_RESOLUTION
y = sy * GRID_RESOLUTION
sample = calculateSample(x, y)
sample = floor(sample * 100) / 100
gfx.print sample, x - 16, y - 16, color.silver, "small"
end if
if INTERPOLATED then
bl = calculateSample(sx, sy)
br = calculateSample(sx + 1, sy)
tl = calculateSample(sx, sy + 1)
tr = calculateSample(sx + 1, sy + 1)
a = [
sx * GRID_RESOLUTION + GRID_RESOLUTION * lerp(1, tl, tr),
sy * GRID_RESOLUTION + GRID_RESOLUTION,
]
b = [
sx * GRID_RESOLUTION + GRID_RESOLUTION,
sy * GRID_RESOLUTION + GRID_RESOLUTION * lerp(1, br, tr),
]
c = [
sx * GRID_RESOLUTION + GRID_RESOLUTION * lerp(1, bl, br),
sy * GRID_RESOLUTION,
]
d = [
sx * GRID_RESOLUTION,
sy * GRID_RESOLUTION + GRID_RESOLUTION * lerp(1, bl, tl),
]
else
a = [
sx * GRID_RESOLUTION + GRID_RESOLUTION / 2,
sy * GRID_RESOLUTION + GRID_RESOLUTION,
]
b = [
sx * GRID_RESOLUTION + GRID_RESOLUTION,
sy * GRID_RESOLUTION + GRID_RESOLUTION / 2,
]
c = [
sx * GRID_RESOLUTION + GRID_RESOLUTION / 2,
sy * GRID_RESOLUTION,
]
d = [
sx * GRID_RESOLUTION,
sy * GRID_RESOLUTION + GRID_RESOLUTION / 2,
]
end if
bl = bl >= 1
br = br >= 1
tl = tl >= 1
tr = tr >= 1
case = bl + br * 2 + tr * 4 + tl * 8
if case == 0 or case == 15 then
continue
else if case == 1 or case == 14 then
line d, c
else if case == 2 or case == 13 then
line b, c
else if case == 3 or case == 12 then
line d, b
else if case == 4 or case == 11 then
line a, b
else if case == 5 then
line d, a
line c, b
else if case == 6 or case == 9 then
line c, a
else if case == 7 or case == 8 then
line d, a
else if case == 10 then
line a, b
line c, d
end if
end for
end for
// Render the blobs.
for b in blobs
b.draw deltaTime
end for
end while
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment