Arbitrary hermite curve thingy for Canvas.
$(main);
function main() {
var $c = $('#demo');
var ctx = $c[0].getContext('2d');
ctx.translate(0, $c.height());
ctx.scale(1, -1);
var points = [[0, 0], [55, 43], [94, 226], [255, 255]];
drawCurve(ctx, hermite(points));
}
// http://en.wikibooks.org/wiki/Cg_Programming/Unity/Hermite_Curves
function hermite(points) {
var pointsPerStep, pointsPerSegment;
var i, len, p0, p1;
var ret = [];
for(i = 0, len = points.length - 1; i < len; i++) {
p0 = points[i];
p1 = points[i + 1];
pointsPerSegment = (p1[0] - p0[0]) * 2;
pointsPerStep = 1 / pointsPerSegment;
if(i == points.length - 2) {
// the last point of the last segment should reach p1
pointsPerStep = 1 / (pointsPerSegment - 1);
}
ret.push({
p0: p0,
p1: p1,
m0: Vec2D.mul(Vec2D.sub(p1, i > 0? points[i - 1]: p0), 0.5),
m1: Vec2D.mul(Vec2D.sub(i < points.length - 2? points[i + 2]: p1, p0), 0.5),
len: pointsPerSegment,
step: pointsPerStep
});
}
return ret;
}
function drawCurve(ctx, segments) {
segments.forEach(drawSegment.bind(null, ctx));
}
function drawSegment(ctx, o) {
var t, i, pos, p0, p1, m0, m1, fac0, fac1, fac2, fac3;
for(i = 0; i < o.len; i++) {
t = i * o.step;
fac0 = 2.0 * t * t * t - 3.0 * t * t + 1.0;
fac1 = t * t * t - 2.0 * t * t + t;
fac2 = -2.0 * t * t * t + 3.0 * t * t;
fac3 = t * t * t - t * t;
pos = Vec2D.add(
Vec2D.mul(o.p0, fac0),
Vec2D.mul(o.m0, fac1),
Vec2D.mul(o.p1, fac2),
Vec2D.mul(o.m1, fac3)
);
ctx.strokeRect(pos[0], pos[1], 1, 1);
}
}
var Vec2D = decorate({
add: function(a, b) {return a + b;},
sub: function(a, b) {return a - b;},
div: function(a, b) {return a / b;},
mul: function(a, b) {return a * b;}
}, function(op) {
return function() {
var pos = arguments[0].slice();
var i, len, arg;
for(i = 1, len = arguments.length; i < len; i++) {
arg = arguments[i];
pos = update(pos, isNumber(arg)? [arg, arg]: arg);
}
return pos;
};
function update(pos, arg) {
return pos.map(function(v, i) {
return op(v, arg[i]);
});
}
});
function decorate(o, fn) {
var ret = {};
for(var k in o) {
if(o.hasOwnProperty(k)) {
ret[k] = fn(o[k]);
}
}
return ret;
}
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}