1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-29 19:20:39 +02:00
This commit is contained in:
Pomax
2020-08-31 14:53:01 -07:00
parent 7cdc4217f5
commit f52727160f
15 changed files with 125 additions and 39 deletions

View File

@@ -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:
<graphics-element title="Moulding a cubic Bézier curve" width="825" src="./moulding.js" data-type="cubic"></graphics-element>
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.
<graphics-element title="Moulding a cubic Bézier curve" width="825" src="./moulding.js" data-type="cubic" data-scaling="true"></graphics-element>
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?
<graphics-element title="Moulding a cubic Bézier curve" width="825" src="./moulding.js" data-type="cubic" data-alternative="true"></graphics-element>
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!

View File

@@ -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();

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="96" height="49px" viewBox="0 0 72 37"><defs><symbol overflow="visible" id="a"><path d="M9.219 14.5c0-.125-.281-.39-.39-.422-1.657-.61-2.642-2.281-2.642-3.75V1.36c0-1.75-1.484-3.671-3.03-4.484 1.546-.797 3.03-2.719 3.03-4.484v-8.954c0-1.468.985-3.14 2.641-3.75.11-.046.39-.296.39-.421 0-.172-.343-.453-.5-.453-.046 0-.077.015-.109.03-2.015.735-4.03 2.782-4.03 4.595v8.953c0 1.64-.954 3.468-2.626 4.078-.11.031-.406.281-.406.406 0 .14.297.39.406.422 1.672.61 2.625 2.422 2.625 4.062v8.97c0 1.796 2.016 3.858 4.031 4.593.032 0 .063.015.11.015.156 0 .5-.265.5-.437zm0 0"/></symbol><symbol overflow="visible" id="b"><path d="M6.297-8.203s-.125-.36-.281-.36c-.188 0-1.313.11-1.516.141-.094 0-.375.203-.375.36 0 .14.313.265.484.265.579 0 .407-.047.407.078l-.047.235-.719 2.828h.406c-.218-.438-.765-.89-1.297-.89-1.406 0-3.078 1.89-3.078 3.64 0 1.125.86 2.031 1.781 2.031.25 0 .97-.078 1.422-.61.032.094.657.61 1.22.61.421 0 .827-.297 1.015-.688l.062-.093c.203-.438.297-1.094.297-1.094l.063-.094c0-.11-.297-.25-.329-.25-.125 0-.343.188-.375.344-.203.781-.218 1.36-.703 1.36-.328 0-.171-.188-.171-.422 0-.282.03-.375.078-.579l1.718-6.906zm-2.188 4.11c0 .062-.015.14-.03.202l-.595 2.344c-.062.203 0 .14-.187.344-.531.656-.875.812-1.203.812-.594 0-.578-.53-.578-1 0-.593.328-1.968.593-2.515.375-.703.782-1.11 1.266-1.11.766 0 .734.844.734.922zm0 0"/></symbol><symbol overflow="visible" id="d"><path d="M8.828-4.281c0-.125-.312-.375-.437-.375H.906c-.125 0-.437.25-.437.375 0 .14.312.375.437.375h7.485c.125 0 .437-.235.437-.375zm0 2.328c0-.14-.312-.375-.437-.375H.906c-.125 0-.437.234-.437.375 0 .125.312.36.437.36h7.485c.125 0 .437-.235.437-.36zm0 0"/></symbol><symbol overflow="visible" id="e"><path d="M5.328-1.406c0-.078-.266-.297-.328-.297-.063 0-.234.062-.297.156C3.75-.359 2.593-.39 2.453-.39c-.937 0-.844-.875-.844-1.265 0-.14 0-.5.141-1.11h.484c.344 0 1.235-.015 1.829-.265.843-.36 1.093-1.203 1.093-1.375 0-.516-.656-1.14-1.468-1.14-1.329 0-3.344 1.296-3.344 3.39C.344-.937 1.25.125 2.422.125c1.719 0 2.906-1.39 2.906-1.531zm-.969-3c0 1.25-1.703 1.125-2.203 1.125h-.25c.422-1.688 1.469-1.735 1.782-1.735.562 0 .671.204.671.61zm0 0"/></symbol><symbol overflow="visible" id="f"><path d="M8.828-3.125c0-.125-.312-.36-.437-.36H.906c-.125 0-.437.235-.437.36 0 .14.312.375.437.375h7.485c.125 0 .437-.234.437-.375zm0 0"/></symbol><symbol overflow="visible" id="g"><path d="M9.234-6.64c0-.891-.984-1.782-2.421-1.782H2.796c-.235 0-.547.125-.547.36 0 .14.313.265.531.265l.14-.031s.11.031.313.062c.22.016.125-.093.125.063 0 .047-.015.078-.046.219l-1.61 6.421c-.11.47.063.422-.875.422-.203 0-.531.141-.531.375 0 .141.312.266.531.266h4.266c1.89 0 3.484-1.547 3.484-2.719 0-.86-.875-1.687-2.047-1.812v.265c1.25-.234 2.703-1.25 2.703-2.375zM7.75-6.689c0 1.047-.828 2.047-2.281 2.047H3.937l.72-2.828c.093-.422-.063-.328.452-.328h1.532c1.062 0 1.109.578 1.109 1.11zm-.672 3.844c0 1.188-.86 2.203-2.266 2.203h-1.89c-.125-.015.031.11.031 0 0-.03 0-.046.063-.265l.78-3.203h2.11c1.14 0 1.172.75 1.172 1.265zm0 0"/></symbol><symbol overflow="visible" id="c"><path d="M4.172-.156v-.469H3.64c-.829 0-.625.063-.625-.219v-4.64c0-.235-.235-.391-.563-.391-.578.578-1.203.531-1.75.531v.625c.406 0 1.11-.047 1.219-.11v3.985c0 .281.203.219-.625.219H.766v.64L2.469-.03l1.703.047zm0 0"/></symbol><symbol overflow="visible" id="h"><path d="M4.234-1.844H3.75c-.047.344-.063.735-.172.813-.062.047-.562 0-.687 0H1.812c.563-.5.86-.735 1.36-1.125.625-.5 1.265-1.14 1.265-1.938 0-1-1.093-1.781-2.171-1.781-1.032 0-1.954.89-1.954 1.656 0 .422.579.625.657.625.203 0 .656-.297.656-.61 0-.14-.266-.593-.422-.593.188-.437.547-.469.938-.469.843 0 1.062.5 1.062 1.172 0 .735-.453 1.203-.719 1.5l-2.03 2C.374-.516.311-.39.311 0h3.844l.313-1.844zm0 0"/></symbol></defs><use xlink:href="#a" x="-1.109" y="21.554"/><use xlink:href="#b" x="9.675" y="12.737"/><use xlink:href="#c" x="15.891" y="15.69"/><use xlink:href="#d" x="24.472" y="12.737"/><use xlink:href="#e" x="37.085" y="12.737"/><use xlink:href="#c" x="42.666" y="15.69"/><use xlink:href="#f" x="50.583" y="12.737"/><use xlink:href="#g" x="62.538" y="12.737"/><use xlink:href="#b" x="9.675" y="30.172"/><use xlink:href="#h" x="15.891" y="33.125"/><use xlink:href="#d" x="24.472" y="30.172"/><use xlink:href="#e" x="37.085" y="30.172"/><use xlink:href="#h" x="42.666" y="33.125"/><use xlink:href="#f" x="50.583" y="30.172"/><use xlink:href="#g" x="62.538" y="30.172"/></svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="95px" height="49px" viewBox="0 0 71 37"><defs><symbol overflow="visible" id="a"><path d="M9.219 14.5c0-.125-.281-.39-.39-.422-1.657-.61-2.642-2.281-2.642-3.75V1.36c0-1.75-1.484-3.671-3.03-4.484 1.546-.797 3.03-2.719 3.03-4.484v-8.954c0-1.468.985-3.14 2.641-3.75.11-.046.39-.296.39-.421 0-.172-.343-.453-.5-.453-.046 0-.077.015-.109.03-2.015.735-4.03 2.782-4.03 4.595v8.953c0 1.64-.954 3.468-2.626 4.078-.11.031-.406.281-.406.406 0 .14.297.39.406.422 1.672.61 2.625 2.422 2.625 4.062v8.97c0 1.796 2.016 3.858 4.031 4.593.032 0 .063.015.11.015.156 0 .5-.265.5-.437zm0 0"/></symbol><symbol overflow="visible" id="b"><path d="M5.328-1.406c0-.078-.266-.297-.328-.297-.063 0-.234.062-.297.156C3.75-.359 2.593-.39 2.453-.39c-.937 0-.844-.875-.844-1.265 0-.14 0-.5.141-1.11h.484c.344 0 1.235-.015 1.829-.265.843-.36 1.093-1.203 1.093-1.375 0-.516-.656-1.14-1.468-1.14-1.329 0-3.344 1.296-3.344 3.39C.344-.937 1.25.125 2.422.125c1.719 0 2.906-1.39 2.906-1.531zm-.969-3c0 1.25-1.703 1.125-2.203 1.125h-.25c.422-1.688 1.469-1.735 1.782-1.735.562 0 .671.204.671.61zm0 0"/></symbol><symbol overflow="visible" id="d"><path d="M8.828-4.281c0-.125-.312-.375-.437-.375H.906c-.125 0-.437.25-.437.375 0 .14.312.375.437.375h7.485c.125 0 .437-.235.437-.375zm0 2.328c0-.14-.312-.375-.437-.375H.906c-.125 0-.437.234-.437.375 0 .125.312.36.437.36h7.485c.125 0 .437-.235.437-.36zm0 0"/></symbol><symbol overflow="visible" id="e"><path d="M9.234-6.64c0-.891-.984-1.782-2.421-1.782H2.796c-.235 0-.547.125-.547.36 0 .14.313.265.531.265l.14-.031s.11.031.313.062c.22.016.125-.093.125.063 0 .047-.015.078-.046.219l-1.61 6.421c-.11.47.063.422-.875.422-.203 0-.531.141-.531.375 0 .141.312.266.531.266h4.266c1.89 0 3.484-1.547 3.484-2.719 0-.86-.875-1.687-2.047-1.812v.265c1.25-.234 2.703-1.25 2.703-2.375zM7.75-6.689c0 1.047-.828 2.047-2.281 2.047H3.937l.72-2.828c.093-.422-.063-.328.452-.328h1.532c1.062 0 1.109.578 1.109 1.11zm-.672 3.844c0 1.188-.86 2.203-2.266 2.203h-1.89c-.125-.015.031.11.031 0 0-.03 0-.046.063-.265l.78-3.203h2.11c1.14 0 1.172.75 1.172 1.265zm0 0"/></symbol><symbol overflow="visible" id="f"><path d="M8.828-3.125c0-.125-.312-.36-.437-.36H5.078v-3.374c0-.141-.297-.375-.422-.375-.14 0-.453.234-.453.375v3.375H.906c-.125 0-.437.234-.437.359 0 .14.312.375.437.375h3.297V.625c0 .125.313.36.453.36.125 0 .422-.235.422-.36V-2.75h3.313c.125 0 .437-.234.437-.375zm0 0"/></symbol><symbol overflow="visible" id="g"><path d="M6.297-8.203s-.125-.36-.281-.36c-.188 0-1.313.11-1.516.141-.094 0-.375.203-.375.36 0 .14.313.265.484.265.579 0 .407-.047.407.078l-.047.235-.719 2.828h.406c-.218-.438-.765-.89-1.297-.89-1.406 0-3.078 1.89-3.078 3.64 0 1.125.86 2.031 1.781 2.031.25 0 .97-.078 1.422-.61.032.094.657.61 1.22.61.421 0 .827-.297 1.015-.688l.062-.093c.203-.438.297-1.094.297-1.094l.063-.094c0-.11-.297-.25-.329-.25-.125 0-.343.188-.375.344-.203.781-.218 1.36-.703 1.36-.328 0-.171-.188-.171-.422 0-.282.03-.375.078-.579l1.718-6.906zm-2.188 4.11c0 .062-.015.14-.03.202l-.595 2.344c-.062.203 0 .14-.187.344-.531.656-.875.812-1.203.812-.594 0-.578-.53-.578-1 0-.593.328-1.968.593-2.515.375-.703.782-1.11 1.266-1.11.766 0 .734.844.734.922zm0 0"/></symbol><symbol overflow="visible" id="c"><path d="M4.172-.156v-.469H3.64c-.829 0-.625.063-.625-.219v-4.64c0-.235-.235-.391-.563-.391-.578.578-1.203.531-1.75.531v.625c.406 0 1.11-.047 1.219-.11v3.985c0 .281.203.219-.625.219H.766v.64L2.469-.03l1.703.047zm0 0"/></symbol><symbol overflow="visible" id="h"><path d="M4.234-1.844H3.75c-.047.344-.063.735-.172.813-.062.047-.562 0-.687 0H1.812c.563-.5.86-.735 1.36-1.125.625-.5 1.265-1.14 1.265-1.938 0-1-1.093-1.781-2.171-1.781-1.032 0-1.954.89-1.954 1.656 0 .422.579.625.657.625.203 0 .656-.297.656-.61 0-.14-.266-.593-.422-.593.188-.437.547-.469.938-.469.843 0 1.062.5 1.062 1.172 0 .735-.453 1.203-.719 1.5l-2.03 2C.374-.516.311-.39.311 0h3.844l.313-1.844zm0 0"/></symbol></defs><use xlink:href="#a" x="-1.109" y="21.554"/><use xlink:href="#b" x="9.675" y="12.737"/><use xlink:href="#c" x="15.246" y="15.69"/><use xlink:href="#d" x="23.827" y="12.737"/><use xlink:href="#e" x="36.44" y="12.737"/><use xlink:href="#f" x="48.479" y="12.737"/><use xlink:href="#g" x="60.434" y="12.737"/><use xlink:href="#c" x="66.654" y="15.69"/><use xlink:href="#b" x="9.675" y="30.172"/><use xlink:href="#h" x="15.246" y="33.125"/><use xlink:href="#d" x="23.827" y="30.172"/><use xlink:href="#e" x="36.44" y="30.172"/><use xlink:href="#f" x="48.479" y="30.172"/><use xlink:href="#g" x="60.434" y="30.172"/><use xlink:href="#h" x="66.654" y="33.125"/></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1632,7 +1632,7 @@ for (coordinate, index) in LUT:
<p>Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the <em>t</em>-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.</p>
<graphics-element title="Moulding a quadratic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="quadratic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\53cf83a9d9f2173d3212ca87f380f071.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\adc7e0785dade356b62fadcd903e73d9.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
@@ -1650,23 +1650,33 @@ for (coordinate, index) in LUT:
</graphics-element>
<p>In addition to the <code>A</code>, <code>B</code>, and <code>C</code> values, we also see the points <code>e1</code> and <code>e2</code>, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points <code>v1</code> and <code>v2</code>, which we can construct when we know our ABC values enriched with <code>e1</code> and <code>e2</code>:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg" width="140px" height="75px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg" width="139px" height="75px" loading="lazy">
<p>After which computing the new control points is straight-forward:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg" width="187px" height="72px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg" width="177px" height="72px" loading="lazy">
<p>So let's put that into practice:</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\a0df70324a97e780df772a837c5f1c31.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\b9928928e1f622e66b59e4a59cfac925.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> while moving the point around. An alternative is to scale the distances of <code>e1</code> and <code>e2</code> to <code>B'</code> to match the scaling that <code>A</code>--<code>C</code> undergoes as <code>A'</code>--<code>C</code>' - 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):</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-scaling="true">
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> 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 <code>e1</code> and <code>e2</code> points, based on the idea of rotating a vector.</p>
<p>If we treat point <code>B</code> as a "a vector originating at <code>C</code>" then we can treat the points <code>e1</code> and <code>e2</code> as offets (let's call these <code>d1</code> and <code>d2</code>) of that vector, where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg" width="95px" height="49px" loading="lazy">
<p>Which means that:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg" width="96px" height="49px" loading="lazy">
<p>Now, if we now <code>B</code> to some new coordinate <code>B'</code> we can treat that "moving of the coordinate" as a rotation and scaling of the vector for <code>B</code> instead. If the new point <code>B'</code> is the same distance away from <code>C</code> as <code>B</code> was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.</p>
<p>We can use both those values to change where <code>e1</code> and <code>e2</code> end up, and thus how our curve moulding "feels", by placing new <code>e1'</code> and <code>e2'</code> where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg" width="465px" height="67px" loading="lazy">
<p>Here, the <code>rotate()</code> function rotates a vector (in this case <code>d1</code> or <code>d2</code>) around some point (in this case, <code>B'</code>), by some angle (in this case, the angle by which we rotated our original <code>B</code> to become <code>B'</code>). So what does <em>that</em> look like?</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-alternative="true">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\9de1da4f061d324415321a5ef688f067.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\db817c3899e954da6c882e4699a49353.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>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 <code>e1</code> and <code>e2</code> 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 <code>B'</code> crosses the baseline, or even some kind of weight-swapping when <code>B'</code> crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to <code>C</code>, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.</p>
<p>There are too many options to go over here, so: the best behaviour is, of course, the behaviour <em>you</em> think is best, and it might be a lot of work to find that and/or implement that!</p>
</section>
<section id="pointcurves">

View File

@@ -1629,7 +1629,7 @@ for (coordinate, index) in LUT:
<p>Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the <em>t</em>-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.</p>
<graphics-element title="Moulding a quadratic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="quadratic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\53cf83a9d9f2173d3212ca87f380f071.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\adc7e0785dade356b62fadcd903e73d9.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
@@ -1647,23 +1647,33 @@ for (coordinate, index) in LUT:
</graphics-element>
<p>In addition to the <code>A</code>, <code>B</code>, and <code>C</code> values, we also see the points <code>e1</code> and <code>e2</code>, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points <code>v1</code> and <code>v2</code>, which we can construct when we know our ABC values enriched with <code>e1</code> and <code>e2</code>:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg" width="140px" height="75px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg" width="139px" height="75px" loading="lazy">
<p>After which computing the new control points is straight-forward:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg" width="187px" height="72px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg" width="177px" height="72px" loading="lazy">
<p>So let's put that into practice:</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\a0df70324a97e780df772a837c5f1c31.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\b9928928e1f622e66b59e4a59cfac925.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> while moving the point around. An alternative is to scale the distances of <code>e1</code> and <code>e2</code> to <code>B'</code> to match the scaling that <code>A</code>--<code>C</code> undergoes as <code>A'</code>--<code>C</code>' - 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):</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-scaling="true">
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> 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 <code>e1</code> and <code>e2</code> points, based on the idea of rotating a vector.</p>
<p>If we treat point <code>B</code> as a "a vector originating at <code>C</code>" then we can treat the points <code>e1</code> and <code>e2</code> as offets (let's call these <code>d1</code> and <code>d2</code>) of that vector, where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg" width="95px" height="49px" loading="lazy">
<p>Which means that:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg" width="96px" height="49px" loading="lazy">
<p>Now, if we now <code>B</code> to some new coordinate <code>B'</code> we can treat that "moving of the coordinate" as a rotation and scaling of the vector for <code>B</code> instead. If the new point <code>B'</code> is the same distance away from <code>C</code> as <code>B</code> was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.</p>
<p>We can use both those values to change where <code>e1</code> and <code>e2</code> end up, and thus how our curve moulding "feels", by placing new <code>e1'</code> and <code>e2'</code> where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg" width="465px" height="67px" loading="lazy">
<p>Here, the <code>rotate()</code> function rotates a vector (in this case <code>d1</code> or <code>d2</code>) around some point (in this case, <code>B'</code>), by some angle (in this case, the angle by which we rotated our original <code>B</code> to become <code>B'</code>). So what does <em>that</em> look like?</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-alternative="true">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\9de1da4f061d324415321a5ef688f067.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\db817c3899e954da6c882e4699a49353.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>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 <code>e1</code> and <code>e2</code> 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 <code>B'</code> crosses the baseline, or even some kind of weight-swapping when <code>B'</code> crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to <code>C</code>, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.</p>
<p>There are too many options to go over here, so: the best behaviour is, of course, the behaviour <em>you</em> think is best, and it might be a lot of work to find that and/or implement that!</p>
</section>
<section id="pointcurves">

View File

@@ -1623,7 +1623,7 @@ for (coordinate, index) in LUT:
<p>Armed with knowledge of the "ABC" relation, we can now update a curve interactively, by letting people click anywhere on the curve, find the <em>t</em>-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.</p>
<graphics-element title="Moulding a quadratic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="quadratic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\53cf83a9d9f2173d3212ca87f380f071.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\adc7e0785dade356b62fadcd903e73d9.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
@@ -1641,23 +1641,33 @@ for (coordinate, index) in LUT:
</graphics-element>
<p>In addition to the <code>A</code>, <code>B</code>, and <code>C</code> values, we also see the points <code>e1</code> and <code>e2</code>, without which constructing our de Casteljau "strut lines" becomes very difficult indeed; as well as the points <code>v1</code> and <code>v2</code>, which we can construct when we know our ABC values enriched with <code>e1</code> and <code>e2</code>:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/8e99a48e3227c97233c4933b5adcb080.svg" width="140px" height="75px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/bc245327e0b011712168bad1c48dfec4.svg" width="139px" height="75px" loading="lazy">
<p>After which computing the new control points is straight-forward:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/e787b4456ccf03c0481f1731c5c32add.svg" width="187px" height="72px" loading="lazy">
<img class="LaTeX SVG" src="./images/chapters/moulding/3c696e0364d61b1391695342707d6ccc.svg" width="177px" height="72px" loading="lazy">
<p>So let's put that into practice:</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\a0df70324a97e780df772a837c5f1c31.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\b9928928e1f622e66b59e4a59cfac925.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> while moving the point around. An alternative is to scale the distances of <code>e1</code> and <code>e2</code> to <code>B'</code> to match the scaling that <code>A</code>--<code>C</code> undergoes as <code>A'</code>--<code>C</code>' - 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):</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-scaling="true">
<p>So that looks pretty good, but you may not like having <code>e1</code> and <code>e2</code> stay the same distances away from <code>B'</code> 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 <code>e1</code> and <code>e2</code> points, based on the idea of rotating a vector.</p>
<p>If we treat point <code>B</code> as a "a vector originating at <code>C</code>" then we can treat the points <code>e1</code> and <code>e2</code> as offets (let's call these <code>d1</code> and <code>d2</code>) of that vector, where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/65c9a6f8a210d41b18f20e4da1ba1403.svg" width="95px" height="49px" loading="lazy">
<p>Which means that:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/28e90ffa101453e4c030174d36d185a5.svg" width="96px" height="49px" loading="lazy">
<p>Now, if we now <code>B</code> to some new coordinate <code>B'</code> we can treat that "moving of the coordinate" as a rotation and scaling of the vector for <code>B</code> instead. If the new point <code>B'</code> is the same distance away from <code>C</code> as <code>B</code> was, this is a pure rotation, but otherwise the length of the vector has decreased or increased by some factor.</p>
<p>We can use both those values to change where <code>e1</code> and <code>e2</code> end up, and thus how our curve moulding "feels", by placing new <code>e1'</code> and <code>e2'</code> where:</p>
<img class="LaTeX SVG" src="./images/chapters/moulding/54e423d1eb61157abd3acffc5271c3ac.svg" width="465px" height="67px" loading="lazy">
<p>Here, the <code>rotate()</code> function rotates a vector (in this case <code>d1</code> or <code>d2</code>) around some point (in this case, <code>B'</code>), by some angle (in this case, the angle by which we rotated our original <code>B</code> to become <code>B'</code>). So what does <em>that</em> look like?</p>
<graphics-element title="Moulding a cubic Bézier curve" width="825" height="275" src="./chapters/moulding/moulding.js" data-type="cubic" data-alternative="true">
<fallback-image>
<img width="825px" height="275px" src="images\chapters\moulding\9de1da4f061d324415321a5ef688f067.png" loading="lazy">
<img width="825px" height="275px" src="images\chapters\moulding\db817c3899e954da6c882e4699a49353.png" loading="lazy">
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element>
<p>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 <code>e1</code> and <code>e2</code> 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 <code>B'</code> crosses the baseline, or even some kind of weight-swapping when <code>B'</code> crosses the midline (perpendicular to the baseline, at its mid point), and instead of scaling both points with respects to <code>C</code>, you might want to scale them to coordinates 1/2rd and 2/3rd along the baseline, etc. etc.</p>
<p>There are too many options to go over here, so: the best behaviour is, of course, the behaviour <em>you</em> think is best, and it might be a lot of work to find that and/or implement that!</p>
</section>
<section id="pointcurves">