diff --git a/docs/chapters/moulding/content.en-GB.md b/docs/chapters/moulding/content.en-GB.md index 01e8f446..8f52339d 100644 --- a/docs/chapters/moulding/content.en-GB.md +++ b/docs/chapters/moulding/content.en-GB.md @@ -24,8 +24,8 @@ In addition to the `A`, `B`, and `C` values, we also see the points `e1` and `e2 \[ \left \{ \begin{aligned} - v1 &= A' - \frac{A' - e1}{1 - t} \\ - v2 &= A' - \frac{A' - e2}{t} + v_1 &= A' - \frac{A' - e_1}{1 - t} \\ + v_2 &= A' - \frac{A' - e_2}{t} \end{aligned} \right . \] @@ -33,8 +33,8 @@ After which computing the new control points is straight-forward: \[ \left \{ \begin{aligned} - C1' &= start + \frac{v1 - start}{t} \\ - C2' &= end + \frac{v2 - end}{1 - t} + C_1' &= start + \frac{v_1 - start}{t} \\ + C_2' &= end + \frac{v_2 - end}{1 - t} \end{aligned} \right . \] @@ -42,7 +42,42 @@ So let's put that into practice: -So that looks pretty good, but you may not like having `e1` and `e2` stay the same distances away from `B'` while moving the point around. An alternative is to scale the distances of `e1` and `e2` to `B'` to match the scaling that `A`--`C` undergoes as `A'`--`C`' - whether this looks better or not depends somewhat on your intention as programmer or user, of course, so the following graphic applies this scaling, but it's up to you to decide whether or not that looks better (or, more appropriately, under which circumstances you might want to apply this scaling vs. when you might not): +So that looks pretty good, but you may not like having `e1` and `e2` stay the same distances away from `B'` while moving the point around, and want to rearrange those to lead to "cleaner looking" curve manipulation. Unfortunately, there are so many differen ways in which we can do this that figuring out "good looking" alternatives, given what the curve is being manipulated for, could be an entire book on its own... so we're only going to look at one way that you might effect alternative `e1` and `e2` points, based on the idea of rotating a vector. - +If we treat point `B` as a "a vector originating at `C`" then we can treat the points `e1` and `e2` as offets (let's call these `d1` and `d2`) of that vector, where: +\[ + \left \{ \begin{aligned} + e_1 &= B + d_1 \\ + e_2 &= B + d_2 + \end{aligned} \right . +\] + +Which means that: + +\[ + \left \{ \begin{aligned} + d_1 &= e_1 - B\\ + d_2 &= e_2 - B + \end{aligned} \right . +\] + +Now, if we now `B` to some new coordinate `B'` we can treat that "moving of the coordinate" as a rotation and scaling of the vector for `B` instead. If the new point `B'` is the same distance away from `C` as `B` was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor. + +We can use both those values to change where `e1` and `e2` end up, and thus how our curve moulding "feels", by placing new `e1'` and `e2'` where: + +\[ + \left \{ \begin{aligned} + angle &= atan2(B_y-C_y,B_x-C_x) - atan2(B_y\prime-C.y, B_x\prime-C.x) \\ + e_1' &= B' + scale \cdot rotate(d_1, B', angle) \\ + e_2' &= B' + scale \cdot rotate(d_2, B', angle) + \end{aligned} \right . +\] + +Here, the `rotate()` function rotates a vector (in this case `d1` or `d2`) around some point (in this case, `B'`), by some angle (in this case, the angle by which we rotated our original `B` to become `B'`). So what does _that_ look like? + + + +As you can see, this is both better, and worse, depending on what you're trying to do with the curve, and there are many different ways in which you can try to change `e1` and `e2` such that they behave "as users would expect them to" based on the context in which you're implementing curve moulding. You might want to add reflections when `B'` crosses the baseline, or even some kind of weight-swapping when `B'` crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to `C`, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc. + +There are too many options to go over here, so: the best behaviour is, of course, the behaviour _you_ think is best, and it might be a lot of work to find that and/or implement that! diff --git a/docs/chapters/moulding/moulding.js b/docs/chapters/moulding/moulding.js index 2810082d..b9b075d8 100644 --- a/docs/chapters/moulding/moulding.js +++ b/docs/chapters/moulding/moulding.js @@ -1,4 +1,4 @@ -let curve; +let curve, utils = Bezier.getUtils(); setup() { setPanelCount(3); @@ -84,14 +84,14 @@ drawQuadraticMark() { } drawCubicMark() { - let {B, t, e1, e2} = this.mark; - let d1 = { x: e1.x - B.x, y: e1.y - B.y}; - let d2 = { x: e2.x - B.x, y: e2.y - B.y}; + let {B, t, e1, e2, d1, d2} = this.mark; + let oB = B; + setFill(`black`); text(`t = ${t.toFixed(2)}`, B.x + 5, B.y + 10); let {A, C, S, E} = curve.getABC(this.mark.t, B); - let olen = dist(A.x, A.y, C.x, C.y); + let olen = dist(B.x, B.y, C.x, C.y); setColor(`lightblue`); line(S.x, S.y, E.x, E.y); line(A.x, A.y, C.x, C.y); @@ -104,11 +104,27 @@ drawCubicMark() { if (this.currentPoint) { let {A,B,C,S,E} = curve.getABC(this.mark.t, this.position); + let st1 = { x: B.x + d1.x, y: B.y + d1.y }; + let st2 = { x: B.x + d2.x, y: B.y + d2.y }; - let nlen = dist(A.x, A.y, C.x, C.y); - let f = this.parameters.scaling ? nlen/olen : 1; - let e1 = { x: B.x + f * d1.x, y: B.y + f * d1.y }; - let e2 = { x: B.x + f * d2.x, y: B.y + f * d2.y }; + if (this.parameters.alternative) { + let nlen = dist(B.x, B.y, C.x, C.y); + let scale = nlen/olen; + let angle = atan2(B.y-C.y, B.x-C.x) - atan2(oB.y-C.y, oB.x-C.x); + + st1 = { + x: B.x + scale * d1.x * cos(angle) - scale * d1.y * sin(angle), + y: B.y + scale * d1.x * sin(angle) + scale * d1.y * cos(angle) + }; + + st2 = { + x: B.x + scale * d2.x * cos(angle) - scale * d2.y * sin(angle), + y: B.y + scale * d2.x * sin(angle) + scale * d2.y * cos(angle) + }; + } + + e1 = st1; + e2 = st2; setColor(`purple`); line(A.x, A.y, C.x, C.y); @@ -171,11 +187,13 @@ onMouseDown() { }; } else { let struts = curve.getStrutPoints(t); - this.mark = { + let m = this.mark = { t, B: this.position.projection, e1: struts[7], e2: struts[8] }; + m.d1 = { x: m.e1.x - m.B.x, y: m.e1.y - m.B.y}; + m.d2 = { x: m.e2.x - m.B.x, y: m.e2.y - m.B.y}; } } redraw(); diff --git a/docs/images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg b/docs/images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg new file mode 100644 index 00000000..00903138 --- /dev/null +++ b/docs/images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg b/docs/images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg new file mode 100644 index 00000000..c3acdb5b --- /dev/null +++ b/docs/images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg b/docs/images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg new file mode 100644 index 00000000..e2bf60e2 --- /dev/null +++ b/docs/images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg b/docs/images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg new file mode 100644 index 00000000..50b10a22 --- /dev/null +++ b/docs/images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg b/docs/images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg deleted file mode 100644 index db1e1d53..00000000 --- a/docs/images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/images/chapters/moulding/53cf83a9d9f2173d3212ca87f380f071.png b/docs/images/chapters/moulding/adc7e0785dade356b62fadcd903e73d9.png similarity index 100% rename from docs/images/chapters/moulding/53cf83a9d9f2173d3212ca87f380f071.png rename to docs/images/chapters/moulding/adc7e0785dade356b62fadcd903e73d9.png diff --git a/docs/images/chapters/moulding/9de1da4f061d324415321a5ef688f067.png b/docs/images/chapters/moulding/b9928928e1f622e66b59e4a59cfac925.png similarity index 100% rename from docs/images/chapters/moulding/9de1da4f061d324415321a5ef688f067.png rename to docs/images/chapters/moulding/b9928928e1f622e66b59e4a59cfac925.png diff --git a/docs/images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg b/docs/images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg new file mode 100644 index 00000000..2d3cf340 --- /dev/null +++ b/docs/images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/chapters/moulding/a0df70324a97e780df772a837c5f1c31.png b/docs/images/chapters/moulding/db817c3899e954da6c882e4699a49353.png similarity index 100% rename from docs/images/chapters/moulding/a0df70324a97e780df772a837c5f1c31.png rename to docs/images/chapters/moulding/db817c3899e954da6c882e4699a49353.png diff --git a/docs/images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg b/docs/images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg deleted file mode 100644 index 777cef71..00000000 --- a/docs/images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 7a4c1adc..7646c13b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1632,7 +1632,7 @@ for (coordinate, index) in LUT:

Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the t-value matching that coordinate, and then letting them drag that point around. With every drag update we'll have a new point "B", which we can combine with the fixed point "C" to find our new point A. Once we have those, we can reconstruct the de Casteljau skeleton and thus construct a new curve with the same start/end points as the original curve, passing through the user-selected point B, with correct new control points.

- + Scripts are disabled. Showing fallback image. @@ -1650,23 +1650,33 @@ for (coordinate, index) in LUT:

In addition to the A, B, and C values, we also see the points e1 and e2, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points v1 and v2, which we can construct when we know our ABC values enriched with e1 and e2:

- +

After which computing the new control points is straight-forward:

- +

So let's put that into practice:

- + Scripts are disabled. Showing fallback image. -

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around. An alternative is to scale the distances of e1 and e2 to B' to match the scaling that A--C undergoes as A'--C' - whether this looks better or not depends somewhat on your intention as programmer or user, of course, so the following graphic applies this scaling, but it's up to you to decide whether or not that looks better (or, more appropriately, under which circumstances you might want to apply this scaling vs. when you might not):

- +

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around, and want to rearrange those to lead to "cleaner looking" curve manipulation. Unfortunately, there are so many differen ways in which we can do this that figuring out "good looking" alternatives, given what the curve is being manipulated for, could be an entire book on its own... so we're only going to look at one way that you might effect alternative e1 and e2 points, based on the idea of rotating a vector.

+

If we treat point B as a "a vector originating at C" then we can treat the points e1 and e2 as offets (let's call these d1 and d2) of that vector, where:

+ +

Which means that:

+ +

Now, if we now B to some new coordinate B' we can treat that "moving of the coordinate" as a rotation and scaling of the vector for B instead. If the new point B' is the same distance away from C as B was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.

+

We can use both those values to change where e1 and e2 end up, and thus how our curve moulding "feels", by placing new e1' and e2' where:

+ +

Here, the rotate() function rotates a vector (in this case d1 or d2) around some point (in this case, B'), by some angle (in this case, the angle by which we rotated our original B to become B'). So what does that look like?

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

As you can see, this is both better, and worse, depending on what you're trying to do with the curve, and there are many different ways in which you can try to change e1 and e2 such that they behave "as users would expect them to" based on the context in which you're implementing curve moulding. You might want to add reflections when B' crosses the baseline, or even some kind of weight-swapping when B' crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to C, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.

+

There are too many options to go over here, so: the best behaviour is, of course, the behaviour you think is best, and it might be a lot of work to find that and/or implement that!

diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index c31691cc..8c1b26ae 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -1629,7 +1629,7 @@ for (coordinate, index) in LUT:

Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the t-value matching that coordinate, and then letting them drag that point around. With every drag update we'll have a new point "B", which we can combine with the fixed point "C" to find our new point A. Once we have those, we can reconstruct the de Casteljau skeleton and thus construct a new curve with the same start/end points as the original curve, passing through the user-selected point B, with correct new control points.

- + Scripts are disabled. Showing fallback image. @@ -1647,23 +1647,33 @@ for (coordinate, index) in LUT:

In addition to the A, B, and C values, we also see the points e1 and e2, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points v1 and v2, which we can construct when we know our ABC values enriched with e1 and e2:

- +

After which computing the new control points is straight-forward:

- +

So let's put that into practice:

- + Scripts are disabled. Showing fallback image. -

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around. An alternative is to scale the distances of e1 and e2 to B' to match the scaling that A--C undergoes as A'--C' - whether this looks better or not depends somewhat on your intention as programmer or user, of course, so the following graphic applies this scaling, but it's up to you to decide whether or not that looks better (or, more appropriately, under which circumstances you might want to apply this scaling vs. when you might not):

- +

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around, and want to rearrange those to lead to "cleaner looking" curve manipulation. Unfortunately, there are so many differen ways in which we can do this that figuring out "good looking" alternatives, given what the curve is being manipulated for, could be an entire book on its own... so we're only going to look at one way that you might effect alternative e1 and e2 points, based on the idea of rotating a vector.

+

If we treat point B as a "a vector originating at C" then we can treat the points e1 and e2 as offets (let's call these d1 and d2) of that vector, where:

+ +

Which means that:

+ +

Now, if we now B to some new coordinate B' we can treat that "moving of the coordinate" as a rotation and scaling of the vector for B instead. If the new point B' is the same distance away from C as B was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.

+

We can use both those values to change where e1 and e2 end up, and thus how our curve moulding "feels", by placing new e1' and e2' where:

+ +

Here, the rotate() function rotates a vector (in this case d1 or d2) around some point (in this case, B'), by some angle (in this case, the angle by which we rotated our original B to become B'). So what does that look like?

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

As you can see, this is both better, and worse, depending on what you're trying to do with the curve, and there are many different ways in which you can try to change e1 and e2 such that they behave "as users would expect them to" based on the context in which you're implementing curve moulding. You might want to add reflections when B' crosses the baseline, or even some kind of weight-swapping when B' crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to C, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.

+

There are too many options to go over here, so: the best behaviour is, of course, the behaviour you think is best, and it might be a lot of work to find that and/or implement that!

diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index e8862b3b..b198d81b 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -1623,7 +1623,7 @@ for (coordinate, index) in LUT:

Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the t-value matching that coordinate, and then letting them drag that point around. With every drag update we'll have a new point "B", which we can combine with the fixed point "C" to find our new point A. Once we have those, we can reconstruct the de Casteljau skeleton and thus construct a new curve with the same start/end points as the original curve, passing through the user-selected point B, with correct new control points.

- + Scripts are disabled. Showing fallback image. @@ -1641,23 +1641,33 @@ for (coordinate, index) in LUT:

In addition to the A, B, and C values, we also see the points e1 and e2, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points v1 and v2, which we can construct when we know our ABC values enriched with e1 and e2:

- +

After which computing the new control points is straight-forward:

- +

So let's put that into practice:

- + Scripts are disabled. Showing fallback image. -

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around. An alternative is to scale the distances of e1 and e2 to B' to match the scaling that A--C undergoes as A'--C' - whether this looks better or not depends somewhat on your intention as programmer or user, of course, so the following graphic applies this scaling, but it's up to you to decide whether or not that looks better (or, more appropriately, under which circumstances you might want to apply this scaling vs. when you might not):

- +

So that looks pretty good, but you may not like having e1 and e2 stay the same distances away from B' while moving the point around, and want to rearrange those to lead to "cleaner looking" curve manipulation. Unfortunately, there are so many differen ways in which we can do this that figuring out "good looking" alternatives, given what the curve is being manipulated for, could be an entire book on its own... so we're only going to look at one way that you might effect alternative e1 and e2 points, based on the idea of rotating a vector.

+

If we treat point B as a "a vector originating at C" then we can treat the points e1 and e2 as offets (let's call these d1 and d2) of that vector, where:

+ +

Which means that:

+ +

Now, if we now B to some new coordinate B' we can treat that "moving of the coordinate" as a rotation and scaling of the vector for B instead. If the new point B' is the same distance away from C as B was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.

+

We can use both those values to change where e1 and e2 end up, and thus how our curve moulding "feels", by placing new e1' and e2' where:

+ +

Here, the rotate() function rotates a vector (in this case d1 or d2) around some point (in this case, B'), by some angle (in this case, the angle by which we rotated our original B to become B'). So what does that look like?

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

As you can see, this is both better, and worse, depending on what you're trying to do with the curve, and there are many different ways in which you can try to change e1 and e2 such that they behave "as users would expect them to" based on the context in which you're implementing curve moulding. You might want to add reflections when B' crosses the baseline, or even some kind of weight-swapping when B' crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to C, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.

+

There are too many options to go over here, so: the best behaviour is, of course, the behaviour you think is best, and it might be a lot of work to find that and/or implement that!