1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-09-03 05:12:43 +02:00

inflections

This commit is contained in:
Pomax
2020-08-19 22:24:01 -07:00
parent ddb7a8f063
commit b745c59f86
31 changed files with 299 additions and 138 deletions

View File

@@ -71,6 +71,8 @@ This gives us three coefficients {a, b, c} that are expressed in terms of `v` va
Easy-peasy. We can now almost trivially find the roots by plugging those values into the quadratic formula.
And as a cubic curve, there is also a meaningful second derivative, which we can compute by simple taking the derivative of the derivative.
### Quartic curves: Cardano's algorithm.
We haven't really looked at them before now, but the next step up would be a Quartic curve, a fourth degree Bézier curve. As expected, these have a derivative that is a cubic function, and now things get much harder. Cubic functions don't have a "simple" rule to find their roots, like the quadratic formula, and instead require quite a bit of rewriting to a form that we can even start to try to solve.
@@ -185,6 +187,9 @@ function getCubicRoots(pa, pb, pc, pd) {
And that's it. The maths is complicated, but the code is pretty much just "follow the maths, while caching as many values as we can to prevent recomputing things as much as possible" and now we have a way to find all roots for a cubic function and can just move on with using that to find extremities of our curves.
And of course, as a quartic curve also has meaningful second and third derivatives, we can quite easily compute those by using the derivative of the derivative (of the derivative), just as for cubic cuvers.
### Quintic and higher order curves: finding numerical solutions
And this is where thing stop, because we _cannot_ find the roots for polynomials of degree 5 or higher using algebra (a fact known as [the AbelRuffini theorem](https://en.wikipedia.org/wiki/Abel%E2%80%93Ruffini_theorem)). Instead, for occasions like these, where algebra simply cannot yield an answer, we turn to [numerical analysis](https://en.wikipedia.org/wiki/Numerical_analysis).
@@ -207,7 +212,10 @@ As it turns out, Newton-Raphson is so blindingly fast that we could get away wit
### In conclusion:
So now that we know how to do root finding, we can determine the first and second derivative roots for our Bézier curves, and show those roots overlaid on the previous graphics:
So now that we know how to do root finding, we can determine the first and second derivative roots for our Bézier curves, and show those roots overlaid on the previous graphics. For the quadratic curve, that means just the first derivative, in red:
<graphics-element title="Quadratic Bézier curve extremities" width="825" src="./quadratic.js"></graphics-element>
And for cubic curves, that means first and second derivatives, in red and purple respectively:
<graphics-element title="Cubic Bézier curve extremities" width="825" src="./cubic.js"></graphics-element>

View File

@@ -46,7 +46,7 @@ plotDimension(dim, dimension) {
dimension.drawCurve();
setFill(`red`);
setStroke(`red)`);
setStroke(`red`);
// There are four possible extrema: t=0, t=1, and
// up to two t values that solves B'(t)=0, provided
@@ -80,15 +80,31 @@ plotDimension(dim, dimension) {
}
});
// Done, show our extrema:
// Done, show our derivative-based extrema:
circle(t1 * dim, y1, 3);
text(`t = ${t1.toFixed(2)}`, map(t1, 0,1, 15,dim-15), y1 + 25);
circle(t2 * dim, y2, 3);
text(`t = ${t2.toFixed(2)}`, map(t2, 0,1, 15,dim-15), y2 + 25);
// And then show the second derivate inflection, if there is one
setFill(`purple`);
setStroke(`purple`);
this.getRoots(...dimension.dpoints[1].map(p => p.y)).forEach(t =>{
if (t > 0 && t < 1) {
let d = dimension.get(t);
circle(t * dim, d.y, 3);
text(`t = ${t.toFixed(2)}`, map(t, 0,1, 15,dim-15), d.y + 25);
}
});
restoreStyle();
}
getRoots(v1, v2, v3) {
if (v3 === undefined) {
return [-v1 / (v2 - v1)];
}
const a = v1 - 2*v2 + v3,
b = 2 * (v2 - v1),
c = v1,

View File

@@ -60,10 +60,10 @@ That is... unwieldy. So, we note that there are a lot of terms that involve mult
</div>
Aligning our curve so that three of the eight coefficients become zero, we end up with the following simple term function for *C(t)*:
Aligning our curve so that three of the eight coefficients become zero, and observing that scale does not affect finding `t` values, we end up with the following simple term function for *C(t)*:
\[
18 \left ( (3 x_3 y_2+2 x_4 y_2+3 x_2 y_3-x_4 y_3)t^2 + (3 x_3 y_2-x_4 y_2-3 x_2 y_3)t + (x_2 y_3-x_3 y_2) \right )
\left ( 3 x_3 y_2+2 x_4 y_2+3 x_2 y_3-x_4 y_3 \right ) t^2 + \left ( 3 x_3 y_2-x_4 y_2-3 x_2 y_3 \right ) t + \left ( x_2 y_3-x_3 y_2 \right )
\]
That's a lot easier to work with: we see a fair number of terms that we can compute and then cache, giving us the following simplification:
@@ -75,16 +75,16 @@ That's a lot easier to work with: we see a fair number of terms that we can comp
c = x_2 \cdot y_3 \\
d = x_4 \cdot y_3
\end{matrix}\right\}
\ C(t) = 18 \cdot \left ( (-3a + 2b + 3c - d)t^2 + (3a - b - 3c)t + (c - a) \right )
\ C(t) = (-3a + 2b + 3c - d)t^2 + (3a - b - 3c)t + (c - a)
\]
This is a plain quadratic curve, and we know how to solve *C(t) = 0*; we use the quadratic formula:
\[
\left.\begin{matrix}
x =& 18(-3a + 2b + 3c - d) \\
y =& 18(3a - b - 3c) \\
z =& 18(c - a)
x =& -3a + 2b + 3c - d \\
y =& 3a - b - 3c \\
z =& c - a
\end{matrix}\right\}
\ C(t) = 0 \ \Rightarrow\ t = \frac{-y \pm \sqrt{y^2 - 4 x z}}{2x}
\]
@@ -93,4 +93,4 @@ We can easily compute this value *if* the discriminator isn't a negative number
Taking that into account, we compute *t*, we disregard any *t* value that isn't in the Bézier interval [0,1], and we now know at which *t* value(s) our curve will inflect.
<Graphic title="Finding cubic Bézier curve inflections" setup={this.setupCubic} draw={this.draw}/>
<graphics-element title="Finding cubic Bézier curve inflections" src="./inflection.js"></graphics-element>

View File

@@ -1,17 +0,0 @@
module.exports = {
setupCubic: function(api) {
var curve = new api.Bezier(135,25, 25, 135, 215,75, 215,240);
api.setCurve(curve);
},
draw: function(api, curve) {
api.reset();
api.drawSkeleton(curve);
api.drawCurve(curve);
api.setColor("red");
curve.inflections().forEach(function(t) {
api.drawCircle(curve.get(t), 5);
});
}
};

View File

@@ -0,0 +1,62 @@
setup() {
const curve = this.curve = new Bezier(this, 70,250, 120,15, 20,95, 225,80);
setMovable(curve.points);
}
draw() {
clear();
const curve = this.curve;
curve.drawSkeleton();
curve.drawCurve();
curve.drawPoints();
const p = curve.align().points,
a = p[2].x * p[1].y,
b = p[3].x * p[1].y,
c = p[1].x * p[2].y,
d = p[3].x * p[2].y,
x = -3*a + 2*b + 3*c - d,
y = 3*a - b - 3*c,
z = c - a,
roots = [];
if (this.almost(x, 0) ) {
if (!this.almost(y, 0) ) {
roots.push(-z / y);
}
}
else {
const det = y * y - 4 * x * z,
sq = sqrt(det),
d2 = 2 * x;
if (!this.almost(d2, 0) ) {
roots.push(-(y+sq) / d2);
roots.push((sq-y) / d2);
}
}
setStroke(`red`);
setFill(`red`);
roots.forEach(t => {
if (0 <= t && t <= 1) {
let p = curve.get(t);
circle(p.x, p.y, 3);
text(`t=${t.toFixed(2)}`, p.x + 5, p.y + 15);
}
});
}
almost(v1, v2, epsilon=0.00001) {
return abs(v1 - v2) < epsilon;
}
onMouseMove() {
redraw();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="44pt" height="16" viewBox="0 0 44 12"><defs><symbol overflow="visible" id="a"><path d="M7.781-8.219c-.922-.344-1.593-.484-2.328-.484-.984 0-2.078.344-2.906.89C1.14-6.859.39-5.358.39-3.53c0 2.328 1.421 3.75 3.703 3.75C5.25.219 6.187-.094 7.28-.844l.157-.578-.266-.187c-1.234.765-1.75.921-2.672.921-1.89 0-2.781-1.015-2.781-3.14 0-1.328.375-2.406 1.11-3.156.546-.563 1.218-.829 2.171-.829.86 0 1.36.141 2 .547v1.063h.578c.078-.813.172-1.328.36-1.953zm0 0"/></symbol><symbol overflow="visible" id="b"><path d="M4.203-7.828a.735.735 0 01-.187-.14c-.063-.063-.11-.126-.22-.329-1.593 1.61-2.5 3.266-2.5 4.781v.797c0 1.516.907 3.172 2.5 4.781.11-.203.157-.265.22-.328.062-.062.125-.109.312-.203C2.875.063 2.281-1.344 2.281-2.719v-.797c0-1.39.594-2.78 2.047-4.25zm0 0"/></symbol><symbol overflow="visible" id="c"><path d="M3.703-5.516c-.453.047-.86.063-1.156.063.172-.984.297-1.578.531-2.25l-.25-.328a7.16 7.16 0 01-1.094.531l-.296 2.031c-.391.203-.704.328-1.063.407l-.047.406h1l-.64 3.25C.625-1.11.53-.813.53-.5c0 .297.266.61.5.61.422 0 .922-.282 1.86-1.032.218-.172.14-.125.437-.36l-.25-.437-.672.469c-.36.25-.484.313-.625.313-.093 0-.031.046-.031-.11 0-.297.156-1.234.516-3l.14-.61h1.266l.203-.89zm0 0"/></symbol><symbol overflow="visible" id="d"><path d="M3.766-2.719v-.797c0-1.515-.907-3.171-2.516-4.78-.11.202-.156.265-.203.327-.063.063-.125.11-.313.203 1.438 1.47 2.032 2.86 2.032 4.25v.797c0 1.375-.594 2.781-2.032 4.25.188.094.25.14.313.203.047.063.094.125.203.329C2.86.452 3.766-1.204 3.766-2.72zm0 0"/></symbol><symbol overflow="visible" id="e"><path d="M8.266-4.078a1.419 1.419 0 01-.047-.36c0-.109.015-.234.062-.484h-7.5c.063.25.063.375.063.484 0 .125 0 .235-.063.5h7.5zm0 2.625a1.332 1.332 0 01-.047-.36c0-.109.015-.234.062-.484h-7.5c.063.25.063.375.063.485 0 .125 0 .25-.063.5h7.5zm0 0"/></symbol><symbol overflow="visible" id="f"><path d="M5.688-4.5c0-2.625-.907-4-2.547-4C1.297-8.5.203-6.89.203-4c0 1.39.281 2.703.703 3.281.422.594 1.203.953 1.938.953 1.812 0 2.844-1.687 2.844-4.734zm-1.282.594C4.406-1.36 4.094-.437 3-.437c-1.156 0-1.516-1.079-1.516-4 0-2.516.313-3.375 1.438-3.375 1.172 0 1.484 1.03 1.484 3.906zm0 0"/></symbol></defs><use xlink:href="#a" x="-.001" y="9.082"/><use xlink:href="#b" x="8.105" y="9.082"/><use xlink:href="#c" x="13.174" y="9.082"/><use xlink:href="#d" x="17.214" y="9.082"/><use xlink:href="#e" x="25.607" y="9.082"/><g><use xlink:href="#f" x="38.017" y="9.082"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="44pt" height="13pt" viewBox="0 0 44 13"><defs><symbol overflow="visible" id="a"><path d="M9.281-8.438c0-.03-.219-.25-.328-.25a.701.701 0 00-.328.172l-.672.75c0 .016-.672-.921-2-.921-2.672 0-5.562 2.765-5.562 5.546C.39-1.17 2.016.266 3.844.266c1.031 0 2.078-.516 2.703-1.063C7.657-1.78 7.922-2.969 7.922-3c0-.125-.203-.031-.203-.031l-.125-.219c-.078 0-.344.156-.36.25-.109.344-.328 1.094-1.156 1.797-.812.672-1.422.828-2.047.828-1.078 0-2.156-.484-2.156-2.344 0-.687.188-2.531 1.39-3.922C4-7.5 4.986-8.062 6.048-8.062c1.219 0 1.719.796 1.719 2.187 0 .469-.032.484-.032.61 0 .109.329.25.375.25.157 0 .36-.157.407-.376zm0 0"/></symbol><symbol overflow="visible" id="b"><path d="M4.156 2.719c0-.047-.078-.172-.11-.203-1.234-.922-2-3.22-2-5.141v-1c0-1.906.767-4.203 2-5.14a.457.457 0 00.11-.188c0-.063-.25-.25-.312-.25a1.01 1.01 0 00-.203.062C2.312-8.156 1-5.67 1-3.625v1C1-.578 2.313 1.906 3.64 2.906c.016.016.188.063.204.063.062 0 .312-.203.312-.25zm0 0"/></symbol><symbol overflow="visible" id="c"><path d="M4.14-5.156c0-.125-.312-.266-.53-.266H2.78c.39-1.562.453-1.797.453-1.875 0-.203-.328-.453-.53-.453-.032 0-.579.14-.688.563l-.422 1.765H.64c-.25 0-.563.14-.563.36 0 .156.297.28.531.28h.813C.594-1.515.547-1.311.547-1.093c0 .64.656 1.219 1.312 1.219 1.22 0 2.079-1.875 2.079-1.969 0-.11-.282-.25-.329-.25-.109 0-.328.172-.375.297C2.72-.547 2.281-.39 1.875-.39c-.25 0-.172-.03-.172-.421 0-.282.016-.375.063-.579l.859-3.39h.953c.25 0 .563-.125.563-.375zm0 0"/></symbol><symbol overflow="visible" id="d"><path d="M3.64-2.625v-1c0-2.047-1.328-4.531-2.64-5.516a.774.774 0 00-.203-.062c-.063 0-.313.187-.313.25 0 .031.079.172.094.187 1.25.938 2.016 3.235 2.016 5.141v1c0 1.922-.766 4.219-2.016 5.14-.015.032-.094.157-.094.204 0 .047.25.25.313.25A.774.774 0 001 2.906c1.313-1 2.64-3.484 2.64-5.531zm0 0"/></symbol><symbol overflow="visible" id="e"><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="f"><path d="M5.688-3.953c0-.953-.063-1.922-.47-2.797-.562-1.156-1.734-1.469-2.234-1.469-.718 0-1.796.438-2.28 1.547-.376.828-.438 1.766-.438 2.719 0 .89.109 2.062.593 2.969.516.968 1.532 1.25 2.11 1.25.656 0 1.75-.391 2.281-1.516.375-.828.438-1.766.438-2.703zm-1.391-.14c0 .89 0 1.702-.125 2.468C4-.485 3.516-.265 2.969-.265 2.516-.266 2-.438 1.78-1.579c-.125-.719-.125-1.813-.125-2.516 0-.765 0-1.562.094-2.203.219-1.422.922-1.406 1.219-1.406.406 0 .984.094 1.219 1.266.109.671.109 1.578.109 2.343zm0 0"/></symbol></defs><use xlink:href="#a" x="-.479" y="9.082"/><use xlink:href="#b" x="8.93" y="9.082"/><use xlink:href="#c" x="13.58" y="9.082"/><use xlink:href="#d" x="17.896" y="9.082"/><g><use xlink:href="#e" x="25.87" y="9.082"/></g><g><use xlink:href="#f" x="38.495" y="9.082"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -2962,8 +2962,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/0ec5cc72a428d75defb480530b50d720.svg"
width="411px"
height="40px"
width="433px"
height="37px"
/>
<p>
So, if we can rewrite the Bézier component function as a plain
@@ -2983,8 +2983,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/e06ec558d99b53e559d24524f4201951.svg"
width="537px"
height="36px"
width="553px"
height="37px"
/>
<p>
And then, using these <em>v</em> values, we can find out what our
@@ -2993,8 +2993,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg"
width="315px"
height="119px"
width="317px"
height="112px"
/>
<p>
This gives us three coefficients {a, b, c} that are expressed in
@@ -3006,12 +3006,17 @@ function drawCurve(points[], t):
class="LaTeX SVG"
src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg"
width="308px"
height="63px"
height="64px"
/>
<p>
Easy-peasy. We can now almost trivially find the roots by plugging
those values into the quadratic formula.
</p>
<p>
And as a cubic curve, there is also a meaningful second derivative,
which we can compute by simple taking the derivative of the
derivative.
</p>
<h3>Quartic curves: Cardano's algorithm.</h3>
<p>
We haven't really looked at them before now, but the next step up
@@ -3034,8 +3039,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg"
width="253px"
height="44px"
width="264px"
height="41px"
/>
<p>
We can see that the easier formula only has two constants, rather
@@ -3180,6 +3185,12 @@ function getCubicRoots(pa, pb, pc, pd) {
way to find all roots for a cubic function and can just move on with
using that to find extremities of our curves.
</p>
<p>
And of course, as a quartic curve also has meaningful second and
third derivatives, we can quite easily compute those by using the
derivative of the derivative (of the derivative), just as for cubic
cuvers.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
And this is where thing stop, because we <em>cannot</em> find the
@@ -3278,7 +3289,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
So now that we know how to do root finding, we can determine the
first and second derivative roots for our Bézier curves, and show
those roots overlaid on the previous graphics:
those roots overlaid on the previous graphics. For the quadratic
curve, that means just the first derivative, in red:
</p>
<graphics-element
title="Quadratic Bézier curve extremities"
@@ -3296,6 +3308,11 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<p>
And for cubic curves, that means first and second derivatives, in
red and purple respectively:
</p>
<graphics-element
title="Cubic Bézier curve extremities"
width="825"
@@ -3306,7 +3323,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
width="825px"
height="275px"
src="images\chapters\extremities\68ed7f964bbfad946506cad184feb1b2.png"
src="images\chapters\extremities\1440c49b9192919163dc44d6b0cf156b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -3562,7 +3579,7 @@ function getCubicRoots(pa, pb, pc, pd) {
class="LaTeX SVG"
src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg"
width="59px"
height="16px"
height="17px"
/>
<p>
What we're saying here is that given the curvature function
@@ -3576,8 +3593,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/2029bca9f4fa15739553636af99b70a8.svg"
width="385px"
height="21px"
width="399px"
height="19px"
/>
<p>
The function <em>C(t)</em> is the cross product between the first
@@ -3607,14 +3624,14 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/4d78ebcf8626f777725d67d3672fa480.svg"
width="601px"
width="613px"
height="71px"
/>
<p>And of course the same functions for <em>y</em>:</p>
<img
class="LaTeX SVG"
src="images/latex/97b34ad5920612574d1b2a1a9d22d571.svg"
width="399px"
width="397px"
height="69px"
/>
<p>
@@ -3625,8 +3642,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg"
width="552px"
height="97px"
width="557px"
height="96px"
/>
<p>
That is... unwieldy. So, we note that there are a lot of terms
@@ -3638,14 +3655,15 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Aligning our curve so that three of the eight coefficients become
zero, we end up with the following simple term function for
<em>C(t)</em>:
zero, and observing that scale does not affect finding
<code>t</code> values, we end up with the following simple term
function for <em>C(t)</em>:
</p>
<img
class="LaTeX SVG"
src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg"
width="560px"
height="21px"
src="images/latex/1679090a942a43d27f886f236fc8d62b.svg"
width="533px"
height="19px"
/>
<p>
That's a lot easier to work with: we see a fair number of terms that
@@ -3654,8 +3672,8 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg"
width="509px"
src="images/latex/4b5c7d0bf0fcd769db007dd98d4a024d.svg"
width="480px"
height="73px"
/>
<p>
@@ -3664,9 +3682,9 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/b94df866222bed63d123df6b839a4d14.svg"
width="435px"
height="55px"
src="images/latex/7c9762c0e04693eb743905cdc0487f8b.svg"
width="428px"
height="53px"
/>
<p>
We can easily compute this value <em>if</em> the discriminator isn't
@@ -3679,11 +3697,22 @@ function getCubicRoots(pa, pb, pc, pd) {
<em>t</em> value that isn't in the Bézier interval [0,1], and we now
know at which <em>t</em> value(s) our curve will inflect.
</p>
<Graphic
<graphics-element
title="Finding cubic Bézier curve inflections"
setup="{this.setupCubic}"
draw="{this.draw}"
/>
width="275"
height="275"
src="./chapters/inflections/inflection.js"
>
<fallback-image>
<img
width="275px"
height="275px"
src="images\chapters\inflections\e81a6573cf3ea31045eb7e8dca3eecb3.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
</section>
<section id="canonical">
<h1><a href="#canonical">Canonical form (for cubic curves)</a></h1>

View File

@@ -2634,8 +2634,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/0ec5cc72a428d75defb480530b50d720.svg"
width="411px"
height="40px"
width="433px"
height="37px"
/>
<p>
So, if we can rewrite the Bézier component function as a plain
@@ -2655,8 +2655,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/e06ec558d99b53e559d24524f4201951.svg"
width="537px"
height="36px"
width="553px"
height="37px"
/>
<p>
And then, using these <em>v</em> values, we can find out what our
@@ -2665,8 +2665,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg"
width="315px"
height="119px"
width="317px"
height="112px"
/>
<p>
This gives us three coefficients {a, b, c} that are expressed in
@@ -2678,12 +2678,17 @@ function drawCurve(points[], t):
class="LaTeX SVG"
src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg"
width="308px"
height="63px"
height="64px"
/>
<p>
Easy-peasy. We can now almost trivially find the roots by plugging
those values into the quadratic formula.
</p>
<p>
And as a cubic curve, there is also a meaningful second derivative,
which we can compute by simple taking the derivative of the
derivative.
</p>
<h3>Quartic curves: Cardano's algorithm.</h3>
<p>
We haven't really looked at them before now, but the next step up
@@ -2706,8 +2711,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg"
width="253px"
height="44px"
width="264px"
height="41px"
/>
<p>
We can see that the easier formula only has two constants, rather
@@ -2852,6 +2857,12 @@ function getCubicRoots(pa, pb, pc, pd) {
way to find all roots for a cubic function and can just move on with
using that to find extremities of our curves.
</p>
<p>
And of course, as a quartic curve also has meaningful second and
third derivatives, we can quite easily compute those by using the
derivative of the derivative (of the derivative), just as for cubic
cuvers.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
And this is where thing stop, because we <em>cannot</em> find the
@@ -2950,7 +2961,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
So now that we know how to do root finding, we can determine the
first and second derivative roots for our Bézier curves, and show
those roots overlaid on the previous graphics:
those roots overlaid on the previous graphics. For the quadratic
curve, that means just the first derivative, in red:
</p>
<graphics-element
title="Quadratic Bézier curve extremities"
@@ -2968,6 +2980,11 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<p>
And for cubic curves, that means first and second derivatives, in
red and purple respectively:
</p>
<graphics-element
title="Cubic Bézier curve extremities"
width="825"
@@ -2978,7 +2995,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
width="825px"
height="275px"
src="images\chapters\extremities\68ed7f964bbfad946506cad184feb1b2.png"
src="images\chapters\extremities\1440c49b9192919163dc44d6b0cf156b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -3236,7 +3253,7 @@ function getCubicRoots(pa, pb, pc, pd) {
class="LaTeX SVG"
src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg"
width="59px"
height="16px"
height="17px"
/>
<p>
What we're saying here is that given the curvature function
@@ -3250,8 +3267,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/2029bca9f4fa15739553636af99b70a8.svg"
width="385px"
height="21px"
width="399px"
height="19px"
/>
<p>
The function <em>C(t)</em> is the cross product between the first
@@ -3281,14 +3298,14 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/4d78ebcf8626f777725d67d3672fa480.svg"
width="601px"
width="613px"
height="71px"
/>
<p>And of course the same functions for <em>y</em>:</p>
<img
class="LaTeX SVG"
src="images/latex/97b34ad5920612574d1b2a1a9d22d571.svg"
width="399px"
width="397px"
height="69px"
/>
<p>
@@ -3299,8 +3316,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg"
width="552px"
height="97px"
width="557px"
height="96px"
/>
<p>
That is... unwieldy. So, we note that there are a lot of terms
@@ -3312,14 +3329,15 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Aligning our curve so that three of the eight coefficients become
zero, we end up with the following simple term function for
<em>C(t)</em>:
zero, and observing that scale does not affect finding
<code>t</code> values, we end up with the following simple term
function for <em>C(t)</em>:
</p>
<img
class="LaTeX SVG"
src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg"
width="560px"
height="21px"
src="images/latex/1679090a942a43d27f886f236fc8d62b.svg"
width="533px"
height="19px"
/>
<p>
That's a lot easier to work with: we see a fair number of terms that
@@ -3328,8 +3346,8 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg"
width="509px"
src="images/latex/4b5c7d0bf0fcd769db007dd98d4a024d.svg"
width="480px"
height="73px"
/>
<p>
@@ -3338,9 +3356,9 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/b94df866222bed63d123df6b839a4d14.svg"
width="435px"
height="55px"
src="images/latex/7c9762c0e04693eb743905cdc0487f8b.svg"
width="428px"
height="53px"
/>
<p>
We can easily compute this value <em>if</em> the discriminator isn't
@@ -3353,11 +3371,22 @@ function getCubicRoots(pa, pb, pc, pd) {
<em>t</em> value that isn't in the Bézier interval [0,1], and we now
know at which <em>t</em> value(s) our curve will inflect.
</p>
<Graphic
<graphics-element
title="Finding cubic Bézier curve inflections"
setup="{this.setupCubic}"
draw="{this.draw}"
/>
width="275"
height="275"
src="./chapters/inflections/inflection.js"
>
<fallback-image>
<img
width="275px"
height="275px"
src="images\chapters\inflections\e81a6573cf3ea31045eb7e8dca3eecb3.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
</section>
<section id="canonical">
<h1>

View File

@@ -356,6 +356,11 @@ class Bezier {
return ret;
}
align() {
let p = this.points;
return new Bezier(utils.align(p, { p1: p[0], p2: p[p.length - 1] }));
}
curvature(t) {
return utils.curvature(t, this.points, this._3d);
}

View File

@@ -643,6 +643,8 @@ const utils = {
}
return [];
}
return [];
},
curvature: function (t, points, _3d, kOnly) {

View File

@@ -2644,8 +2644,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/0ec5cc72a428d75defb480530b50d720.svg"
width="411px"
height="40px"
width="433px"
height="37px"
/>
<p>
So, if we can rewrite the Bézier component function as a plain
@@ -2665,8 +2665,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/e06ec558d99b53e559d24524f4201951.svg"
width="537px"
height="36px"
width="553px"
height="37px"
/>
<p>
And then, using these <em>v</em> values, we can find out what our
@@ -2675,8 +2675,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/ddc6f99a543afad25c55cf16b9deeed9.svg"
width="315px"
height="119px"
width="317px"
height="112px"
/>
<p>
This gives us three coefficients {a, b, c} that are expressed in
@@ -2688,12 +2688,17 @@ function drawCurve(points[], t):
class="LaTeX SVG"
src="images/latex/d9e66caeb45b6643112ce3d971b17e5b.svg"
width="308px"
height="63px"
height="64px"
/>
<p>
Easy-peasy. We can now almost trivially find the roots by plugging
those values into the quadratic formula.
</p>
<p>
And as a cubic curve, there is also a meaningful second derivative,
which we can compute by simple taking the derivative of the
derivative.
</p>
<h3>Quartic curves: Cardano's algorithm.</h3>
<p>
We haven't really looked at them before now, but the next step up
@@ -2716,8 +2721,8 @@ function drawCurve(points[], t):
<img
class="LaTeX SVG"
src="images/latex/997a8cc704c0ab0e364cb8b532df90b0.svg"
width="253px"
height="44px"
width="264px"
height="41px"
/>
<p>
We can see that the easier formula only has two constants, rather
@@ -2862,6 +2867,12 @@ function getCubicRoots(pa, pb, pc, pd) {
way to find all roots for a cubic function and can just move on with
using that to find extremities of our curves.
</p>
<p>
And of course, as a quartic curve also has meaningful second and
third derivatives, we can quite easily compute those by using the
derivative of the derivative (of the derivative), just as for cubic
cuvers.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
And this is where thing stop, because we <em>cannot</em> find the
@@ -2960,7 +2971,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
So now that we know how to do root finding, we can determine the
first and second derivative roots for our Bézier curves, and show
those roots overlaid on the previous graphics:
those roots overlaid on the previous graphics. For the quadratic
curve, that means just the first derivative, in red:
</p>
<graphics-element
title="Quadratic Bézier curve extremities"
@@ -2978,6 +2990,11 @@ function getCubicRoots(pa, pb, pc, pd) {
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
<p>
And for cubic curves, that means first and second derivatives, in
red and purple respectively:
</p>
<graphics-element
title="Cubic Bézier curve extremities"
width="825"
@@ -2988,7 +3005,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
width="825px"
height="275px"
src="images\chapters\extremities\68ed7f964bbfad946506cad184feb1b2.png"
src="images\chapters\extremities\1440c49b9192919163dc44d6b0cf156b.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
@@ -3246,7 +3263,7 @@ function getCubicRoots(pa, pb, pc, pd) {
class="LaTeX SVG"
src="images/latex/bafdb6583323bda71d9a15c02d1fdec2.svg"
width="59px"
height="16px"
height="17px"
/>
<p>
What we're saying here is that given the curvature function
@@ -3260,8 +3277,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/2029bca9f4fa15739553636af99b70a8.svg"
width="385px"
height="21px"
width="399px"
height="19px"
/>
<p>
The function <em>C(t)</em> is the cross product between the first
@@ -3291,14 +3308,14 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/4d78ebcf8626f777725d67d3672fa480.svg"
width="601px"
width="613px"
height="71px"
/>
<p>And of course the same functions for <em>y</em>:</p>
<img
class="LaTeX SVG"
src="images/latex/97b34ad5920612574d1b2a1a9d22d571.svg"
width="399px"
width="397px"
height="69px"
/>
<p>
@@ -3309,8 +3326,8 @@ function getCubicRoots(pa, pb, pc, pd) {
<img
class="LaTeX SVG"
src="images/latex/b2433959e1f451fa3bf238fc37e04527.svg"
width="552px"
height="97px"
width="557px"
height="96px"
/>
<p>
That is... unwieldy. So, we note that there are a lot of terms
@@ -3322,14 +3339,15 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Aligning our curve so that three of the eight coefficients become
zero, we end up with the following simple term function for
<em>C(t)</em>:
zero, and observing that scale does not affect finding
<code>t</code> values, we end up with the following simple term
function for <em>C(t)</em>:
</p>
<img
class="LaTeX SVG"
src="images/latex/4dbe6398d0075b5b9ef39458ef620616.svg"
width="560px"
height="21px"
src="images/latex/1679090a942a43d27f886f236fc8d62b.svg"
width="533px"
height="19px"
/>
<p>
That's a lot easier to work with: we see a fair number of terms that
@@ -3338,8 +3356,8 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/d7a657089da19f032dd3b3e1d9ed1d89.svg"
width="509px"
src="images/latex/4b5c7d0bf0fcd769db007dd98d4a024d.svg"
width="480px"
height="73px"
/>
<p>
@@ -3348,9 +3366,9 @@ function getCubicRoots(pa, pb, pc, pd) {
</p>
<img
class="LaTeX SVG"
src="images/latex/b94df866222bed63d123df6b839a4d14.svg"
width="435px"
height="55px"
src="images/latex/7c9762c0e04693eb743905cdc0487f8b.svg"
width="428px"
height="53px"
/>
<p>
We can easily compute this value <em>if</em> the discriminator isn't
@@ -3363,11 +3381,22 @@ function getCubicRoots(pa, pb, pc, pd) {
<em>t</em> value that isn't in the Bézier interval [0,1], and we now
know at which <em>t</em> value(s) our curve will inflect.
</p>
<Graphic
<graphics-element
title="Finding cubic Bézier curve inflections"
setup="{this.setupCubic}"
draw="{this.draw}"
/>
width="275"
height="275"
src="./chapters/inflections/inflection.js"
>
<fallback-image>
<img
width="275px"
height="275px"
src="images\chapters\inflections\e81a6573cf3ea31045eb7e8dca3eecb3.png"
loading="lazy"
/>
Scripts are disabled. Showing fallback image.
</fallback-image></graphics-element
>
</section>
<section id="canonical">
<h1>