Parabola functions in Lua

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
darkfrei
Party member
Posts: 1181
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

The parabola is given by focus and directrix.
Finding the roots of parabola for given high y:

Code: Select all

function getFocusParabolaRoots (fx, fy, y) -- focus, horizontal line
	-- (directrix dirY is global)
	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
The parabola is given by focus and directrix.
Parabola curve is limited by points A (left) and B (right).
Finding control point C for quadratic Bezier curve for special case, where ay == by:
(based on the code of Mike 'Pomax' Kamermans)

Code: Select all

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)
	if (ay == by) then -- special case: horizontal AB
		return fx, ay + 2*((fy + dirY) / 2-ay)
	else
		local ad = df(ax) -- tangent slope for A
		local dx = (bx-ax)/2
		return ax+dx, ay+ad*dx
	end
end
or just

Code: Select all

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
Animation (83).gif
Animation (83).gif (436.42 KiB) Viewed 750 times

Code: Select all

-- evaluate parabola for given focus and directrix, returns y-position for given x-position:
local function evaluateParabola(fx, fy, x, dirY)
    local numerator = fx*fx + fy*fy - dirY*dirY - x*(2*fx - x)
    local denominator = 2*(fy - dirY)
    return numerator / denominator
end
Attachments
voronoi-beachlines-01.love
(1.54 KiB) Downloaded 8 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1181
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

Two parabolas with common directrix have crossings on the same line:

The the function to get crossing for the given horizontal line is:

Code: Select all

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
main.lua:

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
Animation (92).gif
Animation (92).gif (295.39 KiB) Viewed 604 times
Attachments
two-parabolas-crossing-01.love
(1.43 KiB) Downloaded 5 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
Post Reply

Who is online

Users browsing this forum: No registered users and 63 guests