1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-17 06:04:31 +02:00
Files
BezierInfo-2/docs/chapters/reordering/reorder.js
2020-09-19 18:34:03 -07:00

134 lines
3.0 KiB
JavaScript

let points = [];
setup() {
const w = this.width,
h = this.height;
// let's create a wiggle-circle by randomizing points on a circle
for (let i=0; i<10; i++) {
points.push({
x: w/2 + random(20) + cos(PI*2 * i/10) * (w/2 - 40),
y: h/2 + random(20) + sin(PI*2 * i/10) * (h/2 - 40)
});
}
setMovable(points);
this.bindButtons();
}
bindButtons() {
let rbutton = find(`.raise`);
if (rbutton) rbutton.listen(`click`, v => this.raise());
let lbutton = find(`.lower`);
if (lbutton) lbutton.listen(`click`, v => this.lower());
}
draw() {
clear();
this.drawCurve();
}
drawCurve() {
// we can't "just draw" this curve, since it'll be an arbitrary order,
// And the canvas only does 2nd and 3rd - we use de Casteljau's algorithm:
start();
noFill();
setStroke(`black`);
for(let t=0; t<=1; t+=0.01) {
let q = JSON.parse(JSON.stringify(points));
while(q.length > 1) {
for (let i=0; i<q.length-1; i++) {
q[i] = {
x: q[i].x + (q[i+1].x - q[i].x) * t,
y: q[i].y + (q[i+1].y - q[i].y) * t
};
}
q.splice(q.length-1, 1);
}
vertex(q[0].x, q[0].y);
}
end();
start();
setStroke(`lightgrey`);
points.forEach(p => vertex(p.x, p.y));
end();
setStroke(`black`);
points.forEach(p => circle(p.x, p.y, 3));
}
raise() {
const p = points,
np = [p[0]],
k = p.length;
// raising the order of a curve is lossless:
for (let i = 1, pi, pim; i < k; i++) {
pi = p[i];
pim = p[i - 1];
np[i] = {
x: ((k - i) / k) * pi.x + (i / k) * pim.x,
y: ((k - i) / k) * pi.y + (i / k) * pim.y,
};
}
np[k] = p[k - 1];
points = np;
resetMovable(points);
redraw();
}
lower() {
// Based on https://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/
// TODO: FIXME: this is the same code as in the old codebase,
// and it does something odd to the either the
// first or last point... it starts to travel
// A LOT more than it looks like it should... O_o
const p = points,
k = p.length,
data = [],
n = k-1;
if (k <= 3) return;
// build M, which will be (k) rows by (k-1) columns
for(let i=0; i<k; i++) {
data[i] = (new Array(k - 1)).fill(0);
if(i===0) { data[i][0] = 1; }
else if(i===n) { data[i][i-1] = 1; }
else {
data[i][i-1] = i / k;
data[i][i] = 1 - data[i][i-1];
}
}
// Apply our matrix operations:
const M = new Matrix(data);
const Mt = M.transpose(M);
const Mc = Mt.multiply(M);
const Mi = Mc.invert();
if (!Mi) {
return console.error('MtM has no inverse?');
}
// And then we map our k-order list of coordinates
// to an n-order list of coordinates, instead:
const V = Mi.multiply(Mt);
const x = new Matrix(points.map(p => [p.x]));
const nx = V.multiply(x);
const y = new Matrix(points.map(p => [p.y]));
const ny = V.multiply(y);
points = nx.data.map((x,i) => ({
x: x[0],
y: ny.data[i][0]
}));
resetMovable(points);
redraw();
}