I reformatted some code I found to use cardinal splines and draw a curve given a set of points to work with my Canvas library, which works quite nicely, but then I wanted to also use said technique to move objects along a given set of points -- a path. SO had several questions pertaining to my problem, and I've tried to implement the last answer of this question, but I honestly have no idea what half the variables in his code mean. Here's my library, and the curve object by itself:
Art.prototype.modules.display.Base.extend({
constructor: function (options) {
// Declare variables for brevity.
var extend = Art.prototype.modules.utility.extend,
defaults = {
points: [],
tension: 0.5,
closed: false
};
// Extend the object with the defaults overwritten by the options.
extend(this, extend(defaults, options));
},
id: 'curve',
draw: function () {
// Declare variables for brevity.
var t = this,
graphics = Art.prototype.modules.display.curve.core.graphics,
controls = [],
n = t.points.length,
getControlPoints = function (a, b, c, d, e, f, tension) {
var x = {
x: Math.sqrt(Math.pow(c - a, 2) + Math.pow(d - b, 2)),
y: Math.sqrt(Math.pow(e - c, 2) + Math.pow(f - d, 2))
};
var y = {
x: tension * x.x / (x.x + x.y)
};
y.y = tension - y.x;
var z = {
x: c + y.x * (a - e),
y: d + y.x * (b - f)
};
var $z = {
x: c - y.y * (a - e),
y: d - y.y * (b - f)
};
return [z.x, z.y, $z.x, $z.y];
};
graphics.strokeStyle = t.stroke;
graphics.lineWidth = t.lineWidth;
if (t.closed) {
t.points.push(t.points[0], t.points[1], t.points[2], t.points[3]);
t.points.unshift(t.points[n - 1]);
t.points.unshift(t.points[n - 1]);
for (var p = 0; p < n; p += 2) {
controls = controls.concat(getControlPoints(t.points[p], t.points[p + 1], t.points[p + 2], t.points[p + 3], t.points[p + 4], t.points[p + 5], t.tension));
}
controls = controls.concat(controls[0], controls[1]);
for (var $p = 2; $p < n + 2; $p += 2) {
graphics.beginPath();
graphics.moveTo(t.points[$p], t.points[$p + 1]);
graphics.bezierCurveTo(controls[2 * $p - 2], controls[2 * $p - 1], controls[2 * $p], controls[2 * $p + 1], t.points[$p + 2], t.points[$p + 3]);
graphics.stroke();
graphics.closePath();
}
} else {
for (var p = 0; p < n - 4; p += 2) {
controls = controls.concat(getControlPoints(t.points[p], t.points[p + 1], t.points[p + 2], t.points[p + 3], t.points[p + 4], t.points[p + 5], t.tension));
}
for (var $p = 2; $p < t.points.length - 5; $p += 2) {
graphics.beginPath();
graphics.moveTo(t.points[$p], t.points[$p + 1]);
graphics.bezierCurveTo(controls[2 * $p - 2], controls[2 * $p - 1], controls[2 * $p], controls[2 * $p + 1], t.points[$p + 2], t.points[$p + 3]);
graphics.stroke();
graphics.closePath();
}
graphics.beginPath();
graphics.moveTo(t.points[0], t.points[1]);
graphics.quadraticCurveTo(controls[0], controls[1], t.points[2], t.points[3]);
graphics.stroke();
graphics.closePath();
graphics.beginPath();
graphics.moveTo(t.points[n - 2], t.points[n - 1]);
graphics.quadraticCurveTo(controls[2 * n - 10], controls[2 * n - 9], t.points[n - 4], t.points[n - 3]);
graphics.stroke();
graphics.closePath();
}
return this;
}
});
I don't necessarily want the code handed to me on a silver platter (although... that would be nice) -- rather, I want to learn the math involved, but preferably in psuedocode and in relatively simple terms. An explanation of the SO answer I linked to would be especially helpful, as it works nicely.