mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-17 06:04:31 +02:00
134 lines
3.0 KiB
JavaScript
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();
|
|
}
|