1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-22 00:07:10 +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) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -31,7 +31,7 @@
<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-19T19:29:14+00:00" />
<meta property="og:updated_time" content="2020-09-19T21:15:30+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -581,7 +581,7 @@
<graphics-element title="A (partial) circle: x=sin(t), y=cos(t)" width="275" height="275" src="./chapters/explanation/circle.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\explanation\6d2f915735ebdc05e42c0ea7adc85343.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\explanation\959762e39ae32407e914a687d804ff3a.png" loading="lazy" />
<label>A (partial) circle: x=sin(t), y=cos(t)</label>
</fallback-image>
<input type="range" min="0" max="10" step="0.1" value="5" class="slide-control" />
@@ -749,7 +749,7 @@ function Bezier(3,t):
<graphics-element title="Quadratic interpolations" width="275" height="275" src="./chapters/control/lerp.js" data-degree="3">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\2a0635829051977b265b64ef204f718e.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\f700cd59e6021b0e06b819105ec931da.png" loading="lazy" />
<label>Quadratic interpolations</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -758,7 +758,7 @@ function Bezier(3,t):
<graphics-element title="Cubic interpolations" width="275" height="275" src="./chapters/control/lerp.js" data-degree="4">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\fca18b2602929f80e68d8964832f0f44.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\4b206c16ccf8a06a0834fe9e5254560c.png" loading="lazy" />
<label>Cubic interpolations</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -767,7 +767,7 @@ function Bezier(3,t):
<graphics-element title="15th degree interpolations" width="275" height="275" src="./chapters/control/lerp.js" data-degree="15">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\989f4ca49d7099802cc0345dbeddca8c.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\be82cd31c4892a8f0582b1b5285231d9.png" loading="lazy" />
<label>15th degree interpolations</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -879,7 +879,7 @@ function Bezier(3,t,w[]):
<graphics-element title="Our rational cubic Bézier curve" width="275" height="275" src="./chapters/weightcontrol/rational.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\weightcontrol\be18e8119472af796329f3e2159bdf94.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\weightcontrol\3d71e2b9373684eebcb0dc8563f70b18.png" loading="lazy" />
<label>Our rational cubic Bézier curve</label>
</fallback-image>
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-1" />
@@ -976,7 +976,7 @@ function RationalBezier(3,t,w[],r[]):
>
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\391a61142c56b79260680aefb08cd9c4.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\37948bde4bf0d25bde85f172bf55b9fb.png" loading="lazy" />
<label>Quadratic infinite interval Bézier curve</label>
</fallback-image></graphics-element
>
@@ -989,7 +989,7 @@ function RationalBezier(3,t,w[],r[]):
>
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\baeceec6e1587794b8b275a90d5d85e9.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\2d17acb381ebdd28f0ff43be00d723c4.png" loading="lazy" />
<label>Cubic infinite interval Bézier curve</label>
</fallback-image></graphics-element
>
@@ -1093,7 +1093,7 @@ function RationalBezier(3,t,w[],r[]):
>
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\decasteljau\715d1d2eecc762d6bc1470954b145018.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\decasteljau\df92f529841f39decf9ad62b0967855a.png" loading="lazy" />
<label>Traversing a curve using de Casteljau's algorithm</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -1157,7 +1157,7 @@ function RationalBezier(3,t,w[],r[]):
>
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\3deec756c96e53127cd1d615c61043ae.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\6813bfc608aea11df1dda444b9f18123.png" loading="lazy" />
<label>Flattening a quadratic curve</label>
</fallback-image>
<input type="range" min="1" max="16" step="1" value="4" class="slide-control" />
@@ -1166,7 +1166,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="Flattening a cubic curve" width="275" height="275" src="./chapters/flattening/flatten.js" data-type="cubic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\e2bb7113d5cda2e3fd29bbc54fbe8841.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\0e0e4a2ee46bd89bcfde9f75cfe43292.png" loading="lazy" />
<label>Flattening a cubic curve</label>
</fallback-image>
<input type="range" min="1" max="24" step="1" value="8" class="slide-control" />
@@ -1213,7 +1213,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="Splitting a curve" width="825" height="275" src="./chapters/splitting/splitting.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="825px" height="275px" src="images\chapters\splitting\891133c5f0e140b8c7e179af04e497e0.png" loading="lazy" />
<img width="825px" height="275px" src="images\chapters\splitting\fce5eb16dfcd103797c5e17bd77f1437.png" loading="lazy" />
<label></label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0.5" class="slide-control" />

View File

@@ -33,7 +33,7 @@
<meta property="og:locale" content="ja-JP" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-19T19:29:14+00:00" />
<meta property="og:updated_time" content="2020-09-19T21:15:30+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -528,7 +528,7 @@
<graphics-element title="(部分)円 x=sin(t), y=cos(t)" width="275" height="275" src="./chapters/explanation/circle.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\explanation\6d2f915735ebdc05e42c0ea7adc85343.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\explanation\959762e39ae32407e914a687d804ff3a.png" loading="lazy" />
<label>(部分)円 x=sin(t), y=cos(t)</label>
</fallback-image>
<input type="range" min="0" max="10" step="0.1" value="5" class="slide-control" />
@@ -670,7 +670,7 @@ function Bezier(3,t):
<graphics-element title="2次の補間" width="275" height="275" src="./chapters/control/lerp.js" data-degree="3">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\2a0635829051977b265b64ef204f718e.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\f700cd59e6021b0e06b819105ec931da.png" loading="lazy" />
<label>2次の補間</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -679,7 +679,7 @@ function Bezier(3,t):
<graphics-element title="3次の補間" width="275" height="275" src="./chapters/control/lerp.js" data-degree="4">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\fca18b2602929f80e68d8964832f0f44.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\4b206c16ccf8a06a0834fe9e5254560c.png" loading="lazy" />
<label>3次の補間</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -688,7 +688,7 @@ function Bezier(3,t):
<graphics-element title="15次の補間" width="275" height="275" src="./chapters/control/lerp.js" data-degree="15">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\989f4ca49d7099802cc0345dbeddca8c.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\be82cd31c4892a8f0582b1b5285231d9.png" loading="lazy" />
<label>15次の補間</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -787,7 +787,7 @@ function Bezier(3,t,w[]):
<graphics-element title="Our rational cubic Bézier curve" width="275" height="275" src="./chapters/weightcontrol/rational.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\weightcontrol\be18e8119472af796329f3e2159bdf94.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\weightcontrol\3d71e2b9373684eebcb0dc8563f70b18.png" loading="lazy" />
<label>Our rational cubic Bézier curve</label>
</fallback-image>
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-1" />
@@ -868,14 +868,14 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="無限区間の2次ベジエ曲線" width="275" height="275" src="./chapters/extended/extended.js" data-type="quadratic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\391a61142c56b79260680aefb08cd9c4.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\37948bde4bf0d25bde85f172bf55b9fb.png" loading="lazy" />
<label>無限区間の2次ベジエ曲線</label>
</fallback-image></graphics-element
>
<graphics-element title="無限区間の3次ベジエ曲線" width="275" height="275" src="./chapters/extended/extended.js" data-type="cubic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\baeceec6e1587794b8b275a90d5d85e9.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\2d17acb381ebdd28f0ff43be00d723c4.png" loading="lazy" />
<label>無限区間の3次ベジエ曲線</label>
</fallback-image></graphics-element
>
@@ -965,7 +965,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="ド・カステリョのアルゴリズムで曲線をたどる" width="275" height="275" src="./chapters/decasteljau/decasteljau.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\decasteljau\715d1d2eecc762d6bc1470954b145018.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\decasteljau\df92f529841f39decf9ad62b0967855a.png" loading="lazy" />
<label>ド・カステリョのアルゴリズムで曲線をたどる</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -1015,7 +1015,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="2次ベジエ曲線の平坦化" width="275" height="275" src="./chapters/flattening/flatten.js" data-type="quadratic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\3deec756c96e53127cd1d615c61043ae.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\6813bfc608aea11df1dda444b9f18123.png" loading="lazy" />
<label>2次ベジエ曲線の平坦化</label>
</fallback-image>
<input type="range" min="1" max="16" step="1" value="4" class="slide-control" />
@@ -1024,7 +1024,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="3次ベジエ曲線の平坦化" width="275" height="275" src="./chapters/flattening/flatten.js" data-type="cubic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\e2bb7113d5cda2e3fd29bbc54fbe8841.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\0e0e4a2ee46bd89bcfde9f75cfe43292.png" loading="lazy" />
<label>3次ベジエ曲線の平坦化</label>
</fallback-image>
<input type="range" min="1" max="24" step="1" value="8" class="slide-control" />
@@ -1066,7 +1066,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="曲線の分割" width="825" height="275" src="./chapters/splitting/splitting.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="825px" height="275px" src="images\chapters\splitting\891133c5f0e140b8c7e179af04e497e0.png" loading="lazy" />
<img width="825px" height="275px" src="images\chapters\splitting\fce5eb16dfcd103797c5e17bd77f1437.png" loading="lazy" />
<label></label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0.5" class="slide-control" />

View File

@@ -14,13 +14,17 @@ draw() {
All code starts at `setup()`, automatically calling `draw()` after setup has been completed. Standard JS scoping applies, so any variable declared outside of `setup`/`draw` will be a "global" variable.
## User Events
Note that neither of these functions are _required_: without a `setup()` function the code will just jump straight to `draw()`, and without a `draw()` function the code will simply not draw anything beyond the initial empty canvas.
## User-initiated events
### Touch/mouse events
Graphics code can react to touch/mouse, which can be handled using:
- `onMouseDown()` triggered by mouse/touch start events.
- `onMouseUp()` triggered by mouse/touch end events.
- `onMouseMose()` triggered by moving the mouse/your finger.
- `onMouseMove()` triggered by moving the mouse/your finger.
Mouse event data can be accessed via the `this.cursor` property, which encodes:
@@ -37,6 +41,35 @@ Mouse event data can be accessed via the `this.cursor` property, which encodes:
}
```
#### Example
```js
setup() {
this.defaultBgColor = this.bgColor = `green`;
}
draw() {
clear(this.bgColor);
}
onMouseDown() {
this.bgColor = `blue`;
redraw();
}
onMouseMove() {
this.bgColor = `red`;
redraw();
}
onMouseUp() {
this.bgColor = this.defaultBgColor;
redraw();
}
```
### Keyboard events
Graphics code can also react to keyboard events, although this is a great way to make sure your code won't work for mobile devices, so it's better to use range sliders to keep things accessible. That said, they can be handled using:
- `onKeyDown()` triggered by pressing a key
@@ -54,15 +87,45 @@ Additionally, the `this.keyboard` property can be consulted for named keys to se
```js
draw() {
if (this.keyboard[`ArrowUp`]) {
...
if (this.keyboard[`w`] && this.keyboard[`d`]) {
// move up-left
}
}
```
#### Example
```js
setup() {
this.y = this.height/2;
}
draw() {
clear();
setColor(`black`);
rect(0, this.y-1, this.width, 3);
}
onKeyDown() {
const key = this.keyboard.currentKey;
if (key === `ArrowUp`) {
y -= 5
}
if (key === `ArrowDown`) {
y += 5;
}
y = constrain(y, 0, this.height);
redraw();
}
```
## Controllable parameters
Graphics code can be passed external values from HTML using data attributes:
Graphics code can be provided with outside values in two different ways.
### Using fixed startup parameters
Graphics code can be passed fixed values from HTML using data attributes:
```html
<graphics-element src="..." data-propname="somevalue"></graphics-element>
@@ -74,21 +137,38 @@ which can be access on the code side using
this.parameters.propname;
```
Additionally, graphics code has special functions for range values, either directly in code:
Note that `this.parameters` is a protected object. Properties of the parameters object can be updated by your code, but you cannot reassign `this.parameters` itself.
### Using dynamic value sliders
Graphics code has also be provided with dynamic values by using range sliders. There are two ways to do this: purely in code, or by tying the graphics code to HTML sliders.
#### In code
If sliders may be dynamically required, the `addSlider` function can be used:
```js
setup() {
// name, min, max, step, initial value
addSlider(`rangeValue`, 0, 1, 0.001, 0.5);
}
draw() {
// values defined through sliders become accessible on `this`:
console.log(this.rangeValue);
}
```
Alternatively, they can be hooked into a predefined slider:
Its function signature is `addSlider(property name, min, max, step, initial value)`, in which the arguments represent:
- `property name` a propertyname string that may start with `!`. If no `!` is used, the property name should follow the rules for variable names, as the property will be exposed as `this.propertyname` (e.g. is you use `rangeValue`, then `this.rangeValue` will exis and be kept up to date by the slider logic). If `!` is used, no `this.propertyname` will be be set up for use in your code. Regardless of whether `!` is used or not, the property name will also be displayed in the slider's UI.
- `min` the minimum numerical value this variable will be able to take on
- `max` the meximum numerical value this variable will be able to take on
- `step` the value increase/decrease per step of the slider.
- `initial value` the value that the associated variable will be assigned as part of the `addSlider` call.
#### From HTML
You can also "presupply" a graphic with sliders, if you know your graphic has a fixed number of dynamic variables. This uses the standard HTML `<input type="range">` element:
```html
<graphics-element src="..." data-propname="somevalue">
@@ -96,11 +176,10 @@ Alternatively, they can be hooked into a predefined slider:
</graphics-element>
```
using the `setSlider` function:
With the graphic code using `setSlider` with a query selector to find your slider element and tie it to a variable:
```js
setup() {
// local query selector, name, initial value
setSlider(`.my-slider`, `rangeValue`, 0.5);
}
@@ -109,6 +188,17 @@ draw() {
}
```
Its function signature is `setSlider(query selector, property name, initial value)`, in which the arguments represent:
- `query select` a CSS query selector for finding the right slider in your `<graphics-element>` tree. If you only have one slider then this query selector can simply be `input[type=range]`, but if you have multiple sliders it's a better idea to give each slider a CSS class that can be used to indentify it.
- `property name` a propertyname string that may start with `!`. If no `!` is used, the property name should follow the rules for variable names, as the property will be exposed as `this.propertyname` (e.g. is you use `rangeValue`, then `this.rangeValue` will exis and be kept up to date by the slider logic). If `!` is used, no `this.propertyname` will be be set up for use in your code. Regardless of whether `!` is used or not, the property name will also be displayed in the slider's UI.
- `initial value` the value that the associated variable will be assigned as part of the `addSlider` call.
Note that while it might seem that `<input>` elements can be made fully self-descriptive for both the property name (using the `name` attribute) and initial value (using the `value` attribute), this code still needs to do the right thing even in the absence of an HTML page, and so the property name and initial value are explicitly required.
**warning:** if you try to set up a slider for a property name that you have already defined, the code will throw a runtime error.
## Movable points
An important part of the Graphics API is showing shapes that are controlled or defined by coordinates, and as there are special functions for marking points as "movable" - that is, these points can be click/touch-dragged around a graphic. To fascilitate this, the following functions can be used:
@@ -117,12 +207,12 @@ An important part of the Graphics API is showing shapes that are controlled or d
- `resetMovable()` will clear the list of movable points.
- `resetMovable(points, ...)` is the same as calling `resetMovable()` followed by `setMovable(points, ...)`.
## The API
The following is the list of API functions that can be used to draw... whatever you like, really.
### gGobal constants
### Global constants
- `PI` 3.14159265358979
- `TAU` 6.28318530717958
@@ -247,7 +337,7 @@ In addition to transformations, there are also two functions to allow you to map
- `drawShape(...shapes)` draw one or more saved full shapes (see below)
- `image(img, x = 0, y = 0, w = auto, h = auto)` draw an image at some (x,y) coordinate, defaulting to (0,0), scaled to some width and height, defaulting to the native image dimensions
- `line(x1, y1, x2, y2)` draw a line from (x1,y1) to (x2,y2)
- `plot(fn, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1)` plot a function defined by `fn` (which must take a single numerical input, and output an object of the form `{x:..., y:...}`) for inputs in the interval [start,end], at a resolution of `steps` steps, with the resulting values scaled by `xscale` horizontally, and `yscale` vertically.
- `plot(fn, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1)` plot a function defined by `fn` (which must take a single numerical input, and output an object of the form `{x:..., y:...}`) for inputs in the interval [start,end], at a resolution of `steps` steps, with the resulting values scaled by `xscale` horizontally, and `yscale` vertically. This function returns the plot as a `Shape` for later reuse.
- `point(x, y)` draw a single point at (x,y)
- `rect(x, y, w, h)` draw a rectangle from (x,y) with width `w` and height `h`
- `redraw()` triggers a new draw loop. Use this instead of calling `draw()` directly when you wish to draw a new frame as part of event handling.
@@ -263,7 +353,7 @@ In addition to transformations, there are also two functions to allow you to map
- `saveShape()` returns the current shape for later reuse
## Built in object types
## Built-in object types
### Shape

View File

@@ -53,7 +53,10 @@ class BaseAPI {
this.dataset = {};
}
}
this.parameters = Object.fromEntries(
Object.defineProperty(this, `parameters`, {
writable: false,
configurable: false,
value: Object.fromEntries(
Object.entries(this.dataset)
.map((pair) => {
let name = pair[0];
@@ -69,7 +72,8 @@ class BaseAPI {
return [name, v];
})
.filter((v) => v.length)
);
),
});
this.addListeners();
this.setSize(width, height);
this.currentPoint = false;

View File

@@ -605,7 +605,7 @@ class GraphicsAPI extends BaseAPI {
/**
* Yield a snapshot of the current shape.
*/
save() {
saveShape() {
return this.currentShape;
}

View File

@@ -22,12 +22,12 @@
<meta property="og:title" content="Rewriting the tech stack" />
<meta property="og:image" content="https://pomax.github.io/bezierinfo/images/og-image.png" />
<meta property="og:type" content="text" />
<meta property="og:url" content="https://pomax.github.io/bezierinfo/news/2020-09-20.html" />
<meta property="og:url" content="https://pomax.github.io/bezierinfo/news/2020-09-18.html" />
<meta property="og:description" content="Rewriting the tech stack" />
<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2020-09-20T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-19T19:29:14+00:00" />
<meta property="og:published_time" content="2020-09-18T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-19T21:15:30+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -79,7 +79,7 @@
<header>
<h1>Rewriting the tech stack</h1>
<h5 class="post-date">Sun, 20 Sep 2020</h5>
<h5 class="post-date">Fri, 18 Sep 2020</h5>
</header>
<main>

View File

@@ -94,7 +94,7 @@
</p>
<ul>
<li><a href="news/2020-09-20.html">Rewriting the tech stack</a> (Sun, 20 Sep 2020)</li>
<li><a href="news/2020-09-18.html">Rewriting the tech stack</a> (Fri, 18 Sep 2020)</li>
</ul>
</main>

View File

@@ -16,7 +16,7 @@
<item>
<title>Rewriting the tech stack</title>
<link>https://pomax.github.io/bezierinfo/news/2020-09-20.html</link>
<link>https://pomax.github.io/bezierinfo/news/2020-09-18.html</link>
<description>
&lt;p&gt;Once upon a time, I needed to draw some Bezier curves because I was trying to create a Japanese kanji composition system that turned strokes into outlines, and that required knowing how to offset Bezier curves and... at the time (2011, time flies) there was no good single source of information for Bezier curves on the web. So I made one. Sure it started small, but it turns out that if you just keep adding bits to something, several years later you have quite the monster, and a single HTML file becomes intractible.&lt;/p&gt;
&lt;p&gt;So, in 2016, when &lt;a href=&quot;&quot;&gt;React.js&lt;/a&gt; exploded onto the scene, I rewrote the primer as a React app, and it became a lot easier to maintain. Like, &lt;em&gt;a lot&lt;/em&gt; a lot. However, there was a downside: no JS meant no content. Sure, server-side rendering sort of existed, but not really, and because the Primer is hosted through github, there was no &quot;server&quot; to run. Plus, trying to rehydrate an app the size of the Primer from a giant HTML file had truly &lt;em&gt;dire&lt;/em&gt; performance.&lt;/p&gt;
@@ -44,8 +44,8 @@
&lt;p&gt;See you in the next post!&lt;/p&gt;
&lt;p&gt;— Pomax&lt;/p&gt;
</description>
<pubDate>2020-09-20</pubDate>
<guid>2020-09-20.html</guid>
<pubDate>2020-09-18</pubDate>
<guid>2020-09-18.html</guid>
</item>

View File

@@ -33,7 +33,7 @@
<meta property="og:locale" content="zh-CN" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-19T19:29:14+00:00" />
<meta property="og:updated_time" content="2020-09-19T21:15:30+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -504,7 +504,7 @@
<graphics-element title="(一部分的)圆: x=sin(t), y=cos(t)" width="275" height="275" src="./chapters/explanation/circle.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\explanation\6d2f915735ebdc05e42c0ea7adc85343.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\explanation\959762e39ae32407e914a687d804ff3a.png" loading="lazy" />
<label>(一部分的)圆: x=sin(t), y=cos(t)</label>
</fallback-image>
<input type="range" min="0" max="10" step="0.1" value="5" class="slide-control" />
@@ -640,7 +640,7 @@ function Bezier(3,t):
<graphics-element title="二次插值" width="275" height="275" src="./chapters/control/lerp.js" data-degree="3">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\2a0635829051977b265b64ef204f718e.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\f700cd59e6021b0e06b819105ec931da.png" loading="lazy" />
<label>二次插值</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -649,7 +649,7 @@ function Bezier(3,t):
<graphics-element title="三次插值" width="275" height="275" src="./chapters/control/lerp.js" data-degree="4">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\fca18b2602929f80e68d8964832f0f44.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\4b206c16ccf8a06a0834fe9e5254560c.png" loading="lazy" />
<label>三次插值</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -658,7 +658,7 @@ function Bezier(3,t):
<graphics-element title="15次插值" width="275" height="275" src="./chapters/control/lerp.js" data-degree="15">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\control\989f4ca49d7099802cc0345dbeddca8c.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\control\be82cd31c4892a8f0582b1b5285231d9.png" loading="lazy" />
<label>15次插值</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -753,7 +753,7 @@ function Bezier(3,t,w[]):
<graphics-element title="Our rational cubic Bézier curve" width="275" height="275" src="./chapters/weightcontrol/rational.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\weightcontrol\be18e8119472af796329f3e2159bdf94.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\weightcontrol\3d71e2b9373684eebcb0dc8563f70b18.png" loading="lazy" />
<label>Our rational cubic Bézier curve</label>
</fallback-image>
<input type="range" min="0.01" max="2" value="1" step="0.01" class="ratio-1" />
@@ -830,14 +830,14 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="二次无限区间贝塞尔曲线" width="275" height="275" src="./chapters/extended/extended.js" data-type="quadratic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\391a61142c56b79260680aefb08cd9c4.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\37948bde4bf0d25bde85f172bf55b9fb.png" loading="lazy" />
<label>二次无限区间贝塞尔曲线</label>
</fallback-image></graphics-element
>
<graphics-element title="三次无限区间贝塞尔曲线" width="275" height="275" src="./chapters/extended/extended.js" data-type="cubic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\extended\baeceec6e1587794b8b275a90d5d85e9.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\extended\2d17acb381ebdd28f0ff43be00d723c4.png" loading="lazy" />
<label>三次无限区间贝塞尔曲线</label>
</fallback-image></graphics-element
>
@@ -920,7 +920,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="用de Casteljau算法来遍历曲线" width="275" height="275" src="./chapters/decasteljau/decasteljau.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\decasteljau\715d1d2eecc762d6bc1470954b145018.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\decasteljau\df92f529841f39decf9ad62b0967855a.png" loading="lazy" />
<label>用de Casteljau算法来遍历曲线</label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0" class="slide-control" />
@@ -969,7 +969,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="拉平一条二次曲线" width="275" height="275" src="./chapters/flattening/flatten.js" data-type="quadratic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\3deec756c96e53127cd1d615c61043ae.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\6813bfc608aea11df1dda444b9f18123.png" loading="lazy" />
<label>拉平一条二次曲线</label>
</fallback-image>
<input type="range" min="1" max="16" step="1" value="4" class="slide-control" />
@@ -978,7 +978,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="拉平一条三次曲线" width="275" height="275" src="./chapters/flattening/flatten.js" data-type="cubic">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="275px" height="275px" src="images\chapters\flattening\e2bb7113d5cda2e3fd29bbc54fbe8841.png" loading="lazy" />
<img width="275px" height="275px" src="images\chapters\flattening\0e0e4a2ee46bd89bcfde9f75cfe43292.png" loading="lazy" />
<label>拉平一条三次曲线</label>
</fallback-image>
<input type="range" min="1" max="24" step="1" value="8" class="slide-control" />
@@ -1022,7 +1022,7 @@ function RationalBezier(3,t,w[],r[]):
<graphics-element title="分割一条曲线" width="825" height="275" src="./chapters/splitting/splitting.js">
<fallback-image>
<span class="view-source">Scripts are disabled. Showing fallback image.</span>
<img width="825px" height="275px" src="images\chapters\splitting\891133c5f0e140b8c7e179af04e497e0.png" loading="lazy" />
<img width="825px" height="275px" src="images\chapters\splitting\fce5eb16dfcd103797c5e17bd77f1437.png" loading="lazy" />
<label></label>
</fallback-image>
<input type="range" min="0" max="1" step="0.01" value="0.5" class="slide-control" />