From 10148f46b10a67df93db0beb6e61f33ecb0dce2d Mon Sep 17 00:00:00 2001 From: Pomax Date: Sat, 29 Aug 2020 15:29:49 -0700 Subject: [PATCH] abc --- docs/chapters/abc/abc.js | 71 +++++++ docs/chapters/abc/content.en-GB.md | 75 ++++---- docs/chapters/abc/handler.js | 173 ------------------ .../abc/059000c5c8a37dcc8d7fa04154a05df3.svg | 2 +- .../abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg | 2 +- .../abc/34fe255294faf45ab02128f7997b92ce.svg | 1 - .../abc/385d1fd4aecbd2066e6e284a84408be6.svg | 2 +- .../abc/5484dc53e408a4259891a65212ef8636.svg | 1 + .../abc/62f2f984e43a22a6b4bda4d399dedfc6.svg | 1 - .../abc/63fbe4e666a7dad985ec4110e17c249f.svg | 1 + .../abc/6e40975c21e70b73954a4dce02b9ba75.png | Bin 0 -> 13785 bytes .../abc/b4987e9b77b0df604238b88596c5f7c3.svg | 2 +- .../abc/cd2e47cdc2e23ec86cd1ca1cb4286645.svg | 1 + .../abc/d744a4955a3ff4e2d85760887ea923d4.png | Bin 0 -> 15853 bytes docs/index.html | 76 ++++---- docs/ja-JP/index.html | 76 ++++---- docs/js/custom-element/api/types/bezier.js | 5 +- docs/zh-CN/index.html | 76 ++++---- 18 files changed, 229 insertions(+), 336 deletions(-) create mode 100644 docs/chapters/abc/abc.js delete mode 100644 docs/chapters/abc/handler.js delete mode 100644 docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg create mode 100644 docs/images/chapters/abc/5484dc53e408a4259891a65212ef8636.svg delete mode 100644 docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg create mode 100644 docs/images/chapters/abc/63fbe4e666a7dad985ec4110e17c249f.svg create mode 100644 docs/images/chapters/abc/6e40975c21e70b73954a4dce02b9ba75.png create mode 100644 docs/images/chapters/abc/cd2e47cdc2e23ec86cd1ca1cb4286645.svg create mode 100644 docs/images/chapters/abc/d744a4955a3ff4e2d85760887ea923d4.png diff --git a/docs/chapters/abc/abc.js b/docs/chapters/abc/abc.js new file mode 100644 index 00000000..c811c8f8 --- /dev/null +++ b/docs/chapters/abc/abc.js @@ -0,0 +1,71 @@ +let curve, utils = Bezier.getUtils(); + +setup() { + const type = this.parameters.type ?? `quadratic`; + curve = (type === `quadratic`) ? Bezier.defaultQuadratic(this) : Bezier.defaultCubic(this); + curve.points.forEach(p => p.y -= 20); + setMovable(curve.points); + setSlider(`.slide-control`, `position`, 0.5); +} + + +/** + * The master draw function for the `projection` sketches + */ +draw() { + clear(); + curve.drawSkeleton(); + curve.drawCurve(); + curve.drawPoints(); + + const t = this.position; + const p = curve.get(t); + + setStroke(`black`); + circle(p.x, p.y, 2); + + // find the A/B/C values as described in the section text + const hull = curve.drawStruts(t, `lightblue`); + let A, B, C; + + setStroke(`lightgrey`); + if(hull.length === 6) { + A = curve.points[1]; + B = hull[5]; + let p1 = curve.points[0]; + let p2 = curve.points[2]; + C = utils.lli4(A, B, p1, p2); + line(p1.x, p1.y, p2.x, p2.y); + } else if(hull.length === 10) { + A = hull[5]; + B = hull[9]; + let p1 = curve.points[0]; + let p2 = curve.points[3]; + C = utils.lli4(A, B, p1, p2); + line(p1.x, p1.y, p2.x, p2.y); + } + + this.drawABCdata(t, A, B, C); +} + +drawABCdata(t, A, B, C) { + // show the lines between the A/B/C values + setStroke(`#00FF00`); + line(A.x, A.y, B.x, B.y); + setStroke(`red`); + line(B.x, B.y, C.x, C.y); + setStroke(`black`); + circle(C.x, C.y, 3); + + // with their associated labels + setFill(`black`); + text(`A`, 10 + A.x, A.y); + text(`B (t = ${t.toFixed(2)})`, 10 + B.x, B.y); + text(`C`, 10 + C.x, C.y); + + // and show the distance ratio, which we see does not change irrespective of whether A/B/C change. + const d1 = dist(A.x, A.y, B.x, B.y); + const d2 = dist(B.x, B.y, C.x, C.y); + const ratio = d1/d2; + text(`d1 = A-B: ${d1.toFixed(2)}, d2 = B-C: ${d2.toFixed(2)}, d1/d2: ${ratio.toFixed(4)}`, 10, this.height-7); +} diff --git a/docs/chapters/abc/content.en-GB.md b/docs/chapters/abc/content.en-GB.md index 04bb7667..73170fea 100644 --- a/docs/chapters/abc/content.en-GB.md +++ b/docs/chapters/abc/content.en-GB.md @@ -6,78 +6,69 @@ How does that work? Succinctly: we run de Casteljau's algorithm in reverse! In order to run de Casteljau's algorithm in reverse, we need a few basic things: a start and end point, a point on the curve that want to be moving around, which has an associated *t* value, and a point we've not explicitly talked about before, and as far as I know has no explicit name, but lives one iteration higher in the de Casteljau process then our on-curve point does. I like to call it "A" for reasons that will become obvious. -So let's use graphics instead of text to see where this "A" is, because text only gets us so far: in the following graphic, click anywhere on the curves to see the identity information that we'll be using to run de Casteljau in reverse (you can manipulate the curve even after picking a point. Note the "ratio" value when you do so: does it change?): +So let's use graphics instead of text to see where this "A" is, because text only gets us so far: move the sliders for the following graphics to see what, given specific `t` value, our `A` coordinate is. As well as some other coordinates, which taken together let us derive a value that the graphics call "ratio": if you move the curve's points around, A, B, and C will move, what happens to that value?
- - + + + + + + + +
-Clicking anywhere on the curves shows us three things: +So these graphics show us several things: -1. our on-curve point; let's call that B, -2. a point at the tip of B's "hat", on de Casteljau step up; let's call that A, and -3. a point that we get by projecting B onto the start--end baseline; let's call that C. +1. a point at the tip of the curve construction's "hat": let's call that `A`, as well as +2. our on-curve point give our chosen `t` value: let's call that `B`, and finally, +3. a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that `C`. -These three values A, B, and C hide an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some *t* value, the ratio distance of C along the baseline is fixed: if some *t* value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that *t* value, C will *always* lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change. The following function stays true: +These three values A, B, and C allow us to derive an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some `t` value, the ratio of distances from A to B and B to C is fixed: if some `t` value sets up a C that is 20% away from the start and 80% away from the end, then _it doesn't matter where the start, end, or control points are_; for that `t` value, `C` will *always* lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change. + +So, how can we compute `C`? We start with our observation that `C` always lies somewhere between the start and ends points, so logically `C` will have a function that interpolates between those two coordinates: \[ - C = u \cdot P_{start} + (1-u) \cdot P_{end} + C = u(t) \cdot P_{start} + (1-u(t)) \cdot P_{end} \] -So that just leaves finding A. - -
- -While that relation is fixed, the function *u(t)* differs depending on whether we're working -with quadratic or cubic curves: +If we can figure out what the function `u(t)` looks like, we'll be done. Although we do need to remember that this `u(t)` will have a different for depending on whether we're working with quadratic or cubic curves. [Running through the maths](http://mathoverflow.net/questions/122257/finding-the-formula-for-Bézier-curve-ratios-hull-point-point-baseline) (with thanks to Boris Zbarsky) shows us the following two formulae: \[ -\begin{aligned} -& u(t)_{quadratic} &= \frac{(1-t)^2}{t^2 + (1-t)^2} \\ -& u(t)_{cubic} &= \frac{(1-t)^3}{t^3 + (1-t)^3} -\end{aligned} + u(t)_{quadratic} = \frac{(1-t)^2}{t^2 + (1-t)^2} \] -So, if we know the start and end coordinates, and we know the *t* value, we know C: +And -
- - -
+\[ + u(t)_{cubic} = \frac{(1-t)^3}{t^3 + (1-t)^3} +\] -Mouse-over the graphs to see the expression for C, given the *t* value at the mouse pointer. +So, if we know the start and end coordinates, and we know the *t* value, we know C, without having to calculate the `A` or even `B` coordinates. In fact, we can do the same for the ratio function: as another function of `t`, we technically don't need to know what `A` or `B` or `C` are, we can express it was a pure function of `t`, too. -
- -There's also another important bit of information that is inherent to the ABC values: while the distances between A and B, and B and C, are dynamic (based on where we put B), the *ratio* between the two distances is stable. Given some *t* value, the following always holds: +We start by observing that, given `A`, `B`, and `C`, the following always holds: \[ ratio(t) = \frac{distance(B,C)}{distance(A,B)} = Constant \] -This leads to a pretty powerful bit of knowledge: merely by knowing the *t* value of some on curve point, we know where C has to be (as per the above note), and because we know B and C, and thus have the distance between them, we know where A has to be: - -\[ - A = B - \frac{C - B}{ratio(t)} = B + \frac{B - C}{ratio(t)} -\] - -And that's it, all values found. - -
- -Much like the *u(t)* function in the above note, the *ratio(t)* function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the *u(t)* function in that they both come rolling out of the same function evaluation, explained over on [MathOverflow](http://mathoverflow.net/questions/122257/finding-the-formula-for-Bézier-curve-ratios-hull-point-point-baseline) by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow. +Working out the maths for this, we see the following two formulae for quadratic and cubic curves: \[ ratio(t)_{quadratic} = \left | \frac{t^2 + (1-t)^2 - 1}{t^2 + (1-t)^2} \right | \] +And + \[ ratio(t)_{cubic} = \left | \frac{t^3 + (1-t)^3 - 1}{t^3 + (1-t)^3} \right | \] -Unfortunately, this trick only works for quadratic and cubic curves. Once we hit higher order curves, things become a lot less predictable; the "fixed point *C*" is no longer fixed, moving around as we move the control points, and projections of *B* onto the line between start and end may actually lie on that line before the start, or after the end, and there are no simple ratios that we can exploit. +Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a `t` value, we can _contruct_ curves: we can compute `C` using the start and end points, and our `u(t)` function, and once we have `C`, we can use our on-curve point (`B`) and the `ratio(t)` function to find `A`: -
+\[ + A = B - \frac{C - B}{ratio(t)} = B + \frac{B - C}{ratio(t)} +\] -So: if we know B and its corresponding *t* value, then we know all the ABC values, which —together with a start and end coordinate— gives us the necessary information to reconstruct a curve's "de Casteljau skeleton", which means that two points and a value between 0 and 1, we can come up with a curve. And that opens up possibilities: curve manipulation by dragging an on-curve point, as well as curve fitting of "a bunch of coordinates". These are useful things, and we'll look at both in the next sections. +So: if we have a curve's start and end point, then for any `t` value, we implicitly know all the ABC values, which gives us the necessary information to reconstruct a curve's "de Casteljau skeleton". Which means that we can now do several things: we can "fit" curves using only three points, which means we can also "mould" curves by moving an on-curve point but leaving its start and end point, and then reconstructing the curve based on where we moved the on-curve point to. These are very useful things, and we'll look at both in the next sections. diff --git a/docs/chapters/abc/handler.js b/docs/chapters/abc/handler.js deleted file mode 100644 index 4b6cc74a..00000000 --- a/docs/chapters/abc/handler.js +++ /dev/null @@ -1,173 +0,0 @@ -module.exports = { - - // ============== first sketch set ===================== - - /** - * The entry point for the quadratic curve example - */ - setupQuadratic: function(api) { - var curve = api.getDefaultQuadratic(); - curve.points[0].y -= 10; - api.setCurve(curve); - }, - - /** - * The entry point for the cubic curve example - */ - setupCubic: function(api) { - var curve = api.getDefaultCubic(); - curve.points[2].y -= 20; - api.setCurve(curve); - api.lut = curve.getLUT(100); - }, - - /** - * When someone clicks a graphic, find the associated - * on-curve t value and redraw with that new knowledge. - */ - onClick: function(evt, api) { - api.t = api.curve.on({x: evt.offsetX, y: evt.offsetY},7); - if (api.t < 0.05 || api.t > 0.95) api.t = false; - api.redraw(); - }, - - /** - * The master draw function for the "projection" sketches - */ - draw: function(api, curve) { - // draw the basic curve and curve control points - api.reset(); - api.drawSkeleton(curve); - api.drawCurve(curve); - - api.setColor("black"); - if (!api.t) return; - - // draw the user-clicked on-curve point - api.drawCircle(api.curve.get(api.t),3); - api.setColor("lightgrey"); - - var utils = api.utils; - - // find the A/B/C values as described in the section text - var hull = api.drawHull(curve, api.t); - var A, B, C; - if(hull.length === 6) { - A = curve.points[1]; - B = hull[5]; - C = utils.lli4(A, B, curve.points[0], curve.points[2]); - api.setColor("lightgrey"); - api.drawLine(curve.points[0], curve.points[2]); - } else if(hull.length === 10) { - A = hull[5]; - B = hull[9]; - C = utils.lli4(A, B, curve.points[0], curve.points[3]); - api.setColor("lightgrey"); - api.drawLine(curve.points[0], curve.points[3]); - } - - // show the lines between the A/B/C values - api.setColor("#00FF00"); - api.drawLine(A,B); - api.setColor("red"); - api.drawLine(B,C); - api.setColor("black"); - api.drawCircle(C,3); - - // with their associated labels - api.setFill("black"); - api.text("A", {x:10 + A.x, y: A.y}); - api.text("B (t = " + api.utils.round(api.t,2) + ")", {x:10 + B.x, y: B.y}); - api.text("C", {x:10 + C.x, y: C.y}); - - // and show the distance ratio, which we see does not change irrespective of whether A/B/C change. - var d1 = utils.dist(A, B); - var d2 = utils.dist(B, C); - var ratio = d1/d2; - var h = api.getPanelHeight(); - api.text("d1 (A-B): " + utils.round(d1,2) + ", d2 (B-C): "+ utils.round(d2,2) + ", ratio (d1/d2): " + utils.round(ratio,4), {x:10, y:h-7}); - }, - - // ============== second sketch set ===================== - - /** - * on mouse move, fix the t value for drawing based on the - * cursor position over the sketch. All the way on the left - * is t=0, all the way on the right is t=1, with a linear - * interpolation for anything in between. - */ - setCT: function(evt,api) { - api.t = evt.offsetX / api.getPanelWidth(); - }, - - /** - * Draw the quadratic C(t) values - */ - drawQCT: function(api) { - api.u = api.u || function(t) { - var top = (t-1) * (t-1), - bottom = 2*t*t - 2*t + 1; - return top/bottom; - }; - this.drawCTgraph(api); - }, - - /** - * Draw the cubic C(t) values - */ - drawCCT: function(api) { - api.u = api.u || function(t) { - var top = (1-t) * (1-t) * (1-t), - bottom = t*t*t + top; - return top/bottom; - }; - this.drawCTgraph(api); - }, - - /** - * Draw a C(t) curve - */ - drawCTgraph: function(api) { - api.reset(); - var w = api.getPanelWidth(); - var pad = 20; - var fwh = w - 2*pad; - - // draw some axes - api.setColor("black"); - api.drawAxes(pad, "t",0,1, "u",0,1); - - // draw the C(t) function using an - // indirection function that takes a - // t value and spits out the C(t) value - // as a point coordinate. - api.setColor("blue"); - var uPoint = function(t) { - var value = api.u(t), - res = { x: pad + t*fwh, y: pad + value*fwh }; - return res; - }; - api.drawFunction(uPoint); - - // if the cursor is (or was ever) over this - // graphic, draw the "crosshair" that pinpoints - // where in the function the associated t/C(t) - // coordinate is. - if (api.t) { - var v = api.u(api.t), - v1 = api.utils.round(v,3), - v2 = api.utils.round(1-v,3), - up = uPoint(api.t); - api.drawLine({x:up.x,y:pad}, up); - api.drawLine({x:pad,y:up.y}, up); - api.drawCircle(up,3); - - // with some handy text that shows the actual computed values - api.setFill("blue"); - api.text(" t = " + api.utils.round(api.t,3), {x:up.x+10, y:up.y-7}); - api.text("u(t) = " + api.utils.round(v,3), {x:up.x+10, y:up.y+7}); - api.setFill("black"); - api.text("C = "+v1+" * start + "+v2+" * end", {x:w/2 - pad, y:pad+fwh}); - } - } -}; diff --git a/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg b/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg index 1e91c9d0..c6effbaf 100644 --- a/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg +++ b/docs/images/chapters/abc/059000c5c8a37dcc8d7fa04154a05df3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg b/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg index c5b329cb..db2106d4 100644 --- a/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg +++ b/docs/images/chapters/abc/12aaf0d7fd20b3c551a0ec76b18bd7d2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg b/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg deleted file mode 100644 index f92f1ae5..00000000 --- a/docs/images/chapters/abc/34fe255294faf45ab02128f7997b92ce.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg b/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg index 88940f03..abd1a624 100644 --- a/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg +++ b/docs/images/chapters/abc/385d1fd4aecbd2066e6e284a84408be6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/5484dc53e408a4259891a65212ef8636.svg b/docs/images/chapters/abc/5484dc53e408a4259891a65212ef8636.svg new file mode 100644 index 00000000..8dd175bd --- /dev/null +++ b/docs/images/chapters/abc/5484dc53e408a4259891a65212ef8636.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg b/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg deleted file mode 100644 index 42e84790..00000000 --- a/docs/images/chapters/abc/62f2f984e43a22a6b4bda4d399dedfc6.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/images/chapters/abc/63fbe4e666a7dad985ec4110e17c249f.svg b/docs/images/chapters/abc/63fbe4e666a7dad985ec4110e17c249f.svg new file mode 100644 index 00000000..7aa03beb --- /dev/null +++ b/docs/images/chapters/abc/63fbe4e666a7dad985ec4110e17c249f.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/abc/6e40975c21e70b73954a4dce02b9ba75.png b/docs/images/chapters/abc/6e40975c21e70b73954a4dce02b9ba75.png new file mode 100644 index 0000000000000000000000000000000000000000..c122d396c0d5981466d2dbcdc10c35ecd1d6267d GIT binary patch literal 13785 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 literal 0 HcmV?d00001 diff --git a/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg b/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg index 9cfd3fb8..eb599048 100644 --- a/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg +++ b/docs/images/chapters/abc/b4987e9b77b0df604238b88596c5f7c3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/images/chapters/abc/cd2e47cdc2e23ec86cd1ca1cb4286645.svg b/docs/images/chapters/abc/cd2e47cdc2e23ec86cd1ca1cb4286645.svg new file mode 100644 index 00000000..9019dd86 --- /dev/null +++ b/docs/images/chapters/abc/cd2e47cdc2e23ec86cd1ca1cb4286645.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/abc/d744a4955a3ff4e2d85760887ea923d4.png b/docs/images/chapters/abc/d744a4955a3ff4e2d85760887ea923d4.png new file mode 100644 index 0000000000000000000000000000000000000000..44b075f1e6838ffaab6129eec998537ee2579533 GIT binary patch literal 15853 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 XRLaDe Casteljau's algorithm is the pivotal algorithm when it comes to Bézier curves. You can use it not just to split curves, but also to draw them efficiently (especially for high-order Bézier curves), as well as to come up with curves based on three points and a tangent. Particularly this last thing is really useful because it lets us "mould" a curve, by picking it up at some point, and dragging that point around to change the curve's shape.

How does that work? Succinctly: we run de Casteljau's algorithm in reverse!

In order to run de Casteljau's algorithm in reverse, we need a few basic things: a start and end point, a point on the curve that want to be moving around, which has an associated t value, and a point we've not explicitly talked about before, and as far as I know has no explicit name, but lives one iteration higher in the de Casteljau process then our on-curve point does. I like to call it "A" for reasons that will become obvious.

-

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: in the following graphic, click anywhere on the curves to see the identity information that we'll be using to run de Casteljau in reverse (you can manipulate the curve even after picking a point. Note the "ratio" value when you do so: does it change?):

+

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: move the sliders for the following graphics to see what, given specific t value, our A coordinate is. As well as some other coordinates, which taken together let us derive a value that the graphics call "ratio": if you move the curve's points around, A, B, and C will move, what happens to that value?

- - + + + + + Scripts are disabled. Showing fallback image. + + + + + + + Scripts are disabled. Showing fallback image. + + + +
-

Clicking anywhere on the curves shows us three things:

+

So these graphics show us several things:

    -
  1. our on-curve point; let's call that B,
  2. -
  3. a point at the tip of B's "hat", on de Casteljau step up; let's call that A, and
  4. -
  5. a point that we get by projecting B onto the start--end baseline; let's call that C.
  6. +
  7. a point at the tip of the curve construction's "hat": let's call that A, as well as
  8. +
  9. our on-curve point give our chosen t value: let's call that B, and finally,
  10. +
  11. a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that C.
-

These three values A, B, and C hide an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio distance of C along the baseline is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change. The following function stays true:

- -

So that just leaves finding A.

-
- -

While that relation is fixed, the function u(t) differs depending on whether we're working -with quadratic or cubic curves:

- -

So, if we know the start and end coordinates, and we know the t value, we know C:

-
- - -
- -

Mouse-over the graphs to see the expression for C, given the t value at the mouse pointer.

-
- -

There's also another important bit of information that is inherent to the ABC values: while the distances between A and B, and B and C, are dynamic (based on where we put B), the ratio between the two distances is stable. Given some t value, the following always holds:

- -

This leads to a pretty powerful bit of knowledge: merely by knowing the t value of some on curve point, we know where C has to be (as per the above note), and because we know B and C, and thus have the distance between them, we know where A has to be:

- -

And that's it, all values found.

-
- -

Much like the u(t) function in the above note, the ratio(t) function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the u(t) function in that they both come rolling out of the same function evaluation, explained over on MathOverflow by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow.

- - -

Unfortunately, this trick only works for quadratic and cubic curves. Once we hit higher order curves, things become a lot less predictable; the "fixed point C" is no longer fixed, moving around as we move the control points, and projections of B onto the line between start and end may actually lie on that line before the start, or after the end, and there are no simple ratios that we can exploit.

-
- -

So: if we know B and its corresponding t value, then we know all the ABC values, which —together with a start and end coordinate— gives us the necessary information to reconstruct a curve's "de Casteljau skeleton", which means that two points and a value between 0 and 1, we can come up with a curve. And that opens up possibilities: curve manipulation by dragging an on-curve point, as well as curve fitting of "a bunch of coordinates". These are useful things, and we'll look at both in the next sections.

+

These three values A, B, and C allow us to derive an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio of distances from A to B and B to C is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change.

+

So, how can we compute C? We start with our observation that C always lies somewhere between the start and ends points, so logically C will have a function that interpolates between those two coordinates:

+ +

If we can figure out what the function u(t) looks like, we'll be done. Although we do need to remember that this u(t) will have a different for depending on whether we're working with quadratic or cubic curves. Running through the maths (with thanks to Boris Zbarsky) shows us the following two formulae:

+ +

And

+ +

So, if we know the start and end coordinates, and we know the t value, we know C, without having to calculate the A or even B coordinates. In fact, we can do the same for the ratio function: as another function of t, we technically don't need to know what A or B or C are, we can express it was a pure function of t, too.

+

We start by observing that, given A, B, and C, the following always holds:

+ +

Working out the maths for this, we see the following two formulae for quadratic and cubic curves:

+ +

And

+ +

Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a t value, we can contruct curves: we can compute C using the start and end points, and our u(t) function, and once we have C, we can use our on-curve point (B) and the ratio(t) function to find A:

+ +

So: if we have a curve's start and end point, then for any t value, we implicitly know all the ABC values, which gives us the necessary information to reconstruct a curve's "de Casteljau skeleton". Which means that we can now do several things: we can "fit" curves using only three points, which means we can also "mould" curves by moving an on-curve point but leaving its start and end point, and then reconstructing the curve based on where we moved the on-curve point to. These are very useful things, and we'll look at both in the next sections.

diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index f36b056b..40c7519c 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -1553,49 +1553,49 @@ lli = function(line1, line2):

De Casteljau's algorithm is the pivotal algorithm when it comes to Bézier curves. You can use it not just to split curves, but also to draw them efficiently (especially for high-order Bézier curves), as well as to come up with curves based on three points and a tangent. Particularly this last thing is really useful because it lets us "mould" a curve, by picking it up at some point, and dragging that point around to change the curve's shape.

How does that work? Succinctly: we run de Casteljau's algorithm in reverse!

In order to run de Casteljau's algorithm in reverse, we need a few basic things: a start and end point, a point on the curve that want to be moving around, which has an associated t value, and a point we've not explicitly talked about before, and as far as I know has no explicit name, but lives one iteration higher in the de Casteljau process then our on-curve point does. I like to call it "A" for reasons that will become obvious.

-

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: in the following graphic, click anywhere on the curves to see the identity information that we'll be using to run de Casteljau in reverse (you can manipulate the curve even after picking a point. Note the "ratio" value when you do so: does it change?):

+

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: move the sliders for the following graphics to see what, given specific t value, our A coordinate is. As well as some other coordinates, which taken together let us derive a value that the graphics call "ratio": if you move the curve's points around, A, B, and C will move, what happens to that value?

- - + + + + + Scripts are disabled. Showing fallback image. + + + + + + + Scripts are disabled. Showing fallback image. + + + +
-

Clicking anywhere on the curves shows us three things:

+

So these graphics show us several things:

    -
  1. our on-curve point; let's call that B,
  2. -
  3. a point at the tip of B's "hat", on de Casteljau step up; let's call that A, and
  4. -
  5. a point that we get by projecting B onto the start--end baseline; let's call that C.
  6. +
  7. a point at the tip of the curve construction's "hat": let's call that A, as well as
  8. +
  9. our on-curve point give our chosen t value: let's call that B, and finally,
  10. +
  11. a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that C.
-

These three values A, B, and C hide an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio distance of C along the baseline is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change. The following function stays true:

- -

So that just leaves finding A.

-
- -

While that relation is fixed, the function u(t) differs depending on whether we're working -with quadratic or cubic curves:

- -

So, if we know the start and end coordinates, and we know the t value, we know C:

-
- - -
- -

Mouse-over the graphs to see the expression for C, given the t value at the mouse pointer.

-
- -

There's also another important bit of information that is inherent to the ABC values: while the distances between A and B, and B and C, are dynamic (based on where we put B), the ratio between the two distances is stable. Given some t value, the following always holds:

- -

This leads to a pretty powerful bit of knowledge: merely by knowing the t value of some on curve point, we know where C has to be (as per the above note), and because we know B and C, and thus have the distance between them, we know where A has to be:

- -

And that's it, all values found.

-
- -

Much like the u(t) function in the above note, the ratio(t) function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the u(t) function in that they both come rolling out of the same function evaluation, explained over on MathOverflow by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow.

- - -

Unfortunately, this trick only works for quadratic and cubic curves. Once we hit higher order curves, things become a lot less predictable; the "fixed point C" is no longer fixed, moving around as we move the control points, and projections of B onto the line between start and end may actually lie on that line before the start, or after the end, and there are no simple ratios that we can exploit.

-
- -

So: if we know B and its corresponding t value, then we know all the ABC values, which —together with a start and end coordinate— gives us the necessary information to reconstruct a curve's "de Casteljau skeleton", which means that two points and a value between 0 and 1, we can come up with a curve. And that opens up possibilities: curve manipulation by dragging an on-curve point, as well as curve fitting of "a bunch of coordinates". These are useful things, and we'll look at both in the next sections.

+

These three values A, B, and C allow us to derive an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio of distances from A to B and B to C is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change.

+

So, how can we compute C? We start with our observation that C always lies somewhere between the start and ends points, so logically C will have a function that interpolates between those two coordinates:

+ +

If we can figure out what the function u(t) looks like, we'll be done. Although we do need to remember that this u(t) will have a different for depending on whether we're working with quadratic or cubic curves. Running through the maths (with thanks to Boris Zbarsky) shows us the following two formulae:

+ +

And

+ +

So, if we know the start and end coordinates, and we know the t value, we know C, without having to calculate the A or even B coordinates. In fact, we can do the same for the ratio function: as another function of t, we technically don't need to know what A or B or C are, we can express it was a pure function of t, too.

+

We start by observing that, given A, B, and C, the following always holds:

+ +

Working out the maths for this, we see the following two formulae for quadratic and cubic curves:

+ +

And

+ +

Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a t value, we can contruct curves: we can compute C using the start and end points, and our u(t) function, and once we have C, we can use our on-curve point (B) and the ratio(t) function to find A:

+ +

So: if we have a curve's start and end point, then for any t value, we implicitly know all the ABC values, which gives us the necessary information to reconstruct a curve's "de Casteljau skeleton". Which means that we can now do several things: we can "fit" curves using only three points, which means we can also "mould" curves by moving an on-curve point but leaving its start and end point, and then reconstructing the curve based on where we moved the on-curve point to. These are very useful things, and we'll look at both in the next sections.

diff --git a/docs/js/custom-element/api/types/bezier.js b/docs/js/custom-element/api/types/bezier.js index a5e0a850..0a4b018e 100644 --- a/docs/js/custom-element/api/types/bezier.js +++ b/docs/js/custom-element/api/types/bezier.js @@ -155,13 +155,14 @@ class Bezier extends Original { return p; } - drawStruts(t) { + drawStruts(t, color = `black`) { const p = t.forEach ? t : this.getStrutPoints(t); const api = this.api; const ctx = api.ctx; ctx.cacheStyle(); api.noFill(); + api.setStroke(color); let s = this.points.length; let n = this.points.length; @@ -176,6 +177,8 @@ class Bezier extends Original { s += n; } ctx.restoreStyle(); + + return p; } drawBoundingBox(color) { diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index a3f200f5..4197f42d 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -1547,49 +1547,49 @@ lli = function(line1, line2):

De Casteljau's algorithm is the pivotal algorithm when it comes to Bézier curves. You can use it not just to split curves, but also to draw them efficiently (especially for high-order Bézier curves), as well as to come up with curves based on three points and a tangent. Particularly this last thing is really useful because it lets us "mould" a curve, by picking it up at some point, and dragging that point around to change the curve's shape.

How does that work? Succinctly: we run de Casteljau's algorithm in reverse!

In order to run de Casteljau's algorithm in reverse, we need a few basic things: a start and end point, a point on the curve that want to be moving around, which has an associated t value, and a point we've not explicitly talked about before, and as far as I know has no explicit name, but lives one iteration higher in the de Casteljau process then our on-curve point does. I like to call it "A" for reasons that will become obvious.

-

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: in the following graphic, click anywhere on the curves to see the identity information that we'll be using to run de Casteljau in reverse (you can manipulate the curve even after picking a point. Note the "ratio" value when you do so: does it change?):

+

So let's use graphics instead of text to see where this "A" is, because text only gets us so far: move the sliders for the following graphics to see what, given specific t value, our A coordinate is. As well as some other coordinates, which taken together let us derive a value that the graphics call "ratio": if you move the curve's points around, A, B, and C will move, what happens to that value?

- - + + + + + Scripts are disabled. Showing fallback image. + + + + + + + Scripts are disabled. Showing fallback image. + + + +
-

Clicking anywhere on the curves shows us three things:

+

So these graphics show us several things:

    -
  1. our on-curve point; let's call that B,
  2. -
  3. a point at the tip of B's "hat", on de Casteljau step up; let's call that A, and
  4. -
  5. a point that we get by projecting B onto the start--end baseline; let's call that C.
  6. +
  7. a point at the tip of the curve construction's "hat": let's call that A, as well as
  8. +
  9. our on-curve point give our chosen t value: let's call that B, and finally,
  10. +
  11. a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that C.
-

These three values A, B, and C hide an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio distance of C along the baseline is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change. The following function stays true:

- -

So that just leaves finding A.

-
- -

While that relation is fixed, the function u(t) differs depending on whether we're working -with quadratic or cubic curves:

- -

So, if we know the start and end coordinates, and we know the t value, we know C:

-
- - -
- -

Mouse-over the graphs to see the expression for C, given the t value at the mouse pointer.

-
- -

There's also another important bit of information that is inherent to the ABC values: while the distances between A and B, and B and C, are dynamic (based on where we put B), the ratio between the two distances is stable. Given some t value, the following always holds:

- -

This leads to a pretty powerful bit of knowledge: merely by knowing the t value of some on curve point, we know where C has to be (as per the above note), and because we know B and C, and thus have the distance between them, we know where A has to be:

- -

And that's it, all values found.

-
- -

Much like the u(t) function in the above note, the ratio(t) function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the u(t) function in that they both come rolling out of the same function evaluation, explained over on MathOverflow by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow.

- - -

Unfortunately, this trick only works for quadratic and cubic curves. Once we hit higher order curves, things become a lot less predictable; the "fixed point C" is no longer fixed, moving around as we move the control points, and projections of B onto the line between start and end may actually lie on that line before the start, or after the end, and there are no simple ratios that we can exploit.

-
- -

So: if we know B and its corresponding t value, then we know all the ABC values, which —together with a start and end coordinate— gives us the necessary information to reconstruct a curve's "de Casteljau skeleton", which means that two points and a value between 0 and 1, we can come up with a curve. And that opens up possibilities: curve manipulation by dragging an on-curve point, as well as curve fitting of "a bunch of coordinates". These are useful things, and we'll look at both in the next sections.

+

These three values A, B, and C allow us to derive an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some t value, the ratio of distances from A to B and B to C is fixed: if some t value sets up a C that is 20% away from the start and 80% away from the end, then it doesn't matter where the start, end, or control points are; for that t value, C will always lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change.

+

So, how can we compute C? We start with our observation that C always lies somewhere between the start and ends points, so logically C will have a function that interpolates between those two coordinates:

+ +

If we can figure out what the function u(t) looks like, we'll be done. Although we do need to remember that this u(t) will have a different for depending on whether we're working with quadratic or cubic curves. Running through the maths (with thanks to Boris Zbarsky) shows us the following two formulae:

+ +

And

+ +

So, if we know the start and end coordinates, and we know the t value, we know C, without having to calculate the A or even B coordinates. In fact, we can do the same for the ratio function: as another function of t, we technically don't need to know what A or B or C are, we can express it was a pure function of t, too.

+

We start by observing that, given A, B, and C, the following always holds:

+ +

Working out the maths for this, we see the following two formulae for quadratic and cubic curves:

+ +

And

+ +

Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a t value, we can contruct curves: we can compute C using the start and end points, and our u(t) function, and once we have C, we can use our on-curve point (B) and the ratio(t) function to find A:

+ +

So: if we have a curve's start and end point, then for any t value, we implicitly know all the ABC values, which gives us the necessary information to reconstruct a curve's "de Casteljau skeleton". Which means that we can now do several things: we can "fit" curves using only three points, which means we can also "mould" curves by moving an on-curve point but leaving its start and end point, and then reconstructing the curve based on where we moved the on-curve point to. These are very useful things, and we'll look at both in the next sections.