1
0
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:
Pomax
2020-09-19 14:16:00 -07:00
parent 7c530fee56
commit ad872f83c5
39 changed files with 306 additions and 117 deletions

View File

@@ -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);
});

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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`);

View File

@@ -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);
}

View File

@@ -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) {