Skip to content

Instantly share code, notes, and snippets.

@oatmealine
Created Nov 1, 2021
Embed
What would you like to do?
A quick and dirty polygon shattering simulation
local polygon = {}
local padding = 15
local sw, sh = love.graphics.getDimensions()
-- rectangle polygon
table.insert(polygon, {
{padding, padding},
{sw - padding, padding},
{sw - padding, sh - padding},
{padding, sh - padding}
})
--[[ -- circle polygon
local v = {}
for a = 0, 1, 0.01 do
local x, y = math.cos(a * math.pi * 2), math.sin(a * math.pi * 2)
table.insert(v, {sw / 2 + x * (math.min(sw, sh) / 2 - padding * 2), sh / 2 + y * (math.min(sw, sh) / 2 - padding * 2)})
end
table.insert(polygon, v)
]]
local timer = 0
local function ccw(a, b, c)
return (c[2] - a[2]) * (b[1] - a[1]) > (b[2] - a[2]) * (c[1] - a[1])
end
local function areIntersecting(a, b, c, d)
return ccw(a, c, d) ~= ccw(b, c, d) and ccw(a, b, c) ~= ccw(a, b, d)
end
local function line(p1, p2)
A = (p1[2] - p2[2])
B = (p2[1] - p1[1])
C = (p1[1] * p2[2] - p2[1] * p1[2])
return {A, B, -C}
end
local function intersection(L1, L2)
D = L1[1] * L2[2] - L1[2] * L2[1]
Dx = L1[3] * L2[2] - L1[2] * L2[3]
Dy = L1[1] * L2[3] - L1[3] * L2[1]
if D ~= 0 then
x = Dx / D
y = Dy / D
return {x, y}
else
return false
end
end
local function findArea(p)
local area = 0
for i = 1, #p - 2, 2 do
area = area + p[i+1][1] * (p[i+2][2]-p[i][2]) + p[i+1][2] * (p[i][1]-p[i+2][1]);
end
return area / 2
end
local function split()
for i = #polygon, 1, -1 do
local p = polygon[i]
--print(findArea(p))
--print(sw * sh)
if findArea(p) < (sw * sh) / 30 then goto continue end
local poly1, poly2 = {}, {}
repeat
local ra, rx, ry = math.random() * math.pi * 2, math.random(padding, sw - padding), math.random(padding, sh - padding)
local ax, ay = math.cos(ra) * (sw + sh), math.sin(ra) * (sw + sh)
local rx1, ry1 = rx + ax, ry + ay
local rx2, ry2 = rx - ax, ry - ay
local currentPoly = 1
poly1, poly2 = {}, {}
for pi, p1 in ipairs(p) do
local p2 = p[pi % #p + 1]
local intersect = areIntersecting(p1, p2, {rx1, ry1}, {rx2, ry2}) and intersection(line(p1, p2), line({rx1, ry1}, {rx2, ry2}))
table.insert(currentPoly == 1 and poly1 or poly2, p1)
if intersect then
table.insert(poly1, intersect)
table.insert(poly2, intersect)
currentPoly = currentPoly % 2 + 1
end
end
local area1, area2 = findArea(poly1), findArea(poly2)
local dist = math.sqrt(math.sqrt((p[1][1] - sw/2) * (p[1][1] - sw/2) + (p[1][2] - sh/2) * (p[1][2] - sh/2)) / (sw + sh)) -- using the first point as a lazy hack
until #poly1 > 0 and #poly2 > 0 and (area1 / area2 > 0.4 and area2 / area1 > 0.4) or (math.random() > dist)
if #poly1 > 0 then table.insert(polygon, poly1) end
if #poly2 > 0 then table.insert(polygon, poly2) end
table.remove(polygon, i)
::continue::
end
end
function love.update(dt)
timer = timer + dt
if timer > 1 then
timer = timer - 1
split()
end
end
local function flat1(x) -- flatten with a depth of 1
local t = {}
for _, v in ipairs(x) do
for _, o in ipairs(v) do
table.insert(t, o)
end
end
return t
end
function love.draw()
for _, p in ipairs(polygon) do
local scale = 0.9
local sumX, sumY = 0, 0
for _, v in ipairs(p) do
sumX = sumX + v[1]
sumY = sumY + v[2]
end
local avgX, avgY = sumX / #p, sumY / #p
local scaledP = {}
for _, v in ipairs(p) do
table.insert(scaledP, {(v[1] - avgX) * scale + avgX, (v[2] - avgY) * scale + avgY})
end
love.graphics.setColor(0, 0, 1)
love.graphics.polygon('line', flat1(scaledP))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment