Skip to content

Instantly share code, notes, and snippets.

@Be1zebub
Last active August 1, 2023 21:26
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 Be1zebub/a2d1383a6ba09a2c5d71888dbeedf284 to your computer and use it in GitHub Desktop.
Save Be1zebub/a2d1383a6ba09a2c5d71888dbeedf284 to your computer and use it in GitHub Desktop.
bezier.lua
--[[---------------------------------------------------------
Name: BezierLerp( frac, points )
Desc: Lerp point between points with bezier algorithms
-----------------------------------------------------------]]
function math.BezierLerp( frac, points )
local mu = frac * frac
local mum = 1 - frac
local mum2 = mum * mum
if ( points[4] ) then -- Cubic
return mum2 * mum * points[1] + 3 * mum2 * frac * points[2] + 3 * mum * mu * points[3] + mu * frac * points[4]
elseif ( points[3] ) then -- Quadratic
return mum2 * points[1] + 2 * mum * frac * points[2] + mu * points[3]
else -- Linear
return mum * points[1] + frac * points[2]
end
end
--[[---------------------------------------------------------
Name: BezierSpline( points, steps, spline )
Desc: Calc spline between points with bezier algorithms
-----------------------------------------------------------]]
do
local function GetDistance( p1, p2 )
return math.sqrt( ( p2.x - p1.x ) ^ 2 + ( p2.y - p1.y ) ^ 2 )
end
local bezierStepSize = 1 / ( math.pi * 10 )
function math.BezierSpline( points, steps, spline )
if steps == nil then
local distance = GetDistance( points[1], points[2] )
if points[3] then
distance = distance + GetDistance( points[2], points[3] )
end
if points[4] then
distance = distance + GetDistance( points[3], points[4] )
end
steps = math.max( 1, math.floor( distance * bezierStepSize ) )
end
spline = spline or {}
local i = 1
for frac = 0, 1, 1 / steps do
spline[ i ] = math.BezierLerp( frac, points )
i = i + 1
end
return spline
end
end
local function bbox(points)
local mins, maxs = Vector(math.huge, math.huge), Vector()
for _, point in ipairs(points) do
mins.x, mins.y = math.min(mins.x, point.x), math.min(mins.y, point.y)
maxs.x, maxs.y = math.max(maxs.x, point.x), math.max(maxs.y, point.y)
end
return mins, maxs - mins
end
local function test(name, points)
local spline = math.BezierSpline(points)
return function()
local mins, maxs = bbox(points)
draw.SimpleText(name, "DermaDefault", mins.x + maxs.x * 0.5, mins.y, nil, TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM)
-- draw bbox
surface.SetDrawColor(0, 0, 0, 150)
surface.DrawRect(mins.x, mins.y, maxs.x, maxs.y)
-- draw points
surface.SetDrawColor(0, 255, 0)
for i, point in ipairs(points) do
surface.DrawRect(point.x - 4, point.y - 4, 8, 8)
draw.SimpleText(i, "DermaDefault", point.x + 4, point.y + 4)
end
-- draw spline
surface.SetDrawColor(255, 0, 0)
for _, point in ipairs(spline) do
surface.DrawRect(point.x - 2, point.y - 2, 4, 4)
end
-- draw moving point
local frac = RealTime() % 1
local point = math.BezierLerp(frac, points)
surface.SetDrawColor(255, 255, 0)
surface.DrawRect(point.x - 4, point.y - 4, 8, 8)
end
end
do -- tests
local x, y = 32, 32
local Cubic = test("Cubic", {
Vector(x, y),
Vector(x + 256, y),
Vector(x + 256, y + 256),
Vector(x, y + 256),
})
x = x + 320
local Quadratic = test("Quadratic", {
Vector(x, y),
Vector(x + 256, y),
Vector(x, y + 256)
})
x = x + 320
local Linear = test("Linear", {
Vector(x, y),
Vector(x + 256, y + 256)
})
hook.Add("HUDPaint", "bezier tests", function()
Cubic()
Quadratic()
Linear()
end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment