Skip to content

Instantly share code, notes, and snippets.

@tnlogy
Created February 6, 2021 22:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tnlogy/3700c8dc44409222153ad17309b628e2 to your computer and use it in GitHub Desktop.
Save tnlogy/3700c8dc44409222153ad17309b628e2 to your computer and use it in GitHub Desktop.
Lua code for Codea to create a physics polygon for a sprite
--# Main
function setup()
viewer.mode = FULLSCREEN
walls = {
physics.body(EDGE, vec2(0, 0), vec2(WIDTH, 0)),
physics.body(EDGE, vec2(0, 0), vec2(0, HEIGHT)),
physics.body(EDGE, vec2(WIDTH, HEIGHT), vec2(WIDTH, 0))
}
for k, v in ipairs(walls) do
v.restitution = .5
end
bodies = {}
add()
end
function add()
local assets = asset.builtin.Tyrian_Remastered.all
local r = math.random(1, #assets)
print(r)
img = readImage(assets[r])
pts = simplify(outline(img), 1)
print("simplified to ", #pts)
for i = 1,#pts do
pts[i] = pts[i] - vec2(img.width/2,img.height/2)
end
obj=physics.body(POLYGON, unpack(pts))
obj.x = math.random(100,WIDTH-100)
obj.y = HEIGHT
obj.angle = 180*math.random()
obj.restitution = .25
obj.info = img
table.insert(bodies, obj)
end
function draw()
background()
strokeWidth(1)
translate(WIDTH/2,HEIGHT/2)
local pp = nil
for i,v in ipairs(pts) do
if pp then
line(pp.x,pp.y,v.x,v.y)
end
pp = v
end
for i,obj in ipairs(bodies) do
resetMatrix()
translate(obj.x,obj.y)
rotate(obj.angle)
spriteMode(CENTER)
sprite(obj.info,0,0)
end
end
function touched(touch)
if touch.state == BEGAN then
add()
end
end
--# RamerDouglasPeucker
-- Implementation of the Ramer–Douglas–Peucker algorithm
-- @readme https://github.com/evaera/RobloxLuaAlgorithms#ramerdouglaspeuckerlua
-- @author evaera
-- and now modified for Codea
function getSqSegDist(p, p1, p2)
local x = p1.x
local y = p1.y
local dx = p2.x - x
local dy = p2.y - y
if dx ~= 0 or dy ~= 0 then
local t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy)
if t > 1 then
x = p2.x
y = p2.y
elseif t > 0 then
x = x + dx * t
y = y + dy * t
end
end
dx = p.x - x
dy = p.y - y
return dx * dx + dy * dy
end
function simplifyRadialDist(points, sqTolerance)
local prevPoint = points[1]
local newPoints = {prevPoint}
local point
for i=2, #points do
point = points[i]
if point:distSqr(prevPoint) > sqTolerance then
table.insert(newPoints, point)
prevPoint = point
end
end
if prevPoint ~= point then
table.insert(newPoints, point)
end
return newPoints
end
function simplifyDPStep(points, first, last, sqTolerance, simplified)
local maxSqDist = sqTolerance
local index
for i=first+1, last do
local sqDist = getSqSegDist(points[i], points[first], points[last])
if sqDist > maxSqDist then
index = i
maxSqDist = sqDist
end
end
if maxSqDist > sqTolerance then
if index - first > 1 then
simplifyDPStep(points, first, index, sqTolerance, simplified)
end
table.insert(simplified, points[index])
if last - index > 1 then
simplifyDPStep(points, index, last, sqTolerance, simplified)
end
end
end
function simplifyDouglasPeucker(points, sqTolerance)
local last = #points
local simplified={points[1]}
simplifyDPStep(points, 1, last, sqTolerance, simplified)
table.insert(simplified, points[last])
return simplified
end
function simplify(points, tolerance, highestQuality)
if #points <= 2 then
return points
end
local sqTolerance = tolerance ~= nil and tolerance^2 or 1
points = highestQuality and points or simplifyRadialDist(points, sqTolerance)
points = simplifyDouglasPeucker(points, sqTolerance)
return points
end
--# Outline
-- Find the outline of a sprite
-- by walking around it, like finding
-- your way in a labyrinth by following
-- the wall on the right side
function outline(img)
local w,h = img.width, img.height
local dir, right = vec2(1,0), vec2(0,1)
function turn(d)
right = d == right and -dir or dir
dir = d
end
function empty(img,p)
if 0 < p.x and p.x < w and p.y > 0 and p.y < h then
r,g,b,a = img:get(p.x,p.y)
return a == 0
end
return true
end
local sp = false -- start point
local pts = {}
local pos = vec2(-1,math.ceil(h/2))
repeat
local p = pos + dir
local pr = pos + right
local a = empty(img, p)
local ar = empty(img, pr)
if not sp then
-- find start pos
if a then
pos = p
elseif pos.x > w then
-- missed target
return {}
else -- hit wall
sp = pos
table.insert(pts,pos)
turn(-right)
end
elseif ar then
turn(right)
pos = pos + dir
table.insert(pts,pos)
elseif a then
pos = p
table.insert(pts,p)
else -- wall ahead
turn(-right)
end
until pos == sp and #pts > 1
print(#pts, " points")
return pts
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment