mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-30 03:30:34 +02:00
code comments
This commit is contained in:
@@ -5,21 +5,23 @@ setup() {
|
||||
const degree = this.parameters.degree ?? 3;
|
||||
|
||||
if (degree === 3) {
|
||||
this.f = [
|
||||
// there are three interpolation functions for quadratic curves
|
||||
this.interpolationFunctions = [
|
||||
t => ({ x: t * w, y: h * (1-t) ** 2 }),
|
||||
t => ({ x: t * w, y: h * 2 * (1-t) * t }),
|
||||
t => ({ x: t * w, y: h * t ** 2 })
|
||||
];
|
||||
} else if (degree === 4) {
|
||||
this.f = [
|
||||
// there are four interpolation functions for cubic curves
|
||||
this.interpolationFunctions = [
|
||||
t => ({ x: t * w, y: h * (1-t) ** 3 }),
|
||||
t => ({ x: t * w, y: h * 3 * (1-t) ** 2 * t }),
|
||||
t => ({ x: t * w, y: h * 3 * (1-t) * t ** 2 }),
|
||||
t => ({ x: t * w, y: h * t ** 3})
|
||||
];
|
||||
} else {
|
||||
this.triangle = [[1], [1,1]];
|
||||
this.f = [...new Array(degree + 1)].map((_,i) => {
|
||||
// there are many interpolations functions for more complex curves
|
||||
this.interpolationFunctions = [...new Array(degree + 1)].map((_,i) => {
|
||||
return t => ({
|
||||
x: t * w,
|
||||
y: h * binomial(degree,i) * (1-t) ** (degree-i) * t ** (i)
|
||||
@@ -27,7 +29,11 @@ setup() {
|
||||
});
|
||||
}
|
||||
|
||||
this.s = this.f.map(f => plot(f, 0, 1, degree*5) );
|
||||
// Build the graph for each interpolation function by plotting them,
|
||||
// and capturing the resulting Shape object that yields. We'll draw
|
||||
// those in the draw() function.
|
||||
this.shapes = this.interpolationFunctions.map(f => plot(f, 0, 1, degree*5) );
|
||||
|
||||
setSlider(`.slide-control`, `position`, 0)
|
||||
}
|
||||
|
||||
@@ -36,39 +42,48 @@ draw() {
|
||||
setFill(`black`);
|
||||
setStroke(`black`);
|
||||
|
||||
// In order to plot things nicely, lets scale
|
||||
// down, and plot things in a graph:
|
||||
scale(0.8, 0.9);
|
||||
translate(40,20);
|
||||
drawAxes(`t`, 0, 1, `S`, `0%`, `100%`);
|
||||
|
||||
noFill();
|
||||
|
||||
this.s.forEach((s,i) => {
|
||||
// draw each of the function plots we built in setup()
|
||||
this.shapes.forEach((shape,i) => {
|
||||
// first, draw that plot's mid-line
|
||||
setStroke(randomColor(0.2));
|
||||
line(
|
||||
i/(this.s.length-1) * this.width, 0,
|
||||
i/(this.s.length-1) * this.width, this.height
|
||||
i/(this.shapes.length-1) * this.width, 0,
|
||||
i/(this.shapes.length-1) * this.width, this.height
|
||||
)
|
||||
// and then draw the plot itself
|
||||
setStroke(randomColor(1.0, false ));
|
||||
drawShape(s);
|
||||
drawShape(shape);
|
||||
})
|
||||
|
||||
// depending on the slider, also highlight all values at t=...
|
||||
this.drawHighlight();
|
||||
}
|
||||
|
||||
drawHighlight() {
|
||||
const t = this.position;
|
||||
|
||||
// 0 and 1 are not meaningful to look at. They're just "100% start/end"
|
||||
if (t===0) return;
|
||||
if (t===1) return;
|
||||
|
||||
// draw a little highlighting bar that runs frop top to bottom
|
||||
noStroke();
|
||||
setFill(`rgba(255,0,0,0.3)`);
|
||||
rect(t*this.width - 2, 0, 5, this.height);
|
||||
|
||||
const p = this.f.map(f => f(t));
|
||||
|
||||
// then calculate each interpolation point for our `t` value
|
||||
// and draw it, with a label that says how much it contributes
|
||||
const points = this.interpolationFunctions.map(f => f(t));
|
||||
setFill(`black`);
|
||||
p.forEach(p => {
|
||||
points.forEach(p => {
|
||||
circle(p.x, p.y, 3);
|
||||
text(`${ round(100 * p.y/this.height) }%`, p.x + 10, p.y);
|
||||
});
|
||||
|
@@ -1,34 +1,80 @@
|
||||
let curve;
|
||||
|
||||
setup() {
|
||||
this.curve = new Bezier(this, 90, 200, 25, 100, 220, 40, 210, 240);
|
||||
setMovable(this.curve.points);
|
||||
curve = new Bezier(this, 90, 200, 25, 100, 220, 40, 210, 240);
|
||||
setMovable(curve.points);
|
||||
setSlider(`.slide-control`, `position`, 0);
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
const curve = this.curve;
|
||||
curve.drawSkeleton();
|
||||
curve.drawCurve();
|
||||
|
||||
noFill();
|
||||
setStroke("rgb(200,100,100)");
|
||||
|
||||
let t = this.position;
|
||||
const t = this.position;
|
||||
if (0 < t && t < 1) {
|
||||
curve.drawStruts(t);
|
||||
this.drawStruts(t);
|
||||
}
|
||||
|
||||
curve.drawPoints();
|
||||
|
||||
if (0 < t && t < 1) {
|
||||
let p = curve.get(t);
|
||||
const p = curve.get(t);
|
||||
circle(p.x, p.y, 5);
|
||||
|
||||
let perc = (t*100)|0;
|
||||
let rt = perc/100;
|
||||
const perc = (t*100)|0;
|
||||
const rt = perc/100;
|
||||
text(`Sequential interpolation for ${perc}% (t=${rt})`, 10, 15);
|
||||
}
|
||||
}
|
||||
|
||||
drawStruts(t) {
|
||||
// get all the "de Casteljau" points
|
||||
const p = curve.getStrutPoints(t);
|
||||
|
||||
// and then draw them
|
||||
let s = curve.points.length;
|
||||
let n = curve.points.length;
|
||||
while (--n > 1) {
|
||||
start();
|
||||
for (let i = 0; i < n; i++) {
|
||||
let pt = p[s + i];
|
||||
vertex(pt.x, pt.y);
|
||||
circle(pt.x, pt.y, 5);
|
||||
}
|
||||
end();
|
||||
s += n;
|
||||
}
|
||||
}
|
||||
|
||||
getStrutPoints(t) {
|
||||
const mt = 1 - t;
|
||||
|
||||
// run de Casteljau's algorithm, starting with the base points
|
||||
const points = curve.points.map((p) => new Vector(p));
|
||||
|
||||
let s = 0;
|
||||
let n = p.length + 1;
|
||||
|
||||
// Every iteration will interpolate between `n` points,
|
||||
// as well as decrease that `n` by one. So 4 points yield
|
||||
// 3 new points, which yield 2 new points, which yields 1
|
||||
// final point that is our on-curve point for `t`
|
||||
while (--n > 1) {
|
||||
let list = points.slice(s, s + n);
|
||||
for (let i = 0, e = list.length - 1; i < e; i++) {
|
||||
let pt = list[i + 1].subtract(list[i + 1].subtract(list[i]).scale(mt));
|
||||
points.push(pt);
|
||||
}
|
||||
s += n;
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
onMouseMove() {
|
||||
redraw();
|
||||
}
|
||||
|
@@ -8,29 +8,36 @@ draw() {
|
||||
const dim = this.height,
|
||||
w = dim,
|
||||
h = dim,
|
||||
// midpoints
|
||||
w2 = w/2,
|
||||
h2 = h/2,
|
||||
w4 = w2/2,
|
||||
h4 = h2/2;
|
||||
// quarterpoints
|
||||
q = dim/4;
|
||||
|
||||
// draw axes with (0,0) in the middle of the graphic
|
||||
setStroke(`black`);
|
||||
line(0, h2, w, h2);
|
||||
line(w2, 0, w2, h);
|
||||
|
||||
var offset = {x:w2, y:h2};
|
||||
|
||||
for(let t=0, p, mod; t<=this.steps; t+=0.1) {
|
||||
for(let t=0, p, step=0.1; t<=this.steps; t+=step) {
|
||||
// create a point at distance 'q' from the midpoint
|
||||
p = {
|
||||
x: w2 + w4 * cos(t),
|
||||
y: h2 + h4 * sin(t)
|
||||
x: w2 + q * cos(t),
|
||||
y: h2 + q * sin(t)
|
||||
};
|
||||
|
||||
// and draw it.
|
||||
circle(p.x, p.y, 1);
|
||||
|
||||
mod = t % 1;
|
||||
if(mod >= 0.9) {
|
||||
text(`t = ${ round(t) }`,
|
||||
w2 + 1.25 * w4 * cos(t) - 10,
|
||||
h2 + 1.25 * h4 * sin(t) + 10
|
||||
// then add a text label too, but only "near" each integer
|
||||
// step of `t`. Since we're using floating point numbers,
|
||||
// we can't rely on x * 1/x to actually be x (if only life
|
||||
// were that easy) so we need to check whether `t` is "near"
|
||||
// an integer value instead.
|
||||
if(approx(t % 1, 1, step)) {
|
||||
text(`t = ${round(t)}`,
|
||||
w2 + 1.25 * q * cos(t) - 10,
|
||||
h2 + 1.25 * q * sin(t) + 10
|
||||
);
|
||||
circle(p.x, p.y, 2);
|
||||
}
|
||||
|
@@ -8,20 +8,27 @@ setup() {
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
curve.drawCurve();
|
||||
curve.drawSkeleton();
|
||||
|
||||
let step=0.05, min=-10, max=10;
|
||||
// let's draw the curve from -10 to 10, instead of 0 to 1
|
||||
setStroke(`skyblue`);
|
||||
let min=-10, max=10, step=0.05;
|
||||
|
||||
// calculate the very first point
|
||||
let pt = curve.get(min - step), pn;
|
||||
|
||||
setStroke(`skyblue`);
|
||||
|
||||
for (let t=min; t<=step; t+=step) {
|
||||
// then draw the section from -10 to 0
|
||||
for (let t=min; t<step; t+=step) {
|
||||
pn = curve.get(t);
|
||||
line(pt.x, pt.y, pn.x, pn.y);
|
||||
pt = pn;
|
||||
}
|
||||
|
||||
// then the regular curve, from 0 to 1
|
||||
curve.drawSkeleton();
|
||||
curve.drawCurve();
|
||||
|
||||
// then draw the section from 1 to 10
|
||||
pt = curve.get(1);
|
||||
for (let t=1+step; t<=max; t+=step) {
|
||||
pn = curve.get(t);
|
||||
@@ -29,5 +36,7 @@ draw() {
|
||||
pt = pn;
|
||||
}
|
||||
|
||||
// and just to make sure they show on top,
|
||||
// draw the curve's control points last.
|
||||
curve.drawPoints();
|
||||
}
|
||||
|
@@ -10,8 +10,10 @@ setup() {
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
// draw the curve's polygon, but not the curve itself.
|
||||
curve.drawSkeleton();
|
||||
|
||||
// sample the curve at a few points, and form a polygon with those points
|
||||
noFill();
|
||||
start();
|
||||
for(let i=0, e=this.steps; i<=e; i++) {
|
||||
@@ -20,6 +22,7 @@ draw() {
|
||||
}
|
||||
end();
|
||||
|
||||
// and for completelion, draw the curve's control points
|
||||
curve.drawPoints();
|
||||
|
||||
setFill(`black`);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
let curve;
|
||||
|
||||
setup() {
|
||||
// we're going to look at this as three different "views"
|
||||
setPanelCount(3);
|
||||
curve = Bezier.defaultCubic(this);
|
||||
setMovable(curve.points);
|
||||
@@ -10,20 +11,23 @@ setup() {
|
||||
draw() {
|
||||
clear();
|
||||
|
||||
// form our left and right curves, using the "de Casteljau" points
|
||||
let p = curve.get(this.position);
|
||||
const struts = this.struts = curve.getStrutPoints(this.position);
|
||||
const c1 = new Bezier(this, [struts[0], struts[4], struts[7], struts[9]]);
|
||||
const c2 = new Bezier(this, [struts[9], struts[8], struts[6], struts[3]]);
|
||||
|
||||
// first, draw the same thing we saw in the section on de Casteljau's algorithm
|
||||
this.drawBasics(p);
|
||||
|
||||
// then in the next panel, draw the subcurve to the "left" of point `t`
|
||||
nextPanel();
|
||||
setStroke(`black`);
|
||||
line(0, 0, 0, this.height);
|
||||
this.drawSegment(c1, p, `first`);
|
||||
|
||||
// and in the third panel, draw the subcurve to the "right" of point `t`
|
||||
nextPanel();
|
||||
|
||||
setStroke(`black`);
|
||||
line(0, 0, 0, this.height);
|
||||
this.drawSegment(c2, p, `second`);
|
||||
@@ -33,12 +37,14 @@ drawBasics(p) {
|
||||
curve.drawCurve(`lightgrey`);
|
||||
curve.drawSkeleton(`lightgrey`);
|
||||
curve.drawPoints(false);
|
||||
|
||||
noFill();
|
||||
setStroke(`red`);
|
||||
circle(p.x, p.y, 3);
|
||||
|
||||
setStroke(`lightblue`);
|
||||
curve.drawStruts(this.struts);
|
||||
|
||||
setFill(`black`)
|
||||
text(`The full curve, with struts`, 10, 15);
|
||||
}
|
||||
@@ -47,12 +53,15 @@ drawSegment(c, p, halfLabel) {
|
||||
setStroke(`lightblue`);
|
||||
curve.drawCurve(`lightblue`);
|
||||
curve.drawSkeleton(`lightblue`);
|
||||
|
||||
c.drawCurve();
|
||||
c.drawSkeleton(`black`);
|
||||
c.points.forEach(p => circle(p.x, p.y, 3));
|
||||
|
||||
noFill();
|
||||
setStroke(`red`);
|
||||
circle(p.x, p.y, 3);
|
||||
|
||||
setFill(`black`)
|
||||
text(`The ${halfLabel} half`, 10, 15);
|
||||
}
|
||||
|
@@ -3,9 +3,15 @@ let curve, ratios=[1, 1, 1, 1];
|
||||
setup() {
|
||||
curve = Bezier.defaultCubic(this);
|
||||
setMovable(curve.points);
|
||||
curve.points.forEach((p,i) => {
|
||||
for (let i=0; i<4; i++) {
|
||||
// Set up a slider, but in a way that does not tie it to a variable
|
||||
// that is exposed through `this`, because we want to store its value
|
||||
// in the "ratios" array we already declared globally.
|
||||
//
|
||||
// To make that happen, we tell the slider logic that it should be
|
||||
// calling the setRatio function instead when the slider moves.
|
||||
setSlider(`.ratio-${i+1}`, `!ratio-${i+1}`, 1, v => this.setRatio(i,v))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setRatio(i, v) {
|
||||
|
Reference in New Issue
Block a user