I actually had to implement arbitrary splines are any degree for a graphics class this semester. I believe the method Ref is describing will only give you cubic Catmull-Rom splines. Granted, that's what most people use, but if you want something a bit more robust, here is some Lua code. I quickly converted my C++ code for the project to Lua, so there may be some mistakes, but hopefully the general idea is there:
Code: Select all
local CatmullRom = {}
CatmullRom.__index = CatmullRom
-- http://en.wikipedia.org/wiki/De_Boor%27s_algorithm
local function deBoor(time, values, degree, sep, start)
start = start or 1
local newValues = {}
for i = 0, #values - 1 do
local lhs = (start + i + sep - time) / sep * values[i + 1]
local rhs = (time - (start + i)) / sep * values[i + 2]
table.insert(newValues, lhs + rhs)
end
if sep == 1 then
if #newValues == 1 then
return newValues[0]
else
local i = math.floor(time - degree)
return newValues[i < #newValues and i or i - 1]
end
else
return deBoor(time, newValues, sep - 1, start + 1)
end
end
-- http://en.wikipedia.org/wiki/Neville_algorithm
local function neville(time, values, degree, sep)
sep = sep or 1
local newValues = {}
for i = 0, #values - 1 do
local lhs = (i + sep - time) / sep * values[i + 1]
local rhs = (time - i) / sep * values[i + 2]
table.insert(newValues, lhs + rhs)
end
if sep == degree + 1 then
return deBoor(time, newValues, degree, sep - 1)
else
return neville(time, newValues, degree, sep + 1)
end
end
function CatmullRom:new(controlPoints, degree)
return setmetatable({
controlPoints = controlPoints,
degree = degree,
interpolants = {},
}, self)
end
function CatmullRom:initialize()
for i = self.degree, #controlPoints - degree, 0.1 do
table.insert(self.interpolants, self:interpolate(i))
end
end
-- Gets an arbitrary point of the Catmull-Rom spline
function CatmullRom:interpolate(time)
-- Separate the x- and y-coordinates of the points
local x, y = {}, {}
for _,v in ipairs(self.controlPoints) do
table.insert(x, v[0])
table.insert(y, v[1])
end
-- Catmull-Rom splines are a combination of Lagrange curves and B-Splines,
-- so we use Neville's algorithm to build the Lagrange steps
-- and deBoor's algorithm to build the B-Spline steps
return { neville(time, x, self.degree), neville(time, y, self.degree) }
end
function CatmullRom:draw()
love.graphics.line(self.interpolants)
end
return setmetatable(CatmullRom, { __call = CatmullRom.new })