One method to do what the OP wants with LÖVE is the
Kochanek-Bartels method. It works by drawing one Hermite curve between each pair of points.
While the method generates Hermite curves, these can easily be converted to Bézier for them to be drawn with LÖVE. You can adjust tension to the desired value, and leave continuity and bias set to 0. In my experience, a bit of negative tension works better than zero tension (which would produce a Catmull-Rom spline).
To convert a start Hermite vector to the first control point of a cubic Bézier, divide it by 3 and add the start point. To convert an end Hermite vector to the second point of a cubic Bézier, divide it by -3 and add the end point.
For example, if the Hermite curve is given by start and end points p0 and p1, and tangents d0 and d1, the corresponding Bézier control points P0, P1, P2, P3 (where P0 and P3 are the starting and ending point, and P1 and P2 are the control points) are:
P0 = p0
P1 = p0 + d0 / 3
P2 = p1 - d1 / 3
P3 = p1
Edit: Sorry, I previously said "multiply by 3". I misremembered, it should have been "divide by 3".
Edit 2: Simple example follows:
Code: Select all
local bez = love.math.newBezierCurve(0,0, 0,0, 0,0, 0,0)
local function Kochanek_Bartels(t, c, b,
p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y)
local P0x = p1x
local P0y = p1y
local P1x = p1x + ( (1-t)*(1+b)*(1+c) * (p1x - p0x)
+ (1-t)*(1-b)*(1-c) * (p2x - p1x) ) / 6
local P1y = p1y + ( (1-t)*(1+b)*(1+c) * (p1y - p0y)
+ (1-t)*(1-b)*(1-c) * (p2y - p1y) ) / 6
local P2x = p2x - ( (1-t)*(1+b)*(1-c) * (p2x - p1x)
+ (1-t)*(1-b)*(1+c) * (p3x - p2x) ) / 6
local P2y = p2y - ( (1-t)*(1+b)*(1-c) * (p2y - p1y)
+ (1-t)*(1-b)*(1+c) * (p3y - p2y) ) / 6
local P3x = p2x
local P3y = p2y
bez:setControlPoint(1, P0x, P0y)
bez:setControlPoint(2, P1x, P1y)
bez:setControlPoint(3, P2x, P2y)
bez:setControlPoint(4, P3x, P3y)
love.graphics.line(bez:render())
end
local points = {0, 0, 200, 0, -200, -150, -250, 120, 300, 250}
local tension = -0.6
local continuity = 0
local bias = 0
function love.draw()
love.graphics.translate(love.graphics.getWidth()/2, love.graphics.getHeight()/2)
-- Draw dots
love.graphics.setColor(1, 1, 1, 1)
for i = 1, #points, 2 do
love.graphics.circle("fill", points[i], points[i + 1], 5)
end
love.graphics.setColor(0, 0.3, 1, 1)
local len = #points
local npoints = len / 2
if npoints >= 3 then
-- do first segment by repeating first point
Kochanek_Bartels(tension, continuity, bias,
points[1], points[2], points[1], points[2],
points[3], points[4], points[5], points[6])
-- do middle segments
for i = 2, npoints - 2 do
local base = i * 2 - 1
Kochanek_Bartels(tension, continuity, bias,
points[base - 2], points[base - 1],
points[base], points[base + 1],
points[base + 2], points[base + 3],
points[base + 4], points[base + 5])
end
-- do last segment by repeating last point
Kochanek_Bartels(tension, continuity, bias,
points[len - 5], points[len - 4], points[len - 3], points[len - 2],
points[len - 1], points[len], points[len - 1], points[len])
elseif npoints == 2 then
-- just draw a line between the points
love.graphics.line(points)
end
end
function love.keypressed(k) return k == "escape" and love.event.quit() end
Snapshot: