From ddb7a8f063c346d025528f7e5b57c66049c1a69c Mon Sep 17 00:00:00 2001 From: Pomax Date: Wed, 19 Aug 2020 16:08:36 -0700 Subject: [PATCH] aligning + tight bounds --- chapters/aligning/content.en-GB.md | 2 +- chapters/aligning/cubic.js | 19 ++++- chapters/aligning/handler.js | 72 ---------------- chapters/aligning/quadratic.js | 19 ++++- chapters/boundingbox/cubic.js | 2 +- chapters/boundingbox/quadratic.js | 2 +- chapters/tightbounds/content.en-GB.md | 12 +-- chapters/tightbounds/cubic.js | 79 ++++++++++++++++++ chapters/tightbounds/handler.js | 71 ---------------- chapters/tightbounds/quadratic.js | 78 +++++++++++++++++ ...g => 4da87bfcb036722be99f4b1166d5daeb.png} | Bin ...g => 54238957a61113fae905188b9eb1a582.png} | Bin .../168229f33086b9919756f4a062ff00bd.png | Bin 0 -> 10001 bytes .../1f0e2a574995607c61adf48eded66458.png | Bin 0 -> 8681 bytes .../7d6e04cb038bc7d8e5cfa28beee46ae5.png | Bin 8723 -> 0 bytes .../a5595ac771a586fee13b0bf1fd2f4e49.png | Bin 10032 -> 0 bytes .../40061dae02a2f78fdaaedd37289dc38a.png | Bin 0 -> 11736 bytes .../95d28739a00d2c9c0c4bbc3c93ce3e05.png | Bin 0 -> 11314 bytes index.html | 58 ++++++++----- ja-JP/index.html | 62 +++++++++----- lib/custom-element/api/graphics-api.js | 3 +- zh-CN/index.html | 62 +++++++++----- 22 files changed, 326 insertions(+), 215 deletions(-) delete mode 100644 chapters/aligning/handler.js create mode 100644 chapters/tightbounds/cubic.js delete mode 100644 chapters/tightbounds/handler.js create mode 100644 chapters/tightbounds/quadratic.js rename images/chapters/aligning/{b93cd2d19303c6bcd78b130387cd678a.png => 4da87bfcb036722be99f4b1166d5daeb.png} (100%) rename images/chapters/aligning/{be2c5ccf3d1136da7f04d39f63e4b94b.png => 54238957a61113fae905188b9eb1a582.png} (100%) create mode 100644 images/chapters/boundingbox/168229f33086b9919756f4a062ff00bd.png create mode 100644 images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png delete mode 100644 images/chapters/boundingbox/7d6e04cb038bc7d8e5cfa28beee46ae5.png delete mode 100644 images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png create mode 100644 images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png create mode 100644 images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png 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 0000000000000000000000000000000000000000..023fec5eddbaa5ba4022a15e5bc384aacc24bb14 GIT binary patch literal 10001 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 JLMfvc{|jjlcgg?& literal 0 HcmV?d00001 diff --git a/images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png b/images/chapters/boundingbox/1f0e2a574995607c61adf48eded66458.png new file mode 100644 index 0000000000000000000000000000000000000000..ba413e3a452c38c602f6371b3880542a1c8a83fd GIT binary patch literal 8681 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 diff --git a/images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png b/images/chapters/boundingbox/a5595ac771a586fee13b0bf1fd2f4e49.png deleted file mode 100644 index d97508ae592585fff778b60df7ffa091c254ff02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10032 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 diff --git a/images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png b/images/chapters/tightbounds/40061dae02a2f78fdaaedd37289dc38a.png new file mode 100644 index 0000000000000000000000000000000000000000..f5dd67d2790cb63c448ce84b50b917c095f1fed6 GIT binary patch literal 11736 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 literal 0 HcmV?d00001 diff --git a/images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png b/images/chapters/tightbounds/95d28739a00d2c9c0c4bbc3c93ce3e05.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d774c4fb43435cb0396848fbce0b2ed7f4881c GIT binary patch literal 11314 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 z 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 bdkxdGZLFinding 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) { >
    -

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

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

    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 @@ -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.