Parabola functions in Lua

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

Parabola functions in Lua

Post by darkfrei »

Hi all!

I found the way how to draw a pixelated parabola.

Code: Select all

function getParabolaPoints (a, xMax)
  local x, y = 0, 0
  local points = {x, y}
  while x < xMax do
    local dx, dy = 0, 0
    if ((y+1)/a)^0.5 - x - 0.5 > 0 then dx = 1 end
    if a*(x+1)^2 - y - 0.5 >= 0 then dy = 1 end
    x, y = x + dx, y + dy
    table.insert (points, x)
    table.insert (points, y)
  end
  return points
end
Here the code:

Code: Select all

local h = 1
local k = 2
local yDir = -1
p = k - yDir

a = 1/(4*p)
b = -2 * a * h
c = a * h * h + k

h = -b / (2 * a)
k = a * h * h + b * h + c
p = 1/(4 * a)
dirY = k - p
Parabola directrix

Code: Select all

y = (x - h)^2/(4*(k - yDir)) + k
Last edited by darkfrei on Thu Sep 28, 2023 2:43 pm, edited 8 times in total.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
dusoft
Party member
Posts: 654
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: Parabola functions in Lua

Post by dusoft »

darkfrei wrote: Mon Sep 25, 2023 2:43 pm Hi all!

I found the way how to draw a pixelated parabola.

Code: Select all

function getParabolaPoints (a, xMax)
  local x, y = 0, 0
  local points = {x, y}
  while x < xMax do
    local dx, dy = 0, 0
    if ((y+1)/a)^0.5 - x - 0.5 > 0 then dx = 1 end
    if a*(x+1)^2 - y - 0.5 >= 0 then dy = 1 end
    x = x + dx
    y = y + dy
    table.insert (points, x)
    table.insert (points, y)
  end
  return points
end
Isn't that just replicating what a bezier is for?
https://love2d.org/wiki/BezierCurve

Edit: Oh, I see you are just generating the points.
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

The function to create three parabolas, based on their focuses and their common directrix Y:

Code: Select all

-- common directrix of three parabolas
local w, h = love.graphics.getDimensions()

local function createParabola (fx, fy, yDir)
-- local fy=k-(yDir-k)
	local h = fx
	local k = (yDir+fy)/2
	local parabola = {x=h, k=k, fy=fy}
	local line = {}
	for x = 0, w, 20 do
		local y = (x-h)^2/(4*(k-yDir)) + k
		table.insert (line, x)
		table.insert (line, y)
	end
	parabola.line = line
	print ('focal length', yDir-k)
	return parabola
end

local function drawParabola (p)
	love.graphics.circle ('line', p.x, p.k, 2) -- vertex
	love.graphics.circle ('line', p.x, p.fy, 4) -- focus
	love.graphics.line (p.line) -- parabola curve
end

local function distance(x1, y1, x2, y2)
	return math.sqrt((x1-x2)^2 + (y1-y2)^2)
end

local function findCircumcenterAndRadius(x1, y1, x2, y2, x3, y3)
	local a = distance(x2, y2, x3, y3)
	local b = distance(x1, y1, x3, y3)
	local c = distance(x1, y1, x2, y2)
	local s = (a + b + c) / 2
	local circumradius = (a * b * c) / (4 * math.sqrt(s * (s - a) * (s - b) * (s - c)))
	local A = x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2
	local B = (x1*x1 + y1*y1)*(y3-y2) + (x2*x2 + y2*y2)*(y1-y3) + (x3*x3 + y3*y3)*(y2-y1)
	local C = (x1*x1 + y1*y1)*(x2-x3) + (x2*x2 + y2*y2)*(x3-x1) + (x3*x3 + y3*y3)*(x1-x2)
	local D = 2*((x1-x2)*(y3-y2) - (y1-y2)*(x3-x2))

	local circumcenterX = B / D
	local circumcenterY = C / D
	print (circumcenterX, circumcenterY, circumradius)
	return circumcenterX, circumcenterY, circumradius
end

local points = {150, 375, 250, 225, 430, 360}

local cx, cy, cr = findCircumcenterAndRadius(points[1], points[2], points[3], points[4], points[5], points[6])
local yDir = cy+cr
print ('directrix Y', yDir)
local p1 = createParabola (points[1], points[2], yDir)
local p2 = createParabola (points[3], points[4], yDir)
local p3 = createParabola (points[5], points[6], yDir)

function love.draw ()
	drawParabola (p1)
	drawParabola (p2)
	drawParabola (p3)
	love.graphics.line (0, yDir, w, yDir)
	love.graphics.circle ('line', cx, cy, cr)
end

Code: Select all

-- Problem of Apollonius
-- drawing a circle through three given points (PPP)
local function ppp (x1, y1, x2, y2, x3, y3)
	local dax, day = x2-x1, y2-y1
	local dbx, dby = x3-x2, y3-y2
	local dcx, dcy = x1-x3, y1-y3
	local a = distanceDelta (dax, day)
	local b = distanceDelta (dbx, dby)
	local c = distanceDelta (dcx, dcy)
	
	local s = (b + c + a) / 2
	local cr = (b * c * a) / (4 * math.sqrt(s * (s - b) * (s - c) * (s - a)))
	local s1 = x1*x1 + y1*y1
	local s2 = x2*x2 + y2*y2
	local s3 = x3*x3 + y3*y3
	local B = s1*dby + s2*dcy + s3*day
	local C = s1*dbx + s2*dcx + s3*dax
	local D = 2*(day*dbx - dax*dby)

	local cx = B / D
	local cy = -C / D
	return cx, cy, cr
end

print (ppp(0, 0, 0, 100, 300, 300)) -- 250, 50, ~255
print (ppp(0, 100, 250, 50, 300, 300)) -- 150, 200, ~180

Code: Select all

local function getCircumcircle (x1, y1, x2, y2, x3, y3)
	local d = 2 * (x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))
	local t1, t2, t3 = x1*x1+y1*y1, x2*x2+y2*y2, x3*x3+y3*y3
	local x = (t1 * (y2 - y3) + t2 * (y3 - y1) + t3 * (y1 - y2)) / d
	local y = (t1 * (x3 - x2) + t2 * (x1 - x3) + t3 * (x2 - x1)) / d
	local radius = math.sqrt((x1-x)^2 + (y1-y)^2)
	return x, y, radius
end
Screenshot 2023-09-28 220745.png
Screenshot 2023-09-28 220745.png (38.18 KiB) Viewed 104859 times
:awesome:

Update:
The point, that is on the horizontal line y=baseY an it is a circle center, that goes through two given points x1, y1, x2, y2:

Code: Select all

function findCircleCenter(x1, y1, x2, y2, baseY)
	local yIntercept = 0.5*((y2^2 - y1^2) + (x2^2 - x1^2) )
	local centerX = (yIntercept - baseY*(y2 - y1)) / (x2 - x1)
	local centerY = baseY
	local radius = math.sqrt((x1 - centerX)^2 + (y1 - centerY)^2)
	local eventY = centerY + radius
	return centerX, centerY, radius, eventY
end
Usage:

Code: Select all

function love.load()
	baseY = 0
	p1 = {x=200, y=200}
	p2 = {x=400, y=250}
	p3 = {}
	p3.x, p3.y, radius, eventY = findCircleCenter(p1.x, p1.y, p2.x, p2.y, baseY)
end

function love.draw()
	love.graphics.line (0, baseY, love.graphics.getWidth (), baseY)
	love.graphics.circle ('line', p1.x, p1.y, 3)
	love.graphics.circle ('line', p2.x, p2.y, 3)
	love.graphics.circle ('line', p3.x, p3.y, 3)
	love.graphics.circle ('line', p3.x, p3.y, radius)
	love.graphics.circle ('line', p3.x, eventY, 3)
end

function love.mousemoved (x, y)
	baseY = y
	p3.x, p3.y, radius, eventY = findCircleCenter(p1.x, p1.y, p2.x, p2.y, baseY)
end
2023-12-22T12_36_42-Untitled.png
2023-12-22T12_36_42-Untitled.png (7.13 KiB) Viewed 32991 times
Last edited by darkfrei on Mon Apr 15, 2024 9:25 am, edited 5 times in total.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
dusoft
Party member
Posts: 654
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: Parabola functions in Lua

Post by dusoft »

darkfrei wrote: Thu Sep 28, 2023 8:15 pm The function to create three parabolas, based on their focuses and their common directrix Y:

Code: Select all

-- common directrix of three parabolas
local w, h = love.graphics.getDimensions()

local function createParabola (fx, fy, yDir)
-- local fy=k-(yDir-k)
	local h = fx
	local k = (yDir+fy)/2
	local parabola = {x=h, k=k, fy=fy}
	local line = {}
	for x = 0, w, 20 do
		local y = (x-h)^2/(4*(k-yDir)) + k
		table.insert (line, x)
		table.insert (line, y)
	end
	parabola.line = line
	print ('focal length', yDir-k)
	return parabola
end

local function drawParabola (p)
	love.graphics.circle ('line', p.x, p.k, 2) -- vertex
	love.graphics.circle ('line', p.x, p.fy, 4) -- focus
	love.graphics.line (p.line) -- parabola curve
end

local function distance(x1, y1, x2, y2)
	return math.sqrt((x1-x2)^2 + (y1-y2)^2)
end

local function findCircumcenterAndRadius(x1, y1, x2, y2, x3, y3)
	local a = distance(x2, y2, x3, y3)
	local b = distance(x1, y1, x3, y3)
	local c = distance(x1, y1, x2, y2)
	local s = (a + b + c) / 2
	local circumradius = (a * b * c) / (4 * math.sqrt(s * (s - a) * (s - b) * (s - c)))
	local A = x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2
	local B = (x1*x1 + y1*y1)*(y3-y2) + (x2*x2 + y2*y2)*(y1-y3) + (x3*x3 + y3*y3)*(y2-y1)
	local C = (x1*x1 + y1*y1)*(x2-x3) + (x2*x2 + y2*y2)*(x3-x1) + (x3*x3 + y3*y3)*(x1-x2)
	local D = 2*((x1-x2)*(y3-y2) - (y1-y2)*(x3-x2))

	local circumcenterX = B / D
	local circumcenterY = C / D
	print (circumcenterX, circumcenterY, circumradius)
	return circumcenterX, circumcenterY, circumradius
end

local points = {150, 375, 250, 225, 430, 360}

local cx, cy, cr = findCircumcenterAndRadius(points[1], points[2], points[3], points[4], points[5], points[6])
local yDir = cy+cr
print ('directrix Y', yDir)
local p1 = createParabola (points[1], points[2], yDir)
local p2 = createParabola (points[3], points[4], yDir)
local p3 = createParabola (points[5], points[6], yDir)

function love.draw ()
	drawParabola (p1)
	drawParabola (p2)
	drawParabola (p3)
	love.graphics.line (0, yDir, w, yDir)
	love.graphics.circle ('line', cx, cy, cr)
end
What do you mostly use this for? I.e. What is your scenario for parabolas? Jumps? Genuinely interested.
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

dusoft wrote: Fri Sep 29, 2023 8:15 am What do you mostly use this for? I.e. What is your scenario for parabolas? Jumps? Genuinely interested.
Yes, you can predict the end position of the jump as

Code: Select all

-- start position:
x0 = 1
y0 = 5
-- jump speed at start:
vx0 = 2.5
vy0 = -10 -- negative as up direction

g = 12.5 -- vertical acceleration, also gravity 

a = 0.5 * g / vx0^2
b = vy0/vx0 - 2*a*x0
c = y0 - b*x0 - a*x0^2

-- and the parabola is:
x = x0 + time*vx
y = a*x^2 + b*x + c

-- or
x = x + vx0*dt
local vy2 = vy + g*dt/2 -- half change of velocity
y = y + vy2*dt
vy = vy + g*dt -- update velocity properly 

-- or
x = x0 + time*vx
y = y0 + vy0*time + 0.5*g*time^2 
Parameters for this example:
a = 1, b = -6, c = 0
Control points for this example:
time = 0, x = 1, y = 5
time = 0.8, x = 3, y = 1, vy = 0
time = 2, x = 6, y = 10, vy = 15
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

Just one intersection point between left parabola and right parabola:

Code: Select all

local dirY = 400
local fx1, fy1 = 250, 200
local fx2, fy2 = 425, 375

local p1 = {x=fx1, y=fy1, dirY=dirY}
local p2 = {x=fx2, y=fy2, dirY=dirY}

local function getParabolaLine (x, y, dirY, xMin, xMax) -- focus, directrix, 
	local line = {}
	local f = math.abs(dirY-y)/2
	local a = -1/(4*f) 
	local b = -2*x*a
	local c = x*x*a + y + f
	for x = xMin, xMax, 5 do
		local y = a*x*x + b*x + c
		table.insert (line, x)
		table.insert (line, y)
	end
	return line
end

p1.line = getParabolaLine (p1.x, p1.y, dirY, 0, 800)
p2.line = getParabolaLine (p2.x, p2.y, dirY, 0, 800)

local function getParabolaCrossPoint (fx1, fy1, fx2, fy2, dirY)
	local f1 = math.abs(dirY-fy1)/2
	local f2 = math.abs(dirY-fy2)/2

	local a1 = -1/(4*f1)
	local a2 = -1/(4*f2)
	local b1 = -2*fx1*a1
	local b2 = -2*fx2*a2
	local c1 = fx1*fx1*a1 + fy1 + f1
	local c2 = fx2*fx2*a2 + fy2 + 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

local px, py = getParabolaCrossPoint (p1.x, p1.y, p2.x, p2.y, dirY)

function love.draw ()
	love.graphics.line (p1.line)
	love.graphics.line (p2.line)
	love.graphics.circle ('line', px, py, 5)
end
Screenshot 2023-10-04 180257.png
Screenshot 2023-10-04 180257.png (21.54 KiB) Viewed 104239 times

:awesome:

Update:
fixed some issues, parabola have the same x-limits:

Code: Select all

local dirY = 400

local p1 = {x=200, y=350}
local p2 = {x=525, y=300}

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

local function getParabolaLine (x, y, dirY, xMin, xMax) -- focus, directrix, horizontal limits
-- Function to generate points along a parabola curve
	local line = {}
	local f = math.abs(dirY-y)/2
	local a = -1/(4*f) 
	local b = -2*x*a
	local c = x*x*a + y + f
	local nSteps = math.floor((xMax-xMin)/5)
	local step = (xMax-xMin)/nSteps
	for i = 0, nSteps do
		local x0 = xMin + i * step
		local y0 = a*x0*x0 + b*x0 + c
		table.insert (line, x0)
		table.insert (line, y0)
	end
	return line
end

function love.load ()
	local px, py = getParabolaCrossPoint (p1.x, p1.y, p2.x, p2.y, dirY)
	cross = {x=px, y=py}

	p1.line = getParabolaLine (p1.x, p1.y, dirY, 0, cross.x)
	p2.line = getParabolaLine (p2.x, p2.y, dirY, cross.x, 800)
end

function love.draw ()
	if #p1.line > 3 then
		love.graphics.line (p1.line)
	end
	if #p2.line > 3 then
		love.graphics.line (p2.line)
	end
	love.graphics.circle ('line', cross.x, cross.y, 5)
	love.graphics.line (0, dirY, 800, dirY)
end


function love.mousemoved(x, y)
	p2.x, p2.y = x, y

	local px, py = getParabolaCrossPoint (p1.x, p1.y, p2.x, p2.y, dirY)
	if px then
		cross.x, cross.y = px, py
		love.window.setTitle (px..' '..py)
		p1.line = getParabolaLine (p1.x, p1.y, dirY, 0, px)
		p2.line = getParabolaLine (p2.x, p2.y, dirY, px, 800)
	end
end
Screenshot 2023-10-08 184959.png
Screenshot 2023-10-08 184959.png (21.38 KiB) Viewed 103321 times
Attachments
parabola-intersection-01.love
(835 Bytes) Downloaded 473 times
Last edited by darkfrei on Thu Apr 25, 2024 8:24 am, edited 1 time in total.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

Big problem by the sweep line and no circle event:

Code: Select all

--[[
Copyright 2023 darkfrei
The MIT License
https://opensource.org/license/mit/
...
--]]
local vfa = {}

local function sortXYBackward (list)
	table.sort(list, function(a, b) return a.y > b.y or a.y == b.y and a.x > b.x or false end)
end

local function sortX (list)
	table.sort(list, function(a, b) return a.x < b.x end)
end

local function reload ()
	vfa.dirY = 0
	vfa.parabolaLines = {}
	vfa.sweepLine = {}
	vfa.queue = {}
	for i = 1, #vfa.points-1, 2 do
		local x, y = vfa.points[i], vfa.points[i+1]
		table.insert (vfa.queue, {x=x, y=y, point=true})
	end
	sortXYBackward (vfa.queue)
	for i = #vfa.queue, 1, -1 do
		local event = vfa.queue[i]
--		print (i, event.x, event.y)
	end
end

local function getCircumcircle (x1, y1, x2, y2, x3, y3)
	local d = 2*(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))
--	print (d)
	local t1, t2, t3 = x1*x1+y1*y1, x2*x2+y2*y2, x3*x3+y3*y3
	local x = (t1*(y2-y3)+t2*(y3-y1)+t3*(y1-y2)) / d
	local y = (t1*(x3-x2)+t2*(x1-x3)+t3*(x2-x1)) / d
	local radius = math.sqrt((x1-x)^2+(y1-y)^2)
	return x, y, radius
end

function vfa.load ()
	vfa.points = {}
	vfa.points = {100,100, 320, 120, 400, 200}
	for i = 1, 10 do
		local x = math.random (Width-40)+20
		local y = math.random (Height*1-40)+20
		table.insert (vfa.points, x)
		table.insert (vfa.points, y)
--		table.insert (vfa.points, x+200)
--		table.insert (vfa.points, y)
	end
	reload ()
end

local function pointEvent (point)
	table.insert (vfa.sweepLine, point)
	sortX (vfa.sweepLine)
	local index
	for i, event in ipairs (vfa.sweepLine) do
		if event == point then
			index = i
			break
		end
	end

	if #vfa.sweepLine > 2 then
		local circleEvent = {}
--		print ('index', index)
		local p1 = vfa.sweepLine[index-2]
		local p2 = vfa.sweepLine[index-1]
		local p3 = vfa.sweepLine[index]
		local p4 = vfa.sweepLine[index+1]
		local p5 = vfa.sweepLine[index+2]
		if p1 and p2 and p3 then
			local x, y, r = getCircumcircle (p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
			local circle = {x=x, cy=y, y=y+r, r=r}
--			print ('circle', 'x:'..x,'y:'..y,'r:'..r)
			table.insert (vfa.queue, circle)
		end
		if p3 and p4 and p5 then
			local x, y, r = getCircumcircle (p3.x, p3.y, p4.x, p4.y, p5.x, p5.y)
			local circle = {x=x, cy=y, y=y+r, r=r}
--			print ('circle', 'x:'..x,'y:'..y,'r:'..r)
			table.insert (vfa.queue, circle)
		end
	end
end



local function circleEvent (point)
	
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

local function getParabolaLine (x, y, dirY, xMin, xMax) -- focus, directrix, horizontal limits
-- Function to generate points along a parabola curve
	local line = {}
	local f = math.abs(dirY-y)/2
	local a = -1/(4*f) 
	local b = -2*x*a
	local c = x*x*a + y + f
	local nSteps = math.floor((xMax-xMin)/5)
	local step = (xMax-xMin)/nSteps
	for i = 0, nSteps do
		local x0 = xMin + i * step
		local y0 = a*x0*x0 + b*x0 + c
		table.insert (line, x0)
		table.insert (line, y0)
	end
	return line
end

function vfa.update (dt)
	vfa.dirY = vfa.dirY+0.25*60*dt
	if vfa.dirY > Height then
		reload ()
	end

	for i = #vfa.queue, 1, -1 do
		local event = vfa.queue[i]
		if vfa.dirY > event.y then
			table.remove (vfa.queue, i)
			if event.point then
				pointEvent (event)
			else
				circleEvent (event)
			end
			sortXYBackward (vfa.queue)
		else
			break
		end
	end
	
	-- update parabolas
	local leftX = 0
	vfa.parabolaLines = {}
	for i, focus in ipairs (vfa.sweepLine) do
		local focus2 = vfa.sweepLine[i+1]
		local rightX = Width
		local righty
		if not (i == #vfa.sweepLine) then
			rightX = getParabolaCrossPoint (focus.x, focus.y, focus2.x, focus2.y, vfa.dirY)
		end
		local line = getParabolaLine (focus.x, focus.y, vfa.dirY, leftX, rightX)
		if #line > 3 then
			table.insert (vfa.parabolaLines, line)
		end
		leftX = rightX
	end
end

function vfa.draw ()


	love.graphics.points (vfa.points)
	love.graphics.line (0, vfa.dirY, Width, vfa.dirY)

	
love.graphics.setColor (1,1,0)
	for i, event in ipairs (vfa.sweepLine) do
		love.graphics.circle ('line', event.x, event.y, 5)
	end
	
	love.graphics.setColor (1,0,0)
	
	for i, event in ipairs (vfa.queue) do
		if event.point then
			love.graphics.circle ('line', event.x, event.y, 3)
		else
			love.graphics.circle ('line', event.x, event.cy, event.r)
		end
	end
	
	love.graphics.setColor (1,1,1)
	for i, line in ipairs (vfa.parabolaLines) do
		love.graphics.line (line)
	end
end

-------------------------------------
-- love
-------------------------------------

love.window.setMode(400, 400)
Width, Height = love.graphics.getDimensions( )


function love.load()
	vfa.load ()
	pause = false
end


function love.update(dt)
	if not pause then
		vfa.update (dt)
	end
end


function love.draw()
	vfa.draw ()
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

function love.mousepressed( x, y, button, istouch, presses )
	if button == 1 then -- left mouse button
	elseif button == 2 then -- right mouse button
	end
end

function love.mousemoved( x, y, dx, dy, istouch )
end

function love.mousereleased( x, y, button, istouch, presses )
	if button == 1 then -- left mouse button
	elseif button == 2 then -- right mouse button
	end
end
Animation (71).gif
Animation (71).gif (1.24 MiB) Viewed 100894 times
Attachments
voronoi-fortune-animation-01.love
(2.55 KiB) Downloaded 381 times
2023-10-17T10_01_12-Untitled.png
2023-10-17T10_01_12-Untitled.png (11.35 KiB) Viewed 100894 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
tourgen
Citizen
Posts: 53
Joined: Sat Mar 18, 2023 12:45 am

Re: Parabola functions in Lua

Post by tourgen »

parabolas come up in all kinds of crazy situations. For instance: non-imaging optics - power transport & radiometry. Very cool.
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

Voronoi Fortune's Algorithm Animation in Lua.
The code was reworked, works fine, but the circle event was replaced with beach line cleaning.

Code: Select all

--[[
Copyright 2023 darkfrei

The MIT License
https://opensource.org/license/mit/
<...>
--]]

local vfa = {}

local function sortXYBackward (list)
	table.sort(list, function(a, b) return a.y > b.y or a.y == b.y and a.x > b.x end)
end


local function sortX (list)
	table.sort(list, function(a, b) return a.x < b.x end)
end

local function newCell (x, y)
	local cell = {x=x, y=y}
--	cell.site = {x=x, y=y}
	cell.edges = {}
	cell.vertices = {}
	return cell
end


local function newPoint (x, y)
	print ("newPoint", tostring(x), tostring(y))
	return {x=x, y=y, point = true}
end

local function setPoint (point, x, y)
	print ("newPoint", tostring(x), tostring(y))
	if x then
		point.x=x
	end
	if y then
		point.y=y
	end
end

local function newParabola (cell)
	return {x=cell.x, y=cell.y, cell = cell}
end



local function reload ()
	vfa.dirY = 0
	vfa.segments = {}
	vfa.parabolaLines = {}
	vfa.beachLine = {}
	vfa.queue = {}
	for i = 1, #vfa.points-1, 2 do
		local x, y = vfa.points[i], vfa.points[i+1]
		table.insert (vfa.queue, newCell (x, y))
	end
	sortXYBackward (vfa.queue)
end

local function getCircumcircle (x1, y1, x2, y2, x3, y3)
	local d = 2*(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))
	local t1, t2, t3 = x1*x1+y1*y1, x2*x2+y2*y2, x3*x3+y3*y3
	local x = (t1*(y2-y3)+t2*(y3-y1)+t3*(y1-y2)) / d
	local y = (t1*(x3-x2)+t2*(x1-x3)+t3*(x2-x1)) / d
	local radius = math.sqrt((x1-x)^2+(y1-y)^2)
	return x, y, radius
end

local function getParabolaCircumcircle (p1, p2, p3)
	local x1, y1 = p1.x, p1.y
	local x2, y2 = p2.x, p2.y
	local x3, y3 = p3.x, p3.y
	return getCircumcircle (x1, y1, x2, y2, x3, y3)
end

local function getParabolaCrossPoint (p1x, p1y, p2x, p2y, dirY) -- focus 1, focus 2, directrix Y
	if (p1y == dirY) and (p1y == dirY) then
		local x = (p1x+p2x)/2
		local y = 0
		return x, y, x, y
	end

	-- calculate the focal length (half the distance from the focus to the directrix) for both foci:
	local f1, f2 = math.abs(dirY-p1y)/2, math.abs(dirY-p2y)/2
	-- calculate the a, b, c coefficients as parabolas difference:
	local a1, a2 = -1/(4*f1), -1/(4*f2)
	local b1, b2 = -2*p1x*a1, -2*p2x*a2
	local c1, c2 = p1x*p1x*a1 + p1y + f1, p2x*p2x*a2 + p2y + f2
	-- calculate the coefficients for the combined parabola formed by subtracting the second parabola from the first:
	local a, b, c = a1-a2, b1-b2, c1-c2
	-- calculate the discriminant to determine the number of intersection points:
	local d = b*b-4*a*c
	if d >=0 then
		-- calculate the x-coordinate of the intersection point:
		local x1 = (-b-math.sqrt (d))/(2*a)
		local x2 = (-b+math.sqrt (d))/(2*a)
		-- calculate the y-coordinate of the intersection point using the equation of the first parabola:
		local y1 = a1*x1*x1 + b1*x1 + c1
		local y2 = a1*x2*x2 + b1*x2 + c1
		-- return the intersection point coordinates (just left one):
		return x1, y1, x2, y2
	end
end

local function getParabolasCrossing (p1, p2, dirY)
	local p1x, p1y = p1.x, p1.y
	local p2x, p2y = p2.x, p2.y
	return getParabolaCrossPoint (p1x, p1y, p2x, p2y, dirY)
end

local function getRandomPoints (amount)
	local points = {}
	for i = 1, amount do
		local x = math.random (Width*0.8) + Width*0.1
		local y = math.random (Height*0.8) + Height*0.1
		table.insert (points, x)
		table.insert (points, y)
	end
	return points
end

function vfa.load ()
	vfa.points = getRandomPoints (8)
--	vfa.points = {260,80, 
----		550, 80, -- special case
--		300, 100, 
--		200,140, 
----		920,130, 
--		980,270, 260,350, 350,460, 290,590, 800,650, 610,780, 680,830, 50,890}
	reload ()
end

local function setDoubleLinking (...)
	local nodes = {...}
	local a = nodes[1]
	for i = 2, #nodes do
		local b = nodes[i]
		a.next = b
		b.prev = a
		a = b
	end
end

local function evaluateParabola (parabola, x, dirY)
	local cell = parabola.cell
	local px, py = cell.x, cell.y
	local f = math.abs(dirY-py)/2
	if (f == 0) then
		-- exception, x is same, y is 0
		return x, 0
	end
	local a = -1/(4*f)
	local b = -2*px*a
	local c = px*px*a + py + f
	return x, a*x*x + b*x + c -- x, y
end

local function getCircleEvent(parabola1, parabola2, parabola3)
	-- Check if the parabolas have the same focus (this indicates a potential circle event)
	if (parabola1.focus == parabola2.focus) 
	or (parabola2.focus == parabola3.focus) 
	or (parabola1.focus == parabola3.focus) then
		-- no circle with two points
		return 
	else
		-- Calculate the intersection point of the three parabolas
		local x1, y1 = parabola1.focus.x, parabola1.focus.y
		local x2, y2 = parabola1.focus.x, parabola2.focus.y
		local x3, y3 = parabola1.focus.x, parabola3.focus.y

		local x, y, radius = getCircumcircle (x1, y1, x2, y2, x3, y3)

		-- Calculate the event's y-coordinate
		local eventY = y + radius -- lower than middle

		local highestFocusParabola = parabola1
		if parabola2.focus.y < highestFocusParabola.focus.y then
			highestFocusParabola = parabola2
		end
		if parabola3.focus.y < highestFocusParabola.focus.y then
			highestFocusParabola = parabola3
		end

		-- Create and return the circle event as a table
		local circleEvent = {
			x = x,
			y = eventY,
			radius = radius,
			parabola = highestFocusParabola
		}
		return circleEvent
	end
end




local function newBeachLine (cell)
	local p2 = newParabola (cell) -- parabola
	local p1 = newPoint (0,0) -- separator
	local p3 = newPoint (Width,0)
--	setDoubleLinking (p1, p2, p3)
	local beachLine = {p1, p2, p3}
	return beachLine
end


local function updateBeachLineX (beachLine, dirY)
	for i = 2, #beachLine-3, 2 do -- every parabola
		local p2 = beachLine[i] -- parabola
		local p3 = beachLine[i+1] -- point
		local p4 = beachLine[i+2] -- parabola
		if p2.cell.y == p4.cell.y then
			local x = (p2.cell.x + p4.cell.x)/2
			setPoint (p3, x)
		else
			-- update cross point
			local x, y = getParabolasCrossing (p2, p4, dirY)
			setPoint (p3, x)
		end
	end
end

local function updateBeachLine (beachLine, dirY)
	for i = 2, #beachLine-3, 2 do -- every parabola
		local p2 = beachLine[i] -- parabola
		local p3 = beachLine[i+1] -- point
		local p4 = beachLine[i+2] -- parabola
		if p2.cell.y == p4.cell.y then
			local x = (p2.cell.x + p4.cell.x)/2
			p3.x = x
		else
			-- update cross point
			local x, y = getParabolasCrossing (p2, p4, dirY)
			if not x then
				error ('no x')
			end
			p3.x, p3.y = x, y
		end
	end
end


local function findIndex (beachLine, cell)
	local x = cell.x
	for i = 1, #beachLine-2, 2 do
		local p3 = beachLine[i+2]
		if (not (p3 and p3.x)) or (not x) then
			serpent = require ("serpent")
			print (serpent.block (beachLine))
			print (i, #beachLine)
			print (tostring (p3.x), tostring (x))
		end
		if p3.x >= x then
			return i
		end
	end
end


local function insertParabola (beachLine, cell, index)
--	print ('insertParabola', index)
	local x = cell.x
	local dirY = cell.y
	
	local p1 = beachLine[index] -- point
	local p7 = beachLine[index+2] -- point
	local p2Old = table.remove (beachLine, index+1)  -- old parabola
	
	local p2 = newParabola (p2Old.cell)
	local p4 = newParabola (cell)
	local p6 = newParabola (p2Old.cell)
	local x3, y3 = evaluateParabola (p2Old, x, dirY)
--	print ('x3, y3', x3, y3)
	local p3 = newPoint (x3, y3)
	local p5 = newPoint (x3, y3)
	
	table.insert (beachLine, index+1, p2)
	table.insert (beachLine, index+2, p3)
	table.insert (beachLine, index+3, p4)
	table.insert (beachLine, index+4, p5)
	table.insert (beachLine, index+5, p6)
	
--	print ('inserted point', cell.x, cell.y, #beachLine)
--	for i = 1, #beachLine do
--		local p = beachLine[i]
--		print (i, p.x, p.y, tostring(p.cell == nil))
--	end
--	setDoubleLinking (p1, p2, p3, p4, p5, p6, p7)
end

local function insertCircleEvent (queue, beachLine, index)
	local p4 = beachLine[index+3]
	local p6 = beachLine[index+5]
	local p8 = beachLine[index+7]
	if p4 and p8 then
		local x, y, radius = getParabolaCircumcircle (p4, p6, p8)
		local yEvent = y + radius
		local min = p4
		if p6.y < min.y then
			min = p6
		end
		if p8.y < min.y then
			min = p8
		end
		local sep = newPoint (x, yEvent)
		local circle = {x=x, y=yEvent, sep=sep, par = min}
	end
end


local function pointEvent (cell)
	local dirY = cell.y

	if #vfa.beachLine == 0 then
		vfa.beachLine = newBeachLine (cell)
--		print ('beachLine created')
		return
	end

	
	for i = 1, #vfa.beachLine do
		if not vfa.beachLine[i].x then
			error ('no x'..i)
		end
	end
	
	local p2 = vfa.beachLine [2] -- parabola
	if (#vfa.beachLine == 3) and (p2.y == dirY) then
		local p4 = newParabola (cell) -- parabola
		local p1 = vfa.beachLine [1] -- point
		local p3 = newPoint ((p2.x+p4.x)/2, 0) -- point
		local p5 = vfa.beachLine [3] -- point
		table.insert (vfa.beachLine, 3, p3)
		table.insert (vfa.beachLine, 4, p4)
--		setDoubleLinking (p1, p2, p3, p4, p5)
--		print ('special case beachLine: y=y')
		return
	end
	
	
	for i = 1, #vfa.beachLine do
		if not vfa.beachLine[i].x then
			error ('no x'..i)
		end
	end

	updateBeachLineX (vfa.beachLine, dirY)
	
	
	for i = 1, #vfa.beachLine do
		if not vfa.beachLine[i].x then
			error ('no x'..i)
		end
	end

	local index = 1
	if #vfa.beachLine > 3 then 
		index = findIndex (vfa.beachLine, cell)
	end

	insertParabola (vfa.beachLine, cell, index)
	
	if #vfa.beachLine >= 9 then 
--		print ('index point', index)
		insertCircleEvent (vfa.queue, vfa.beachLine, index)
	end
end



local function circleEvent (circle)
	local separator = circle.sep
	local index
	for i = 3, #vfa.beachLine, 2 do
		local sep = vfa.beachLine[i]
		if separator == sep then
			index = i-1
			break
		end
	end

	if index then
		local p1 = vfa.beachLine[index-3]
		local p5 = vfa.beachLine[index+3]
		local s2 = table.remove (vfa.beachLine, index-1)
		local p3 = table.remove (vfa.beachLine, index-1)
		local s4 = table.remove (vfa.beachLine, index-1)
--		print (tostring(circle.cx))
		s2.x, s2.y = circle.cx, circle.cy
		s4.x, s4.y = circle.cx, circle.cy

		local pNew = {x=circle.cx, y=circle.cy}
		table.insert (vfa.beachLine, index-1, pNew)

		table.insert (vfa.segments, {s2, pNew})
--		table.insert (vfa.segments, {p5, pNew})
	end
end


local function getParabolaLine (x, y, dirY, xMin, xMax) -- focus, directrix, horizontal limits
-- Function to generate points along a parabola curve
--	xMin, xMax = math.min (xMin, xMax), math.max (xMin, xMax)
	local line = {}
	local f = math.abs(dirY-y)/2
	local a = -1/(4*f) 
	local b = -2*x*a
	local c = x*x*a + y + f
	local nSteps = math.floor((xMax-xMin)/5)
	local step = (xMax-xMin)/nSteps
	for i = 0, nSteps do
		local x0 = xMin + i * step
		local y0 = a*x0*x0 + b*x0 + c
		table.insert (line, x0)
		table.insert (line, y0)
	end
	return line
end


local function cleanBeachLine (beachLine, dirY)
	-- no circle event right now, placeholder
	local p1 = beachLine[1]
--	local p2 = beachLine[3]
	for i = 2, #vfa.beachLine-1, 2 do
		local p2 = beachLine[i]
		local p3 = beachLine[i+1]
		local xMax = p2.x
			if p1.x > p3.x then
			p2.toRemove = true
		end
		p1 = p3
	end
	
	local i = 2
	while vfa.beachLine[i] do
		local focus = vfa.beachLine[i]
		if focus.toRemove then
			local p5 = vfa.beachLine[i+2] -- parabola
			local p1 = vfa.beachLine[i-2] -- parabola
			local p4 = table.remove (vfa.beachLine, i+1) -- point
			local p3 = table.remove (vfa.beachLine, i) -- old parabola
			local p2 = table.remove (vfa.beachLine, i-1) -- point
			local x, y = (p2.x+p4.x)/2, (p2.y+p4.y)/2
			if p1 and p5 then
				x, y = getParabolasCrossing (p1, p5, dirY)
				print ('x', tostring (x))
			end
			local p2New = newPoint (x, y)
			print ('p2New', tostring(p2New.x), tostring(p2New.y))
			table.insert (vfa.beachLine, i-1, p2New)
--			setDoubleLinking (p1, p2New, p5)
		else
			i = i + 2
		end
	end
end


function vfa.update ()
--	for i = #vfa.queue, 1, -1 do
	while (#vfa.queue > 0) do -- true
		sortXYBackward (vfa.queue)
		local event = vfa.queue[#vfa.queue]
		if vfa.dirY >= event.y then
			table.remove (vfa.queue, #vfa.queue)
			if event.circle then
				circleEvent (event)
			else
				pointEvent (event)
			end
--			sortXYBackward (vfa.queue)
		else
			break
		end
	end

	
	
	
	-- update parabolas
	local dirY = vfa.dirY
	updateBeachLine (vfa.beachLine, dirY)
	cleanBeachLine (vfa.beachLine, dirY)
	
	vfa.parabolaLines = {}
	for i = 2, #vfa.beachLine-1, 2 do
		local sep1 = vfa.beachLine[i-1]
		local focus = vfa.beachLine[i]
		local sep2 = vfa.beachLine[i+1]

		local xMin = sep1.x
		local xMax = sep2.x

		local line = getParabolaLine (focus.x, focus.y, dirY, xMin, xMax)
		if #line > 3 then
			table.insert (vfa.parabolaLines, line)
		end

	end

end

function vfa.draw ()

	love.graphics.setColor (1,1,1)
	love.graphics.points (vfa.points)
	for i = 1, #vfa.points, 2 do
		love.graphics.circle ('line', vfa.points[i], vfa.points[i+1], 3)
	end

	love.graphics.line (0, vfa.dirY, Width, vfa.dirY)

	-- points
	love.graphics.setColor (1,1,0)
	for i = 1, #vfa.beachLine, 2 do
		local sep = vfa.beachLine[i]
		love.graphics.circle ('line', sep.x, sep.y, 5)
--		love.graphics.print (i, sep.x, sep.y-20)
	end

	-- queue
	love.graphics.setColor (1,0,0)
	for i, event in ipairs (vfa.queue) do
		if event.circle then
			love.graphics.circle ('line', event.cx, event.cy, event.r)
		else
			love.graphics.circle ('line', event.x, event.y, 3)
		end
	end

	
	love.graphics.setColor (1,1,1)
	for i, line in ipairs (vfa.parabolaLines) do
		love.graphics.line (line)
	end

	love.graphics.setColor (0,1,0)
	for i = 2, #vfa.beachLine-1, 2 do
		local focus = vfa.beachLine[i]
		love.graphics.circle ('line', focus.x, focus.y, 5)
--		love.graphics.print (i, focus.x+i, focus.y-20)
	end

	love.graphics.setColor (0,1,1)
	for i, segment in ipairs (vfa.segments) do
		love.graphics.line (segment[1].x, segment[1].y, segment[2].x, segment[2].y)
	end
end

-------------------------------------
-- love
-------------------------------------

--love.window.setMode(1200, 900)
love.window.setMode(800, 800)
Width, Height = love.graphics.getDimensions( )


function love.load()
	love.window.setTitle (Width ..' x '.. Height)
	-- preheat
	for i = 1, 6 do math.random () end


	vfa.load ()
	pause = false
--	pause = true
end


function love.update(dt)
	if not pause then
		vfa.dirY = vfa.dirY+1*60*dt

		if vfa.dirY > Height then
			vfa.points = getRandomPoints (8)
			reload ()
		end
		vfa.update ()
	end
end


function love.draw()
	love.graphics.setLineWidth (2)
	love.graphics.setLineStyle ('rough')
	vfa.draw ()
end

function love.keypressed(key, scancode, isrepeat)
	if false then
	elseif key == "s" then
		vfa.dirY = vfa.dirY+1
		vfa.update ()
	elseif key == "space" then
		pause = not pause
	elseif key == "escape" then
		love.event.quit()
	end
end

function love.mousepressed( x, y, button, istouch, presses )
	reload ()
	vfa.dirY = y
	vfa.update ()
end

function love.mousemoved( x, y, dx, dy, istouch )

end

function love.mousereleased( x, y, button, istouch, presses )
	if button == 1 then -- left mouse button
	elseif button == 2 then -- right mouse button
	end
end

-- https://github.com/darkfrei/love2d-lua-tests/tree/main/voronoi-fortune-animation
Animation (73).gif
Animation (73).gif (921.25 KiB) Viewed 98001 times
Animation (74).gif
Animation (74).gif (240.2 KiB) Viewed 97994 times
https://github.com/darkfrei/love2d-lua- ... -animation
Attachments
voronoi-fortune-animation-03.love
(4.73 KiB) Downloaded 403 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Parabola functions in Lua

Post by darkfrei »

Small example to calculate p h k form of parabola to a b c form with example

Code: Select all

local p = -2 -- vertical distance to focus (negative as direction)
local h = 2 -- horizontal shift
local k = 8 -- vertical shift
print ('h:'..h, 'p:'..p, 'k:'..k)

local a = 1/(4*p)
local b = -2*a*h
local c = a*h^2+k
print ('a:'..a, 'b:'..b, 'c:'..c)

for _, x in ipairs ({-6, 0, 2, 10}) do
	local y = a*x^2+b*x+c
	print ('x:'..x, 'y:'..y)
end
-- roots of parabola:
local x1 = h - math.sqrt (-k/a)
local x2 = h + math.sqrt (-k/a)
print ('x1:'..x1, 'x2:'..x2)
Result:

Code: Select all

h:2	p:-2	k:8
a:-0.125	b:0.5	c:7.5
x:-6	y:0.0
x:0	y:7.5
x:2	y:8.0
x:10	y:0.0
x1:-6.0	x2:10.0
Update:
Making the equations simpler:

Code: Select all

function getFocusParabolaRoots (fx, fy, dirY) -- focus, directrix
	-- https://love2d.org/forums/viewtopic.php?p=257944#p257944
	-- vertex form of parabola
	-- focus: (h, k+p)
	-- directrix Y: y=k-p

	local h = fx -- x shift
	local p = (fy-dirY)/2 -- always negative for voronoi
	local k = fy - p
	
	-- roots
	local left_x = h - math.sqrt (-k*4*p)
	local right_x = h + math.sqrt (-k*4*p)
	print (left_x, right_x)
	return left_x, right_x
end

getFocusParabolaRoots (2, 6, 10) -- focus x, y, directrix y
getFocusParabolaRoots (5, 3, 5) -- focus x, y, directrix y
Output:

Code: Select all

-6	10
1	9
See graph: https://www.desmos.com/calculator/vx3tj0tixk
2023-12-20T09_54_55-Parabola-Focus-Directrix _ Desmos.png
2023-12-20T09_54_55-Parabola-Focus-Directrix _ Desmos.png (134.85 KiB) Viewed 33286 times
:awesome:
Update:
full and short version:

Code: Select all

local function findParabolaCrossings(fx1, fy1, fx2, fy2, dirY)
	if fy1 == fy2 then
		local x = (fx1+fx2)/2
-- p == (fy-dirY)/2 -- negative
-- (x-fx)^2 == 4*p*(y+p-fy)
-- y+p-fy = (x-fx)^2 /(4*p)
-- y = (x-fx)^2 /(4*p) -p+fy
		local y = (x-fx1)^2 / (2 * (fy1 - dirY)) + fy1 - (fy1-dirY)/2 -- right
		print ('a==0: x, y', x, y)
		return x, y, x, y
	else
--		local h1 = fx1
--		local p1 = (fy1-dirY)/2 -- negative
--		local k1 = fy1-p1
		
--		local h2 = fx2
--		local p2 = (fy2-dirY)/2 -- negative
--		local k2 = fy2-(fy2-dirY)/2

		local a = 1/(2*(fy2-dirY))-1/(2*(fy1-dirY))
		local b = -fx2/(fy2-dirY)+fx1/(fy1-dirY)
		local c2 = (fx2^2/(2*(fy2-dirY))+fy2-(fy2-dirY)/2)
		local c1 = (fx1^2/(2*(fy1-dirY))+fy1-(fy1-dirY)/2)

		-- b = -2*a*h
		local h = -b / (2 * a)
		-- c = a*h^2 + k
--		local k = -(b^2 - 4 * a * c) / (4 * a)
		local k = (c2-c1) - a*h^2
		

		local x = h - math.sqrt (-k/a)
		local y = (x-fx1)^2 / (2 * (fy1 - dirY)) + fy1 - (fy1-dirY)/2 -- right
		print ('a='..a..' x, y', x, y)
		return x, y
	end
end

-- parabola crosing example: 
--https://www.desmos.com/calculator/rpimwapsrg

findParabolaCrossings (3, 12, 23, 12, 22) -- 13, 12
findParabolaCrossings (3, 12, 21, 18, 22) -- 13, 12
findParabolaCrossings (3, 12, 26, 20, 22) -- 18.137046609938	5.5434909964282
Short version:
See desmos: https://www.desmos.com/calculator/ecz8x5qoi8

Code: Select all

local function findParabolaCrossing(fx1, fy1, fx2, fy2, dirY) -- focus1, focus2, directrixY
	
	if fy1 == fy2 then
		local x = (fx1+fx2)/2
		local y = (x-fx1)^2 / (2 * (fy1 - dirY)) + fy1 - (fy1-dirY)/2 -- right
		print ('a==0: x, y', x, y)
		return x, y, x, y
	else
		local z2 = 2*(fy2-dirY)
		local z1 = 2*(fy1-dirY)
		local a = 1/z2-1/z1
		local b = 2*fx1/z1-2*fx2/z2
		local c2 = (fx2^2/z2+fy2-z2/4)
		local c1 = (fx1^2/z1+fy1-z1/4)
		local c = c2-c1
		
-- https://www.desmos.com/calculator/ecz8x5qoi8
--		local c = (fx2^2/z2+fy2-z2/4)-(fx1^2/z1+fy1-z1/4)
		
		local k = c - b^2 / (4 * a)
		local x = -b / (2 * a) - math.sqrt (-k/a)
		local y = (x-fx1)^2 / z1 + fy1 - z1/4
		print ('a='..a..' x, y', x, y)
		return x, y -- the point between two parabolas:
	end
end

Code: Select all

findParabolaCrossings (3, 12, 23, 12, 22) -- 13, 12
findParabolaCrossings (3, 12, 21, 18, 22) -- 13, 12
findParabolaCrossings (3, 12, 26, 20, 22) -- 18.137046609938	5.5434909964282
2023-12-20T13_13_56-Parabola-Focus-Directrix _ Desmos.png
2023-12-20T13_13_56-Parabola-Focus-Directrix _ Desmos.png (68.67 KiB) Viewed 33271 times

:awesome:
https://www.desmos.com/calculator/qoxpkk0fib

Code: Select all

local function evaluateParabola(fx, fy, dirY, x) -- focus, directrixY, current x
	local z = 2*(fy-dirY)
	local y = (x-fx)^2 / z + fy - z/4
	return y
end
print (evaluateParabola(3, 12, 22, 18.137)) -- 5.54356155
2023-12-26T13_47_05-Parabola-Focus-Directrix _ Desmos.png
2023-12-26T13_47_05-Parabola-Focus-Directrix _ Desmos.png (50.56 KiB) Viewed 32825 times
:awesome:
How to get the directrix for given focus and point:

Code: Select all

function getDirY_Right (fx, fy, x, y)
	-- https://www.desmos.com/calculator/tfvmwevfn2
	return y + math.sqrt((fy-y)^2+(fx-x)^2)
end
2023-12-26T15_07_13-Parabola-Focus-Directrix _ Desmos.png
2023-12-26T15_07_13-Parabola-Focus-Directrix _ Desmos.png (94.78 KiB) Viewed 32820 times
Last edited by darkfrei on Thu Oct 03, 2024 3:26 pm, edited 1 time in total.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests