diff --git a/chapters/pointvectors/content.en-GB.md b/chapters/pointvectors/content.en-GB.md
index 34407b12..8be2cf24 100644
--- a/chapters/pointvectors/content.en-GB.md
+++ b/chapters/pointvectors/content.en-GB.md
@@ -73,6 +73,6 @@ treated as a sequence of three (elementary) shear operations. When we combine th
The following two graphics show the tangent and normal along a quadratic and cubic curve, with the direction vector coloured blue, and the normal vector coloured red (the markers are spaced out evenly as *t*-intervals, not spaced equidistant).
-
-
+
+
diff --git a/chapters/pointvectors/cubic.js b/chapters/pointvectors/cubic.js
new file mode 100644
index 00000000..d27e4370
--- /dev/null
+++ b/chapters/pointvectors/cubic.js
@@ -0,0 +1,67 @@
+setup() {
+ this.curve = Bezier.defaultCubic(this);
+ setMovable(this.curve.points);
+}
+
+draw() {
+ clear();
+ const curve = this.curve;
+ curve.drawSkeleton();
+ const pts = curve.points;
+ const f = 15;
+
+ for(let i=0; i<=10; i++) {
+ let t = i/10.0;
+ let p = curve.get(t);
+ let d = this.getDerivative(t, pts);
+
+ let m = sqrt(d.x*d.x + d.y*d.y);
+ d = { x: d.x/m, y: d.y/m };
+ let n = this.getNormal(t, d);
+
+ setStroke(`blue`);
+ line(p.x, p.y, p.x + d.x*f, p.y + d.y*f);
+
+ setStroke(`red`);
+ line(p.x, p.y, p.x + n.x*f, p.y + n.y*f);
+
+ setStroke(`purple`);
+ line(p.x, p.y, p.x - n.x*f, p.y - n.y*f);
+
+ setStroke(`black`);
+ circle(p.x, p.y, 3);
+ }
+
+ curve.drawPoints();
+}
+
+getDerivative(t, points) {
+ let mt = (1 - t), a = mt*mt, b = mt*t, c = t*t, d = [
+ {
+ x: 3 * (points[1].x - points[0].x),
+ y: 3 * (points[1].y - points[0].y)
+ },
+ {
+ x: 3 * (points[2].x - points[1].x),
+ y: 3 * (points[2].y - points[1].y)
+ },
+ {
+ x: 3 * (points[3].x - points[2].x),
+ y: 3 * (points[3].y - points[2].y)
+ }
+ ];
+
+ return {
+ x: a * d[0].x + b * d[1].x + c * d[2].x,
+ y: a * d[0].y + b * d[1].y + c * d[2].y
+ };
+}
+
+getNormal(t, d) {
+ const q = sqrt(d.x * d.x + d.y * d.y);
+ return { x: -d.y / q, y: d.x / q };
+}
+
+onMouseMove() {
+ redraw();
+}
diff --git a/chapters/pointvectors/quadratic.js b/chapters/pointvectors/quadratic.js
new file mode 100644
index 00000000..0e64461f
--- /dev/null
+++ b/chapters/pointvectors/quadratic.js
@@ -0,0 +1,63 @@
+setup() {
+ this.curve = Bezier.defaultQuadratic(this);
+ setMovable(this.curve.points);
+}
+
+draw() {
+ clear();
+ const curve = this.curve;
+ curve.drawSkeleton();
+ const pts = curve.points;
+ const f = 15;
+
+ for(let i=0; i<=10; i++) {
+ let t = i/10.0;
+ let p = curve.get(t);
+ let d = this.getDerivative(t, pts);
+
+ let m = sqrt(d.x*d.x + d.y*d.y);
+ d = { x: d.x/m, y: d.y/m };
+ let n = this.getNormal(t, d);
+
+ setStroke(`blue`);
+ line(p.x, p.y, p.x + d.x*f, p.y + d.y*f);
+
+ setStroke(`red`);
+ line(p.x, p.y, p.x + n.x*f, p.y + n.y*f);
+
+ setStroke(`purple`);
+ line(p.x, p.y, p.x - n.x*f, p.y - n.y*f);
+
+ setStroke(`black`);
+ circle(p.x, p.y, 3);
+ }
+
+ curve.drawPoints();
+}
+
+getDerivative(t, points) {
+ let mt = (1 - t), d = [
+ {
+ x: 2 * (points[1].x - points[0].x),
+ y: 2 * (points[1].y - points[0].y)
+ },
+ {
+ x: 2 * (points[2].x - points[1].x),
+ y: 2 * (points[2].y - points[1].y)
+ }
+ ];
+
+ return {
+ x: mt * d[0].x + t * d[1].x,
+ y: mt * d[0].y + t * d[1].y
+ };
+}
+
+getNormal(t, d) {
+ const q = sqrt(d.x * d.x + d.y * d.y);
+ return { x: -d.y / q, y: d.x / q };
+}
+
+onMouseMove() {
+ redraw();
+}
diff --git a/images/chapters/pointvectors/05a1ee3b81a4838292814a7097b4cf50.png b/images/chapters/pointvectors/05a1ee3b81a4838292814a7097b4cf50.png
new file mode 100644
index 00000000..5da69142
Binary files /dev/null and b/images/chapters/pointvectors/05a1ee3b81a4838292814a7097b4cf50.png differ
diff --git a/images/chapters/pointvectors/3158c82ea512f70ec17d4a1ce2e7c7b2.png b/images/chapters/pointvectors/3158c82ea512f70ec17d4a1ce2e7c7b2.png
new file mode 100644
index 00000000..d4d2f0df
Binary files /dev/null and b/images/chapters/pointvectors/3158c82ea512f70ec17d4a1ce2e7c7b2.png differ
diff --git a/images/chapters/reordering/140b23b10b4159b03b2a555db7ddf826.png b/images/chapters/reordering/140b23b10b4159b03b2a555db7ddf826.png
deleted file mode 100644
index 36384d8a..00000000
Binary files a/images/chapters/reordering/140b23b10b4159b03b2a555db7ddf826.png and /dev/null differ
diff --git a/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png b/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png
index 9b71de14..7afa0c92 100644
Binary files a/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png and b/images/chapters/reordering/4541eeb2113d81cbc0c0a56122570d48.png differ
diff --git a/index.html b/index.html
index 8107e51a..e186d5fd 100644
--- a/index.html
+++ b/index.html
@@ -2586,18 +2586,38 @@ function drawCurve(points[], t):
evenly as t-intervals, not spaced equidistant).
diff --git a/ja-JP/index.html b/ja-JP/index.html
index 3ea0641c..450aea6a 100644
--- a/ja-JP/index.html
+++ b/ja-JP/index.html
@@ -2180,18 +2180,38 @@ function drawCurve(points[], t):
evenly as t-intervals, not spaced equidistant).
diff --git a/lib/custom-element/api/base-api.js b/lib/custom-element/api/base-api.js
index e080a396..db0185b9 100644
--- a/lib/custom-element/api/base-api.js
+++ b/lib/custom-element/api/base-api.js
@@ -200,7 +200,7 @@ class BaseAPI {
*/
setup() {
// console.log(`setup`);
- this.moveable = [];
+ this.movable = [];
}
/**
diff --git a/lib/custom-element/api/graphics-api.js b/lib/custom-element/api/graphics-api.js
index 49caf566..83ae78c8 100644
--- a/lib/custom-element/api/graphics-api.js
+++ b/lib/custom-element/api/graphics-api.js
@@ -72,8 +72,8 @@ class GraphicsAPI extends BaseAPI {
? TOUCH_PRECISION_ZONE
: MOUSE_PRECISION_ZONE;
- for (let i = 0, e = this.moveable.length, p, d; i < e; i++) {
- p = this.moveable[i];
+ for (let i = 0, e = this.movable.length, p, d; i < e; i++) {
+ p = this.movable[i];
d = new Vector(p).dist(this.cursor);
if (d <= cdist) {
this.currentPoint = p;
@@ -88,8 +88,8 @@ class GraphicsAPI extends BaseAPI {
this.currentPoint.x = this.cursor.x;
this.currentPoint.y = this.cursor.y;
} else {
- for (let i = 0, e = this.moveable.length, p; i < e; i++) {
- p = this.moveable[i];
+ for (let i = 0, e = this.movable.length, p; i < e; i++) {
+ p = this.movable[i];
if (new Vector(p).dist(this.cursor) <= 5) {
this.setCursor(this.HAND);
return; // NOTE: this is a return, not a break.
@@ -105,12 +105,12 @@ class GraphicsAPI extends BaseAPI {
}
resetMovable(points) {
- this.moveable.splice(0, this.moveable.length);
+ this.movable.splice(0, this.movable.length);
if (points) this.setMovable(points);
}
setMovable(points) {
- points.forEach((p) => this.moveable.push(p));
+ points.forEach((p) => this.movable.push(p));
}
/**
diff --git a/tools/build/markdown/generate-fallback-image.js b/tools/build/markdown/generate-fallback-image.js
index 23c0c25d..d1714e84 100644
--- a/tools/build/markdown/generate-fallback-image.js
+++ b/tools/build/markdown/generate-fallback-image.js
@@ -10,7 +10,9 @@ const __root = path.join(__dirname, `..`, `..`, `..`);
/**
* ...docs go here...
*/
-async function generateFallbackImage(src, width, height) {
+async function generateFallbackImage(localeStrings, src, width, height) {
+ const locale = localeStrings.getCurrentLocale();
+
// Get the sketch code
const sourcePath = path.join(__root, src);
let code;
@@ -23,6 +25,9 @@ async function generateFallbackImage(src, width, height) {
// Do we need to even generate a file here?
const hash = createHash(`md5`).update(code).digest(`hex`);
+
+ if (locale !== localeStrings.getDefaultLocale()) return hash;
+
const destPath = path.dirname(path.join(__root, `images`, src));
const filename = path.join(destPath, `${hash}.png`);
if (fs.existsSync(filename)) return hash;
@@ -50,7 +55,7 @@ async function generateFallbackImage(src, width, height) {
fs.ensureDirSync(path.dirname(filename));
fs.writeFileSync(filename, imageData);
- console.log(`Generated fallback image for ${src}`);
+ console.log(`Generated fallback image for ${src} (${locale})`);
return hash;
}
diff --git a/tools/build/markdown/preprocess-graphics-element.js b/tools/build/markdown/preprocess-graphics-element.js
index c560bd03..42c09faa 100644
--- a/tools/build/markdown/preprocess-graphics-element.js
+++ b/tools/build/markdown/preprocess-graphics-element.js
@@ -51,7 +51,12 @@ async function preprocessGraphicsElement(chapter, localeStrings, markdown) {
src = src.replace(`./`, `./chapters/${chapter}/`);
}
- let imageHash = await generateFallbackImage(src, width, height); // ← this is super fancy functionality.
+ let imageHash = await generateFallbackImage(
+ localeStrings,
+ src,
+ width,
+ height
+ ); // ← this is super fancy functionality.
let imgUrl = path.join(
path.dirname(src.replace(`./`, `./images/`)),
`${imageHash}.png`
diff --git a/zh-CN/index.html b/zh-CN/index.html
index 3c970dd6..8029c917 100644
--- a/zh-CN/index.html
+++ b/zh-CN/index.html
@@ -2190,18 +2190,38 @@ function drawCurve(points[], t):
evenly as t-intervals, not spaced equidistant).