Code: Select all
function newParabola (x, y)
local parabola = {
x=x,
y=y,
minX = 0,
maxX = love.graphics.getWidth(),
minY = 0,
}
return parabola
end
local function getParabolaCrossPoint (p1x, p1y, p2x, p2y, dirY) -- focus 1, focus 2, directrix
-- Function to find the intersection point of two parabolas
local f1 = math.abs(dirY-p1y)/2
local f2 = math.abs(dirY-p2y)/2
local a1 = -1/(4*f1)
local a2 = -1/(4*f2)
local b1 = -2*p1x*a1
local b2 = -2*p2x*a2
local c1 = p1x*p1x*a1 + p1y + f1
local c2 = p2x*p2x*a2 + p2y + f2
local a = a1-a2
local b = b1-b2
local c = c1-c2
local d = b*b-4*a*c
local x, y
if d >=0 then
x = (-b-math.sqrt (d))/(2*a)
y = a1*x*x + b1*x + c1
end
return x, y
end
function getFocusParabolaRoots (fx, fy, y, dirY) -- focus, horizontal line
local h = fx -- x shift
local p = -(dirY-fy)/2 -- always negative for voronoi
local k = (fy - p) - y -- (fy-p) is vertex high
local leftX = h - math.sqrt (-k*4*p)
local rightX = h + math.sqrt (-k*4*p)
return leftX, rightX
end
function focusFocusLineYCrossing (p1x, p1y, p2x, p2y, y)
local cx = (p1x+p2x)/2
local cy = (p1y+p2y)/2
local m = (p2y-p1y)/(p2x-p1x) -- slope
local x = cx - m*(y-p1y) -- perpendicular to slope
return x
end
function updateParabolas (p1, p2, dirY)
local p1x, p1y = p1.x, p1.y
local p2x, p2y = p2.x, p2.y
local x, y = getParabolaCrossPoint (p1x, p1y, p2x, p2y, dirY)
local x2 = focusFocusLineYCrossing (p1x, p1y, p2x, p2y, y)
-- print (x, x2, y)
local pMinX1, pMaxX1 = getFocusParabolaRoots (p1x, p1y, frameMinY, dirY)
local pMinX2, pMaxX2 = getFocusParabolaRoots (p2x, p2y, frameMinY, dirY)
p1.minX = pMinX1
p1.maxX = math.min (x, pMaxX1)
p2.minX = math.max (x, pMinX2)
p2.maxX = pMaxX2
table.insert (points, x)
table.insert (points, y)
-- table.insert (points2, x2)
-- table.insert (points2, y)
end
function love.load()
frameMinY = 100
parabola1 = newParabola (300, 250)
parabola2 = newParabola (500, 300)
points = {}
-- points2 = {}
step = 0.25
dirY = 302
updateParabolas (parabola1, parabola2, dirY)
love.window.setTitle ('press SPACE to start')
pause = false
end
function love.update(dt)
if pause then return end
dirY = dirY + step^2
step = step+0.02
updateParabolas (parabola1, parabola2, dirY)
if dirY > 800 then
step = 0.25
dirY = 302
points = {}
end
end
function getBezierControlPoint (fx, fy, ax, bx, dirY)
local f = function (x) return (x*x-2*fx*x+fx*fx+fy*fy-dirY*dirY) / (2*(fy-dirY)) end
local function df(x) return (x-fx) / (fy-dirY) end
if (fy == dirY) then return end -- not parabola
local ay, by = f(ax), f(bx)
local ad, dx = df(ax), (bx-ax)/2
return ax+dx, ay+ad*dx
end
function drawParabola (p, dirY)
local fx = p.x
local fy = p.y
local ax, bx = p.minX, p.maxX
local cx, cy = getBezierControlPoint (fx, fy, ax, bx, dirY)
local ay = (ax*ax-2*fx*ax+fx*fx+fy*fy-dirY*dirY) / (2*(fy-dirY))
local by = (bx*bx-2*fx*bx+fx*fx+fy*fy-dirY*dirY) / (2*(fy-dirY))
local line = love.math.newBezierCurve (ax, ay, cx, cy, bx, by):render()
love.graphics.line (line)
love.graphics.circle ('fill', fx, fy, 2)
end
function love.draw()
love.graphics.setColor (1,1,1)
drawParabola (parabola1, dirY)
drawParabola (parabola2, dirY)
love.graphics.points (points)
love.graphics.line (0, frameMinY, love.graphics.getWidth(), frameMinY)
love.graphics.line (0, dirY, love.graphics.getWidth(), dirY)
-- love.graphics.setColor (1,1,0)
-- love.graphics.points (points2)
end
function love.keypressed(key, scancode, isrepeat)
if false then
elseif key == "space" then
pause = not pause
elseif key == "escape" then
love.event.quit()
end
end