diff --git a/chapters/aligning/content.en-GB.md b/chapters/aligning/content.en-GB.md
index f50fd2e8..0282abb2 100644
--- a/chapters/aligning/content.en-GB.md
+++ b/chapters/aligning/content.en-GB.md
@@ -41,4 +41,4 @@ If we drop all the zero-terms, this gives us:
We can see that our original curve definition has been simplified considerably. The following graphics illustrate the result of aligning our example curves to the x-axis, with the cubic case using the coordinates that were just used in the example formulae:
-
+
diff --git a/chapters/aligning/cubic.js b/chapters/aligning/cubic.js
index 4cd2def2..ad00abea 100644
--- a/chapters/aligning/cubic.js
+++ b/chapters/aligning/cubic.js
@@ -13,27 +13,40 @@ draw() {
translate(this.width/2, 0);
line(0,0,0,this.height);
+ this.drawRTCurve(
+ this.rotatePoints(
+ this.translatePoints(
+ this.curve.points
+ )
+ )
+ );
+}
+
+translatePoints(points) {
// translate to (0,0)
- let points = this.curve.points;
let m = points[0];
- points = points.map(v => {
+ return points.map(v => {
return {
x: v.x - m.x,
y: v.y - m.y
}
});
+}
+rotatePoints(points) {
// rotate so that last point is (...,0)
let dx = points[3].x;
let dy = points[3].y;
let a = atan2(dy, dx);
- points = points.map(v => {
+ return points.map(v => {
return {
x: v.x * cos(-a) - v.y * sin(-a),
y: v.x * sin(-a) + v.y * cos(-a)
};
});
+}
+drawRTCurve(points) {
let ncurve = new Bezier(this, points);
translate(60, this.height/2);
setStroke(`grey`);
diff --git a/chapters/aligning/handler.js b/chapters/aligning/handler.js
deleted file mode 100644
index cd5f252c..00000000
--- a/chapters/aligning/handler.js
+++ /dev/null
@@ -1,72 +0,0 @@
-module.exports = {
- /**
- * Setup function for a default quadratic curve.
- */
- setupQuadratic: function(api) {
- var curve = api.getDefaultQuadratic();
- api.setCurve(curve);
- },
-
- /**
- * Setup function for a default cubic curve.
- */
- setupCubic: function(api) {
- var curve = api.getDefaultCubic();
- api.setCurve(curve);
- },
-
- /**
- * A coordinate rotation function that rotates and
- * translates the curve, such that the first coordinate
- * of the curve is (0,0) and the last coordinate is (..., 0)
- */
- align: function(points, line) {
- var tx = line.p1.x,
- ty = line.p1.y,
- // The atan2 function is so important to computing
- // that most CPUs have a dedicated implementation
- // at the hardware level for it.
- a = -Math.atan2(line.p2.y-ty, line.p2.x-tx),
- cos = Math.cos,
- sin = Math.sin,
- d = function(v) {
- return {
- x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a),
- y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a)
- };
- };
- return points.map(d);
- },
-
- /**
- * Draw a curve and its aligned counterpart
- * side by side across two panels.
- */
- draw: function(api, curve) {
- api.setPanelCount(2);
- api.reset();
- api.drawSkeleton(curve);
- api.drawCurve(curve);
-
- var pts = curve.points;
- var line = {p1: pts[0], p2: pts[pts.length-1]};
- var apts = this.align(pts, line);
- var aligned = new api.Bezier(apts);
- var w = api.getPanelWidth();
- var h = api.getPanelHeight();
-
- var offset = {x:w, y:0};
- api.setColor("black");
- api.drawLine({x:0,y:0}, {x:0,y:h}, offset);
- offset.x += w/4;
- offset.y += h/2;
- api.setColor("grey");
- api.drawLine({x:0,y:-h/2}, {x:0,y:h/2}, offset);
- api.drawLine({x:-w/4,y:0}, {x:w,y:0}, offset);
- api.setFill("grey");
-
- api.setColor("black");
- api.drawSkeleton(aligned, offset);
- api.drawCurve(aligned, offset);
- }
-};
diff --git a/chapters/aligning/quadratic.js b/chapters/aligning/quadratic.js
index bc653ba3..0b4953fd 100644
--- a/chapters/aligning/quadratic.js
+++ b/chapters/aligning/quadratic.js
@@ -13,27 +13,40 @@ draw() {
translate(this.width/2, 0);
line(0,0,0,this.height);
+ this.drawRTCurve(
+ this.rotatePoints(
+ this.translatePoints(
+ this.curve.points
+ )
+ )
+ );
+}
+
+translatePoints(points) {
// translate to (0,0)
- let points = this.curve.points;
let m = points[0];
- points = points.map(v => {
+ return points.map(v => {
return {
x: v.x - m.x,
y: v.y - m.y
}
});
+}
+rotatePoints(points) {
// rotate so that last point is (...,0)
let dx = points[2].x;
let dy = points[2].y;
let a = atan2(dy, dx);
- points = points.map(v => {
+ return points.map(v => {
return {
x: v.x * cos(-a) - v.y * sin(-a),
y: v.x * sin(-a) + v.y * cos(-a)
};
});
+}
+drawRTCurve(points) {
let ncurve = new Bezier(this, points);
translate(10, this.height/2);
setStroke(`grey`);
diff --git a/chapters/boundingbox/cubic.js b/chapters/boundingbox/cubic.js
index 9b5728d8..e67c98a3 100644
--- a/chapters/boundingbox/cubic.js
+++ b/chapters/boundingbox/cubic.js
@@ -28,7 +28,7 @@ draw() {
if (p.x > maxx) maxx = p.x;
if (p.y < miny) miny = p.y;
if (p.y > maxy) maxy = p.y;
- circle(p.x, p.y, 3);
+ if (t > 0 && t< 1) circle(p.x, p.y, 3);
});
setStroke(`#0F0`);
diff --git a/chapters/boundingbox/quadratic.js b/chapters/boundingbox/quadratic.js
index 7c7d7c6b..3f4e1e84 100644
--- a/chapters/boundingbox/quadratic.js
+++ b/chapters/boundingbox/quadratic.js
@@ -27,7 +27,7 @@ draw() {
if (p.x > maxx) maxx = p.x;
if (p.y < miny) miny = p.y;
if (p.y > maxy) maxy = p.y;
- circle(p.x, p.y, 3);
+ if (t > 0 && t< 1) circle(p.x, p.y, 3);
});
setStroke(`#0F0`);
diff --git a/chapters/tightbounds/content.en-GB.md b/chapters/tightbounds/content.en-GB.md
index 2f68ff54..be8c8935 100644
--- a/chapters/tightbounds/content.en-GB.md
+++ b/chapters/tightbounds/content.en-GB.md
@@ -1,8 +1,10 @@
-# Tight boxes
+# Tight bounding boxes
-With our knowledge of bounding boxes, and curve alignment, We can now form the "tight" bounding box for curves. We first align our curve, recording the translation we performed, "T", and the rotation angle we used, "R". We then determine the aligned curve's normal bounding box. Once we have that, we can map that bounding box back to our original curve by rotating it by -R, and then translating it by -T. We now have nice tight bounding boxes for our curves:
+With our knowledge of bounding boxes, and curve alignment, We can now form the "tight" bounding box for curves. We first align our curve, recording the translation we performed, "T", and the rotation angle we used, "R". We then determine the aligned curve's normal bounding box. Once we have that, we can map that bounding box back to our original curve by rotating it by -R, and then translating it by -T.
-
-
+We now have nice tight bounding boxes for our curves:
-These are, strictly speaking, not necessarily the tightest possible bounding boxes. It is possible to compute the optimal bounding box by determining which spanning lines we need to effect a minimal box area, but because of the parametric nature of Bézier curves this is actually a rather costly operation, and the gain in bounding precision is often not worth it. If there is high demand for it, I'll add a section on how to precisely compute the best fit bounding box, but the maths is fairly grueling and just not really worth spending time on.
+
+
+
+These are, strictly speaking, not necessarily the tightest possible bounding boxes. It is possible to compute the optimal bounding box by determining which spanning lines we need to effect a minimal box area, but because of the parametric nature of Bézier curves this is actually a rather costly operation, and the gain in bounding precision is often not worth it.
diff --git a/chapters/tightbounds/cubic.js b/chapters/tightbounds/cubic.js
new file mode 100644
index 00000000..65fcae85
--- /dev/null
+++ b/chapters/tightbounds/cubic.js
@@ -0,0 +1,79 @@
+setup() {
+ const curve = this.curve = Bezier.defaultCubic(this);
+ curve.points[2].x = 210;
+ setMovable(curve.points);
+}
+
+draw() {
+ const curve = this.curve;
+
+ clear();
+ curve.drawSkeleton();
+ curve.drawCurve();
+ curve.drawPoints();
+
+ let translated = this.translatePoints(curve.points);
+ let rotated = this.rotatePoints(translated);
+ let rtcurve = new Bezier(this, rotated);
+ let extrema = rtcurve.extrema();
+
+ let minx = Number.MAX_SAFE_INTEGER,
+ miny = minx,
+ maxx = Number.MIN_SAFE_INTEGER,
+ maxy = maxx;
+
+ setStroke(`red`);
+
+ [0, ...extrema.x, ...extrema.y, 1].forEach(t => {
+ let p = curve.get(t);
+ let rtp = rtcurve.get(t);
+ if (rtp.x < minx) minx = rtp.x;
+ if (rtp.x > maxx) maxx = rtp.x;
+ if (rtp.y < miny) miny = rtp.y;
+ if (rtp.y > maxy) maxy = rtp.y;
+ if (t > 0 && t< 1) circle(p.x, p.y, 3);
+ });
+
+ noFill();
+ setStroke(`#0F0`);
+
+ let tx = curve.points[0].x;
+ let ty = curve.points[0].y;
+ let a = rotated[0].a;
+
+ start();
+ vertex(tx + minx * cos(a) - miny * sin(a), ty + minx * sin(a) + miny * cos(a));
+ vertex(tx + maxx * cos(a) - miny * sin(a), ty + maxx * sin(a) + miny * cos(a));
+ vertex(tx + maxx * cos(a) - maxy * sin(a), ty + maxx * sin(a) + maxy * cos(a));
+ vertex(tx + minx * cos(a) - maxy * sin(a), ty + minx * sin(a) + maxy * cos(a));
+ end(true);
+}
+
+translatePoints(points) {
+ // translate to (0,0)
+ let m = points[0];
+ return points.map(v => {
+ return {
+ x: v.x - m.x,
+ y: v.y - m.y
+ }
+ });
+}
+
+rotatePoints(points) {
+ // rotate so that last point is (...,0)
+ let dx = points[3].x;
+ let dy = points[3].y;
+ let a = atan2(dy, dx);
+ return points.map(v => {
+ return {
+ a: a,
+ x: v.x * cos(-a) - v.y * sin(-a),
+ y: v.x * sin(-a) + v.y * cos(-a)
+ };
+ });
+}
+
+onMouseMove() {
+ redraw();
+}
diff --git a/chapters/tightbounds/handler.js b/chapters/tightbounds/handler.js
deleted file mode 100644
index 7907ea84..00000000
--- a/chapters/tightbounds/handler.js
+++ /dev/null
@@ -1,71 +0,0 @@
-module.exports = {
- setupQuadratic: function(api) {
- var curve = api.getDefaultQuadratic();
- api.setCurve(curve);
- },
-
- setupCubic: function(api) {
- var curve = api.getDefaultCubic();
- api.setCurve(curve);
- },
-
- align: function(points, line) {
- var tx = line.p1.x,
- ty = line.p1.y,
- a = -Math.atan2(line.p2.y-ty, line.p2.x-tx),
- cos = Math.cos,
- sin = Math.sin,
- d = function(v) {
- return {
- x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a),
- y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a),
- a: a
- };
- };
- return points.map(d);
- },
-
- // FIXME: I'm not satisfied with needing to turn a bbox[] into a point[],
- // this needs a bezier.js solution, really, with a call curve.tightbbox()
- transpose: function(points, angle, offset) {
- var tx = offset.x,
- ty = offset.y,
- cos = Math.cos,
- sin = Math.sin,
- v = [points.x.min, points.y.min, points.x.max, points.y.max];
- return [
- {x: v[0], y: v[1] },
- {x: v[2], y: v[1] },
- {x: v[2], y: v[3] },
- {x: v[0], y: v[3] }
- ].map(p => {
- var x=p.x, y=p.y;
- return {
- x: x*cos(angle) - y*sin(angle) + tx,
- y: x*sin(angle) + y*cos(angle) + ty
- };
- });
- },
-
- draw: function(api, curve) {
- api.reset();
-
- var pts = curve.points;
- var line = {p1: pts[0], p2: pts[pts.length-1]};
- var apts = this.align(pts, line);
- var angle = -apts[0].a;
- var aligned = new api.Bezier(apts);
- var bbox = aligned.bbox();
- var tpts = this.transpose(bbox, angle, pts[0]);
-
- api.setColor("#00FF00");
- api.drawLine(tpts[0], tpts[1]);
- api.drawLine(tpts[1], tpts[2]);
- api.drawLine(tpts[2], tpts[3]);
- api.drawLine(tpts[3], tpts[0]);
-
- api.setColor("black");
- api.drawSkeleton(curve);
- api.drawCurve(curve);
- }
-};
diff --git a/chapters/tightbounds/quadratic.js b/chapters/tightbounds/quadratic.js
new file mode 100644
index 00000000..31137b81
--- /dev/null
+++ b/chapters/tightbounds/quadratic.js
@@ -0,0 +1,78 @@
+setup() {
+ this.curve = Bezier.defaultQuadratic(this);
+ setMovable(this.curve.points);
+}
+
+draw() {
+ const curve = this.curve;
+
+ clear();
+ curve.drawSkeleton();
+ curve.drawCurve();
+ curve.drawPoints();
+
+ let translated = this.translatePoints(curve.points);
+ let rotated = this.rotatePoints(translated);
+ let rtcurve = new Bezier(this, rotated);
+ let extrema = rtcurve.extrema();
+
+ let minx = Number.MAX_SAFE_INTEGER,
+ miny = minx,
+ maxx = Number.MIN_SAFE_INTEGER,
+ maxy = maxx;
+
+ setStroke(`red`);
+
+ [0, ...extrema.x, ...extrema.y, 1].forEach(t => {
+ let p = curve.get(t);
+ let rtp = rtcurve.get(t);
+ if (rtp.x < minx) minx = rtp.x;
+ if (rtp.x > maxx) maxx = rtp.x;
+ if (rtp.y < miny) miny = rtp.y;
+ if (rtp.y > maxy) maxy = rtp.y;
+ if (t > 0 && t< 1) circle(p.x, p.y, 3);
+ });
+
+ noFill();
+ setStroke(`#0F0`);
+
+ let tx = curve.points[0].x;
+ let ty = curve.points[0].y;
+ let a = rotated[0].a;
+
+ start();
+ vertex(tx + minx * cos(a) - miny * sin(a), ty + minx * sin(a) + miny * cos(a));
+ vertex(tx + maxx * cos(a) - miny * sin(a), ty + maxx * sin(a) + miny * cos(a));
+ vertex(tx + maxx * cos(a) - maxy * sin(a), ty + maxx * sin(a) + maxy * cos(a));
+ vertex(tx + minx * cos(a) - maxy * sin(a), ty + minx * sin(a) + maxy * cos(a));
+ end(true);
+}
+
+translatePoints(points) {
+ // translate to (0,0)
+ let m = points[0];
+ return points.map(v => {
+ return {
+ x: v.x - m.x,
+ y: v.y - m.y
+ }
+ });
+}
+
+rotatePoints(points) {
+ // rotate so that last point is (...,0)
+ let dx = points[2].x;
+ let dy = points[2].y;
+ let a = atan2(dy, dx);
+ return points.map(v => {
+ return {
+ a: a,
+ x: v.x * cos(-a) - v.y * sin(-a),
+ y: v.x * sin(-a) + v.y * cos(-a)
+ };
+ });
+}
+
+onMouseMove() {
+ redraw();
+}
diff --git a/images/chapters/aligning/b93cd2d19303c6bcd78b130387cd678a.png b/images/chapters/aligning/4da87bfcb036722be99f4b1166d5daeb.png
similarity index 100%
rename from images/chapters/aligning/b93cd2d19303c6bcd78b130387cd678a.png
rename to images/chapters/aligning/4da87bfcb036722be99f4b1166d5daeb.png
diff --git a/images/chapters/aligning/be2c5ccf3d1136da7f04d39f63e4b94b.png b/images/chapters/aligning/54238957a61113fae905188b9eb1a582.png
similarity index 100%
rename from images/chapters/aligning/be2c5ccf3d1136da7f04d39f63e4b94b.png
rename to images/chapters/aligning/54238957a61113fae905188b9eb1a582.png
diff --git a/images/chapters/boundingbox/168229f33086b9919756f4a062ff00bd.png b/images/chapters/boundingbox/168229f33086b9919756f4a062ff00bd.png
new file mode 100644
index 00000000..023fec5e
Binary files /dev/null and b/images/chapters/boundingbox/168229f33086b9919756f4a062ff00bd.png differ
diff --git a/images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png b/images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png
new file mode 100644
index 00000000..ba413e3a
Binary files /dev/null and b/images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png differ
diff --git a/images/chapters/boundingbox/7d6e04cb038bc7d8e5cfa28beee46ae5.png b/images/chapters/boundingbox/7d6e04cb038bc7d8e5cfa28beee46ae5.png
deleted file mode 100644
index acd205e6..00000000
Binary files a/images/chapters/boundingbox/7d6e04cb038bc7d8e5cfa28beee46ae5.png and /dev/null differ
diff --git a/images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png b/images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png
deleted file mode 100644
index d97508ae..00000000
Binary files a/images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png and /dev/null differ
diff --git a/images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png b/images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png
new file mode 100644
index 00000000..f5dd67d2
Binary files /dev/null and b/images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png differ
diff --git a/images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png b/images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png
new file mode 100644
index 00000000..b8d774c4
Binary files /dev/null and b/images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png differ
diff --git a/index.html b/index.html
index 93b90df0..5ee3fedf 100644
--- a/index.html
+++ b/index.html
@@ -111,7 +111,7 @@
Finding extremities: root finding
Bounding boxes
Aligning curves
- Tight boxes
+ Tight bounding boxes
Curve inflections
Canonical form (for cubic curves)
Finding Y, given X
@@ -3356,7 +3356,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3372,7 +3372,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3457,7 +3457,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3473,7 +3473,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3481,7 +3481,7 @@ function getCubicRoots(pa, pb, pc, pd) {
>
-
+
With our knowledge of bounding boxes, and curve alignment, We can
now form the "tight" bounding box for curves. We first align our
@@ -3489,18 +3489,41 @@ function getCubicRoots(pa, pb, pc, pd) {
angle we used, "R". We then determine the aligned curve's normal
bounding box. Once we have that, we can map that bounding box back
to our original curve by rotating it by -R, and then translating it
- by -T. We now have nice tight bounding boxes for our curves:
+ by -T.
- We now have nice tight bounding boxes for our curves:
+
-
+
+
+ Scripts are disabled. Showing fallback image.
+
+
+ width="275"
+ height="275"
+ src="./chapters/tightbounds/cubic.js"
+ >
+
+
+ Scripts are disabled. Showing fallback image.
+
These are, strictly speaking, not necessarily the tightest possible
@@ -3508,10 +3531,7 @@ function getCubicRoots(pa, pb, pc, pd) {
by determining which spanning lines we need to effect a minimal box
area, but because of the parametric nature of Bézier curves this is
actually a rather costly operation, and the gain in bounding
- precision is often not worth it. If there is high demand for it,
- I'll add a section on how to precisely compute the best fit bounding
- box, but the maths is fairly grueling and just not really worth
- spending time on.
+ precision is often not worth it.
diff --git a/ja-JP/index.html b/ja-JP/index.html
index c7e838a9..89828154 100644
--- a/ja-JP/index.html
+++ b/ja-JP/index.html
@@ -143,7 +143,9 @@
Bounding boxes
Aligning curves
- Tight boxes
+
+ Tight bounding boxes
+
Curve inflections
Scripts are disabled. Showing fallback image.
@@ -3042,7 +3044,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3127,7 +3129,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3143,7 +3145,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3151,7 +3153,9 @@ function getCubicRoots(pa, pb, pc, pd) {
>
-
+
With our knowledge of bounding boxes, and curve alignment, We can
now form the "tight" bounding box for curves. We first align our
@@ -3159,18 +3163,41 @@ function getCubicRoots(pa, pb, pc, pd) {
angle we used, "R". We then determine the aligned curve's normal
bounding box. Once we have that, we can map that bounding box back
to our original curve by rotating it by -R, and then translating it
- by -T. We now have nice tight bounding boxes for our curves:
+ by -T.
- We now have nice tight bounding boxes for our curves:
+
-
+
+
+ Scripts are disabled. Showing fallback image.
+
+
+ width="275"
+ height="275"
+ src="./chapters/tightbounds/cubic.js"
+ >
+
+
+ Scripts are disabled. Showing fallback image.
+
These are, strictly speaking, not necessarily the tightest possible
@@ -3178,10 +3205,7 @@ function getCubicRoots(pa, pb, pc, pd) {
by determining which spanning lines we need to effect a minimal box
area, but because of the parametric nature of Bézier curves this is
actually a rather costly operation, and the gain in bounding
- precision is often not worth it. If there is high demand for it,
- I'll add a section on how to precisely compute the best fit bounding
- box, but the maths is fairly grueling and just not really worth
- spending time on.
+ precision is often not worth it.
diff --git a/lib/custom-element/api/graphics-api.js b/lib/custom-element/api/graphics-api.js
index 9cc52510..b48a8603 100644
--- a/lib/custom-element/api/graphics-api.js
+++ b/lib/custom-element/api/graphics-api.js
@@ -386,13 +386,14 @@ class GraphicsAPI extends BaseAPI {
/**
* A signal to draw the current complex shape
*/
- end() {
+ end(close = false) {
this.ctx.beginPath();
let { x, y } = this.currentShape.first;
this.ctx.moveTo(x, y);
this.currentShape.segments.forEach((s) =>
this[`draw${s.type}`](s.points, s.factor)
);
+ if (close) this.ctx.closePath();
this.ctx.fill();
this.ctx.stroke();
}
diff --git a/zh-CN/index.html b/zh-CN/index.html
index 69029399..cae8202f 100644
--- a/zh-CN/index.html
+++ b/zh-CN/index.html
@@ -137,7 +137,9 @@
Bounding boxes
Aligning curves
- Tight boxes
+
+ Tight bounding boxes
+
Curve inflections
Scripts are disabled. Showing fallback image.
@@ -3052,7 +3054,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3137,7 +3139,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3153,7 +3155,7 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
@@ -3161,7 +3163,9 @@ function getCubicRoots(pa, pb, pc, pd) {
>
-
+
With our knowledge of bounding boxes, and curve alignment, We can
now form the "tight" bounding box for curves. We first align our
@@ -3169,18 +3173,41 @@ function getCubicRoots(pa, pb, pc, pd) {
angle we used, "R". We then determine the aligned curve's normal
bounding box. Once we have that, we can map that bounding box back
to our original curve by rotating it by -R, and then translating it
- by -T. We now have nice tight bounding boxes for our curves:
+ by -T.
- We now have nice tight bounding boxes for our curves:
+
-
+
+
+ Scripts are disabled. Showing fallback image.
+
+
+ width="275"
+ height="275"
+ src="./chapters/tightbounds/cubic.js"
+ >
+
+
+ Scripts are disabled. Showing fallback image.
+
These are, strictly speaking, not necessarily the tightest possible
@@ -3188,10 +3215,7 @@ function getCubicRoots(pa, pb, pc, pd) {
by determining which spanning lines we need to effect a minimal box
area, but because of the parametric nature of Bézier curves this is
actually a rather costly operation, and the gain in bounding
- precision is often not worth it. If there is high demand for it,
- I'll add a section on how to precisely compute the best fit bounding
- box, but the maths is fairly grueling and just not really worth
- spending time on.
+ precision is often not worth it.