1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-29 11:10:38 +02:00

spell check

This commit is contained in:
Pomax
2020-09-26 15:55:13 -07:00
parent 6d3f6c4de4
commit 5fe5878eaa
17 changed files with 121 additions and 121 deletions

View File

@@ -24,7 +24,7 @@ So these graphics show us several things:
1. a point at the tip of the curve construction's "hat": let's call that `A`, as well as
2. our on-curve point give our chosen `t` value: let's call that `B`, and finally,
3. a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that `C`.
4. for both qudratic and cubic curves, two points `e1` and `e2`, which represent the single-to-last step in de Casteljau's algorithm: in the last step, we find `B` at `(1-t) * e1 + t * e2`.
4. for both quadratic and cubic curves, two points `e1` and `e2`, which represent the single-to-last step in de Casteljau's algorithm: in the last step, we find `B` at `(1-t) * e1 + t * e2`.
4. for cubic curves, also the points `v1` and `v2`, which together with `A` represent the first step in de Casteljau's algorithm: in the next step, we find `e1` and `e2`.
These three values A, B, and C allow us to derive an important identity formula for quadratic and cubic Bézier curves: for any point on the curve with some `t` value, the ratio of distances from A to B and B to C is fixed: if some `t` value sets up a C that is 20% away from the start and 80% away from the end, then _it doesn't matter where the start, end, or control points are_; for that `t` value, `C` will *always* lie at 20% from the start and 80% from the end point. Go ahead, pick an on-curve point in either graphic and then move all the other points around: if you only move the control points, start and end won't move, and so neither will C, and if you move either start or end point, C will move but its relative position will not change.
@@ -67,7 +67,7 @@ And
ratio(t)_{cubic} = \left | \frac{t^3 + (1-t)^3 - 1}{t^3 + (1-t)^3} \right |
\]
Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a `t` value, we can _contruct_ curves: we can compute `C` using the start and end points, and our `u(t)` function, and once we have `C`, we can use our on-curve point (`B`) and the `ratio(t)` function to find `A`:
Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a `t` value, we can _construct_ curves: we can compute `C` using the start and end points, and our `u(t)` function, and once we have `C`, we can use our on-curve point (`B`) and the `ratio(t)` function to find `A`:
\[
A = B - \frac{C - B}{ratio(t)} = B + \frac{B - C}{ratio(t)}

View File

@@ -221,7 +221,7 @@ This is essentially the "free form" version of a B-Spline, and also the least in
## One last thing: Rational B-Splines
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's "Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control pointl, just like for rational Bézier curves.
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's "Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control point, just like for rational Bézier curves.
<graphics-element title="A (closed) rational, uniform B-Spline" width="400" height="400" src="rational-uniform.js">
<!-- knot sliders go here -->

View File

@@ -48,7 +48,7 @@ Without repeating the paper mentioned at the top of this section, the loop-bound
The right bound for the loop region, indicating where the curve switches from "having inflections" to "having a loop", for the general cubic curve, is actually mirrored over x=1, but for Bézier curves this right half doesn't apply, so we don't need to pay attention to it. Similarly, the boundaries for t=0 and t=1 loops are also nice clean curves but get "cut off" when we only look at what the general curve does over the interval t=0 to t=1.
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school precalculus, you can probably follow along with this paper, although you might have to read it a few times before all the bits "click".
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school pre-calculus, you can probably follow along with this paper, although you might have to read it a few times before all the bits "click".
</div>

View File

@@ -1,6 +1,6 @@
# Bézier curves and Catmull-Rom curves
Taking an excursion to different splines, the other common design curve is the [Catmull-Rom spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline), which unlike Bézier curves pass _through_ each control point, so they offer a kind of "nuilt-in" curve fitting.
Taking an excursion to different splines, the other common design curve is the [Catmull-Rom spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline), which unlike Bézier curves pass _through_ each control point, so they offer a kind of "built-in" curve fitting.
In fact, let's start with just playing with one: the following graphic has a predefined curve that you manipulate the points for, and lets you add points by clicking/tapping the background, as well as let you control "how fast" the curve passes through its point using the tension slider. The tenser the curve, the more the curve tends towards straight lines from one point to the next.
@@ -28,7 +28,7 @@ Now, it may look like Catmull-Rom curves are very different from Bézier curves,
\right ]_{point-tangent}
\]
One downside of this is that—as you may have noticed from the graphic—the first and last point of the overall curve don't actually join up with the rest of the curve: they don't have a previous/next point respectively, and so there is no way to calculate what their tangent should be. Which also makes it rather tricky to fit a Camull-Rom curve to three points like we were able to do for Bézier curves. More on that in [the next section](#catmullfitting).
One downside of this is that—as you may have noticed from the graphic—the first and last point of the overall curve don't actually join up with the rest of the curve: they don't have a previous/next point respectively, and so there is no way to calculate what their tangent should be. Which also makes it rather tricky to fit a Catmull-Rom curve to three points like we were able to do for Bézier curves. More on that in [the next section](#catmullfitting).
In fact, before we move on, let's look at how to actually draw the basic form of these curves (I say basic, because there are a number of variations that make things [considerable](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline#Definition) more [complex](https://en.wikipedia.org/wiki/Kochanek%E2%80%93Bartels_spline)):
@@ -54,11 +54,11 @@ for p = 1 to points.length-3 (inclusive):
point(c0 * v1 + c1 * dv1 + c2 * v2 + c3 * dv2)
```
Now, since a Catmull-Rom curve is a form of [cubic Hermite spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline), and as cubic Bézier curves are _also_ a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can losslessly convert between the two, and the maths for doing so is surprisingly simple!
Now, since a Catmull-Rom curve is a form of [cubic Hermite spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline), and as cubic Bézier curves are _also_ a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can convert one to the other and back, and the maths for doing so is surprisingly simple!
The main difference between Catmull-Rom curves and Bézier curves is "what the points mean":
- A cubic Bézier curve is defined by a start point, a control point that implies the tangent at the start, a control point that implies the tangent at the end, and an end point, plus a characterising matrix that we can multiply by that point vector to get on-curve coordinates.
- A cubic Bézier curve is defined by a start point, a control point that implies the tangent at the start, a control point that implies the tangent at the end, and an end point, plus a characterizing matrix that we can multiply by that point vector to get on-curve coordinates.
- A Catmull-Rom curve is defined by a start point, a tangent that for that starting point, an end point, and a tangent for that end point, plus a characteristic matrix that we can multiple by the point vector to get on-curve coordinates.
Those are _very_ similar, so let's see exactly _how_ similar they are. We've already see the matrix form for Bézier curves, so how different is the matrix form for Catmull-Rom curves?:

View File

@@ -8,7 +8,7 @@ What we want is to ensure that the [curvature](https://en.wikipedia.org/wiki/Cur
Problem solved!
However, there's a problem with this approach: if we think about this a little more, we realise that "what a curve looks like" and its derivative values are pretty much entirely unrelated. After all, the section on [reordering curves](#reordering) showed us that the same looking curve can have an infinite number of curve expressions of arbitraryly high Bézier degree, and each of those will have _widly_ different derivative values.
However, there's a problem with this approach: if we think about this a little more, we realise that "what a curve looks like" and its derivative values are pretty much entirely unrelated. After all, the section on [reordering curves](#reordering) showed us that the same looking curve can have an infinite number of curve expressions of arbitrarily high Bézier degree, and each of those will have _wildly_ different derivative values.
So what we really want is some kind of expression that's not based on any particular expression of `t`, but is based on something that is invariant to the _kind_ of function(s) we use to draw our curve. And the prime candidate for this is our curve expression, reparameterised for distance: no matter what order of Bézier curve we use, if we were able to rewrite it as a function of distance-along-the-curve, all those different degree Bézier functions would end up being _the same_ function for "coordinate at some distance D along the curve".
@@ -39,7 +39,7 @@ Which is really just a "short form" that glosses over the fact that we're dealin
\kappa(t) = \frac{{B_x}'(t){B_y}''(t) - {B_x}''(t){B_y}'(t)}{({B_x}'(t)^2+{B_y}'(t)^2)^{\frac{3}{2}}}
\]
And while that's a litte more verbose, it's still just as simple to work with as the first function: the curvature at some point on any (and this cannot be overstated: _any_) curve is a ratio between the first and second derivative cross product, and something that looks oddly similar to the standard Euclidean distance function. And nothing in these functions is hard to calculate either: for Bézier curves, simply knowing our curve coordinates means [we know what the first and second derivatives are](#derivatives), and so evaluating this function for any **t** value is just a matter of basic arithematics.
And while that's a little more verbose, it's still just as simple to work with as the first function: the curvature at some point on any (and this cannot be overstated: _any_) curve is a ratio between the first and second derivative cross product, and something that looks oddly similar to the standard Euclidean distance function. And nothing in these functions is hard to calculate either: for Bézier curves, simply knowing our curve coordinates means [we know what the first and second derivatives are](#derivatives), and so evaluating this function for any **t** value is just a matter of basic arithematics.
In fact, let's just implement it right now:
@@ -53,9 +53,9 @@ function kappa(t, B):
return numerator / denominator
```
That was easy! (Well okay, that "not a number" value will need to be taken into account by downstream code, but that's a reality of programming anwyay)
That was easy! (Well okay, that "not a number" value will need to be taken into account by downstream code, but that's a reality of programming anyway)
With all of that covered, let's line up some curves! The following graphic gives you two curves that look identical, but use quadratic and cubic functions, respectively. As you can see, despite their derivatives being necessarily different, their curvature (thanks to being derived based on maths that "ignores" specific function derivative, and instead gives a formulat that smooths out any differences) is exactly the same. And because of that, we can put them together such that the point where they overlap has the same curvature for both curves, giving us the smoothest transition.
With all of that covered, let's line up some curves! The following graphic gives you two curves that look identical, but use quadratic and cubic functions, respectively. As you can see, despite their derivatives being necessarily different, their curvature (thanks to being derived based on maths that "ignores" specific function derivative, and instead gives a formula that smooths out any differences) is exactly the same. And because of that, we can put them together such that the point where they overlap has the same curvature for both curves, giving us the smoothest transition.
<graphics-element title="Matching curvatures for a quadratic and cubic Bézier curve" width="825" src="./curvature.js"></graphics-element>

View File

@@ -249,7 +249,7 @@ Now, given the above derivative, we can rearrange the terms (following the rules
Here, the "to the power negative one" is the notation for the [matrix inverse](https://en.wikipedia.org/wiki/Invertible_matrix). But that's all we have to do: we're done. Starting with **P** and inventing some `t` values based on the polygon the coordinates in **P** define, we can compute the corresponding Bézier coordinates **C** that specify a curve that goes through our points. Or, if it can't go through them exactly, as near as possible.
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified coordinates.
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to pre-specified coordinates.
So let's try it out! The following graphic lets you place points, and will start computing exact-fit curves once you've placed at least three. You can click for more points, and the code will simply try to compute an exact fit using a Bézier curve of the appropriate order. Four points? Cubic Bézier. Five points? Quartic. And so on. Of course, this does break down at some point: depending on where you place your points, it might become mighty hard for the fitter to find an exact fit, and things might actually start looking horribly off once there's enough points for compound [floating point rounding errors](https://en.wikipedia.org/wiki/Round-off_error#Floating-point_number_system) to start making a difference (which is around 10~11 points).

View File

@@ -87,9 +87,9 @@ Back in the 16<sup>th</sup> century, before Bézier curves were a thing, and eve
\end{aligned}
\]
We can see that the easier formula only has two constants, rather than four, and only two expressions involving `t`, rather than three: this makes things considerably easier to solve because it lets us use [regular calculus](https://www.wolframalpha.com/input/?i=t^3+%2B+pt+%2B+q) to find the values that satisfy the equasion.
We can see that the easier formula only has two constants, rather than four, and only two expressions involving `t`, rather than three: this makes things considerably easier to solve because it lets us use [regular calculus](https://www.wolframalpha.com/input/?i=t^3+%2B+pt+%2B+q) to find the values that satisfy the equation.
Now, there is one small hitch: as a cubic function, the solutions may be [complex numbers](https://en.wikipedia.org/wiki/Complex_number) rather than plain numbers... And Cardona realised this, centuries befor complex numbers were a well-understood and established part of number theory. His interpretation of them was "these numbers are impossible but that's okay because they disappear again in later steps", allowing him to not think about them too much, but we have it even easier: as we're trying to find the roots for display purposes, we don't even _care_ about complex numbers: we're going to simplify Cardano's approach just that tiny bit further by throwing away any solution that's not a plain number.
Now, there is one small hitch: as a cubic function, the solutions may be [complex numbers](https://en.wikipedia.org/wiki/Complex_number) rather than plain numbers... And Cardano realised this, centuries before complex numbers were a well-understood and established part of number theory. His interpretation of them was "these numbers are impossible but that's okay because they disappear again in later steps", allowing him to not think about them too much, but we have it even easier: as we're trying to find the roots for display purposes, we don't even _care_ about complex numbers: we're going to simplify Cardano's approach just that tiny bit further by throwing away any solution that's not a plain number.
So, how do we rewrite the hard formula into the easier formula? This is explained in detail over at [Ken J. Ward's page](https://trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm) for solving the cubic equation, so instead of showing the maths, I'm simply going to show the programming code for solving the cubic equation, with the complex roots getting totally ignored, but if you're interested you should definitely head over to Ken's page and give the procedure a read-through.
@@ -187,7 +187,7 @@ 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.
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 curves.
### Quintic and higher order curves: finding numerical solutions
@@ -196,7 +196,7 @@ And this is where thing stop, because we _cannot_ find the roots for polynomials
That's a fancy term for saying "rather than trying to find exact answers by manipulating symbols, find approximate answers by describing the underlying process as a combination of steps, each of which _can_ be assigned a number via symbolic manipulation". For example, trying to mathematically compute how much water fits in a completely crazy three dimensional shape is very hard, even if it got you the perfect, precise answer. A much easier approach, which would be less perfect but still entirely useful, would be to just grab a buck and start filling the shape until it was full: just count the number of buckets of water you used. And if we want a more precise answer, we can use smaller buckets.
So that's what we're going to do here, too: we're going to treat the problem as a sequence of steps, and the smaller we can make each step, the closer we'll get to that "perfect, precise" answer. And as it turns out, there is a really nice numerical root-finding algorithm, called the [Newton-Raphson](https://en.wikipedia.org/wiki/Newton-Raphson) root finding method (yes, after *[that](https://en.wikipedia.org/wiki/Isaac_Newton)* Newton), which we can make use of. The Newton-Raphson approach consists of taking our impossible-to-solve function `f(x)`, picking some intial value `x` (literally any value will do), and calculating `f(x)`. We can think of that value as the "height" of the function at `x`. If that height is zero, we're done, we have found a root. If it isn't, we calculate the tangent line at `f(x)` and calculate at which `x` value _its_ height is zero (which we've already seen is very easy). That will give us a new `x` and we repeat the process until we find a root.
So that's what we're going to do here, too: we're going to treat the problem as a sequence of steps, and the smaller we can make each step, the closer we'll get to that "perfect, precise" answer. And as it turns out, there is a really nice numerical root-finding algorithm, called the [Newton-Raphson](https://en.wikipedia.org/wiki/Newton-Raphson) root finding method (yes, after *[that](https://en.wikipedia.org/wiki/Isaac_Newton)* Newton), which we can make use of. The Newton-Raphson approach consists of taking our impossible-to-solve function `f(x)`, picking some initial value `x` (literally any value will do), and calculating `f(x)`. We can think of that value as the "height" of the function at `x`. If that height is zero, we're done, we have found a root. If it isn't, we calculate the tangent line at `f(x)` and calculate at which `x` value _its_ height is zero (which we've already seen is very easy). That will give us a new `x` and we repeat the process until we find a root.
Mathematically, this means that for some `x`, at step `n=1`, we perform the following calculation until `f<sub>y</sub>(x)` is zero, so that the next `t` is the same as the one we already have:

View File

@@ -2,7 +2,7 @@
Armed with knowledge of the "ABC" relation, point-on-curve projection, and guestimating reasonable looking helper values for cubic curve construction, we can finally cover curve molding: updating a curve's shape interactively, by dragging points on the curve around.
For quadratic curve, this is a really simple trick: we project our cursor onto the curve, which gives us a `t` value and initial `B` coordinate. We don't even need the latter: with our `t` value and "whever the cursor is" as target `B`, we can compute the associated `C`:
For quadratic curve, this is a really simple trick: we project our cursor onto the curve, which gives us a `t` value and initial `B` coordinate. We don't even need the latter: with our `t` value and "wherever the cursor is" as target `B`, we can compute the associated `C`:
\[
C = u(t)_{q} \cdot Start + \left ( 1-u(t)_{q} \right ) \cdot End
@@ -26,12 +26,12 @@ For example, let's see what happens if we just "go with what we get" when we pic
That looks reasonable, close to the original point, but the further we drag our point, the less "useful" things become. Especially if we drag our point across the baseline, rather than turning into a nice curve.
One way to combat this might be to combine the above approach with the approach from the [creating curves](#pointcurves) section: generate both the "unchanged `t`/`e1`/`e2`" curve, as well as the "idealised" curve through the start/cursor/end points, with idealised `t` value, and then interpolating between those two curves:
One way to combat this might be to combine the above approach with the approach from the [creating curves](#pointcurves) section: generate both the "unchanged `t`/`e1`/`e2`" curve, as well as the "idealized" curve through the start/cursor/end points, with idealized `t` value, and then interpolating between those two curves:
<graphics-element title="Molding a cubic Bézier curve" width="825" src="./molding.js" data-type="cubic" data-interpolated="true">
<input type="range" min="10" max="200" step="1" value="100" class="slide-control">
</graphics-element>
The slide controls the "falloff distance" relative to where the original point on the curve is, so that as we drag our point around, it interpolates with a bias towards "preserving `t`/`e1`/`e2`" closer to the original point, and bias towards "idealised" form the further away we move our point, with anything that's further than our falloff distance simply _being_ the idealised curve. We don't even try to interpolate at that point.
The slide controls the "falloff distance" relative to where the original point on the curve is, so that as we drag our point around, it interpolates with a bias towards "preserving `t`/`e1`/`e2`" closer to the original point, and bias towards "idealized" form the further away we move our point, with anything that's further than our falloff distance simply _being_ the idealized curve. We don't even try to interpolate at that point.
A more advanced way to try to smooth things out is to implement _continuous_ molding, where we constantly update the curve as we move around, and constantly change what our `B` point is, based on constantly projecting the cursor on the curve _as we're updating it_ - this is, you won't be surprised to learn, tricky, and beyond the scope of this section: interpolation (with a reasonable distance) will do for now!

View File

@@ -54,6 +54,6 @@ It is important to remember that even though we're using a circular arc to come
<graphics-element title="Fitting a quadratic Bézier curve" src="./cubic.js"></graphics-element>
That looks perfectly servicable!
That looks perfectly serviceable!
Of course, we can take this one step further: we can't just "create" curves, we also have (almost!) all the tools available to "mold" curves, where we can reshape a curve by dragging a point on the curve around while leaving the start and end fixed, effectively molding the shape as if it were clay or the like. We'll see the last tool we need to do that in the next section, and then we'll look at implementing curve molding in the section after that, so read on!

View File

@@ -39,7 +39,7 @@ The tangent is very useful for moving along a line, but what if we want to move
<div class="note">
Rotating coordinates is actually very easy, if you know the rule for it. You might find it explained as "applying a [rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix), which is what we'll look at here, too. Essentially, the idea is to take the circles over which we can rotate, and simply "sliding the coordinates" over these circles by the desired
angle. If we want a quarter circle turn, we take the coordinate, slide it along the cirle by a quarter turn, and done.
angle. If we want a quarter circle turn, we take the coordinate, slide it along the circle by a quarter turn, and done.
To turn any point <i>(x,y)</i> into a rotated point <i>(x',y')</i> (over 0,0) by some angle φ, we apply this nice and easy computation:

View File

@@ -1,6 +1,6 @@
# Finding Y, given X
One common task that pops up in things like CSS work, or parametric equalisers, or image leveling, or any other number of applications where Bézier curves are used as control curves in a way that there is really only ever one "y" value associated with one "x" value, you might want to cut out the middle man, as it were, and compute "y" directly based on "x". After all, the function looks simple enough, finding the "y" value should be simple too, right? Unfortunately, not really. However, it _is_ possible and as long as you have some code in place to help, it's not a lot of a work either.
One common task that pops up in things like CSS work, or parametric equalizers, or image leveling, or any other number of applications where Bézier curves are used as control curves in a way that there is really only ever one "y" value associated with one "x" value, you might want to cut out the middle man, as it were, and compute "y" directly based on "x". After all, the function looks simple enough, finding the "y" value should be simple too, right? Unfortunately, not really. However, it _is_ possible and as long as you have some code in place to help, it's not a lot of a work either.
We'll be tackling this problem in two stages: the first, which is the hard part, is figuring out which "t" value belongs to any given "x" value. For instance, have a look at the following graphic. On the left we have a Bézier curve that looks for all intents and purposes like it fits our criteria: every "x" has one and only one associated "y" value. On the right we see the function for just the "x" values: that's a cubic curve, but not a really crazy cubic curve. If you move the graphic's slider, you will see a red line drawn that corresponds to the `x` coordinate: this is a vertical line in the left graphic, and a horizontal line on the right.
@@ -8,7 +8,7 @@ We'll be tackling this problem in two stages: the first, which is the hard part,
<input type="range" min="0" max="1" step="0.01" class="slide-control">
</graphics-element>
Now, if you look more closely at that right graphic, you'll notice something interesting: if we treat the red line as "the x axis", then the point where the function crosses our line is really just a root for the cubic function x(t) through a shifted "x-axis"... and [we've already seen](#extremities) how to calculate roots, so let's just run cubuc root finding - and not even the complicated cubic case either: because of the kind of curve we're starting with, we _know_ there is only root, simplifying the code we need!
Now, if you look more closely at that right graphic, you'll notice something interesting: if we treat the red line as "the x axis", then the point where the function crosses our line is really just a root for the cubic function x(t) through a shifted "x-axis"... and [we've already seen](#extremities) how to calculate roots, so let's just run cubic root finding - and not even the complicated cubic case either: because of the kind of curve we're starting with, we _know_ there is only root, simplifying the code we need!
First, let's look at the function for x(t):
@@ -43,7 +43,7 @@ t = getRoots(p[0], p[1], p[2], p[3])[0]
y = curve.get(t).y
```
So the procedure is fairly straight forward: pick an `x`, find the associted `t` value, evaluate our curve _for_ that `t` value, which gives us the curve's {x,y} coordinate, which means we know `y` for this `x`. Move the slider for the following graphic to see this in action:
So the procedure is fairly straight forward: pick an `x`, find the associated `t` value, evaluate our curve _for_ that `t` value, which gives us the curve's {x,y} coordinate, which means we know `y` for this `x`. Move the slider for the following graphic to see this in action:
<graphics-element title="Finding By(t), by finding t for a given x" src="./yforx.js">
<input type="range" min="0" max="1" step="0.01" class="slide-control">

View File

@@ -31,7 +31,7 @@
<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:21:50+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:53:06+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -2364,7 +2364,7 @@ function drawCurve(points[], t):
Rotating coordinates is actually very easy, if you know the rule for it. You might find it explained as "applying a
<a href="https://en.wikipedia.org/wiki/Rotation_matrix">rotation matrix</a>, which is what we'll look at here, too. Essentially, the
idea is to take the circles over which we can rotate, and simply "sliding the coordinates" over these circles by the desired angle. If
we want a quarter circle turn, we take the coordinate, slide it along the cirle by a quarter turn, and done.
we want a quarter circle turn, we take the coordinate, slide it along the circle by a quarter turn, and done.
</p>
<p>
To turn any point <i>(x,y)</i> into a rotated point <i>(x',y')</i> (over 0,0) by some angle φ, we apply this nice and easy computation:
@@ -2930,12 +2930,12 @@ generateRMFrames(steps) -> frames:
<p>
We can see that the easier formula only has two constants, rather than four, and only two expressions involving <code>t</code>, rather
than three: this makes things considerably easier to solve because it lets us use
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equasion.
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equation.
</p>
<p>
Now, there is one small hitch: as a cubic function, the solutions may be
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardona realised this,
centuries befor complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardano realised this,
centuries before complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
numbers are impossible but that's okay because they disappear again in later steps", allowing him to not think about them too much, but we
have it even easier: as we're trying to find the roots for display purposes, we don't even <em>care</em> about complex numbers: we're
going to simplify Cardano's approach just that tiny bit further by throwing away any solution that's not a plain number.
@@ -3295,7 +3295,7 @@ function getCubicRoots(pa, pb, pc, pd) {
</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.
derivative of the derivative (of the derivative), just as for cubic curves.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
@@ -3316,7 +3316,7 @@ function getCubicRoots(pa, pb, pc, pd) {
step, the closer we'll get to that "perfect, precise" answer. And as it turns out, there is a really nice numerical root-finding
algorithm, called the <a href="https://en.wikipedia.org/wiki/Newton-Raphson">Newton-Raphson</a> root finding method (yes, after
<em><a href="https://en.wikipedia.org/wiki/Isaac_Newton">that</a></em> Newton), which we can make use of. The Newton-Raphson approach
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some intial value <code>x</code> (literally any value will
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some initial value <code>x</code> (literally any value will
do), and calculating <code>f(x)</code>. We can think of that value as the "height" of the function at <code>x</code>. If that height is
zero, we're done, we have found a root. If it isn't, we calculate the tangent line at <code>f(x)</code> and calculate at which
<code>x</code> value <em>its</em> height is zero (which we've already seen is very easy). That will give us a new <code>x</code> and we
@@ -3754,7 +3754,7 @@ function getCubicRoots(pa, pb, pc, pd) {
curve does over the interval t=0 to t=1.
</p>
<p>
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school precalculus, you
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school pre-calculus, you
can probably follow along with this paper, although you might have to read it a few times before all the bits "click".
</p>
</div>
@@ -3876,7 +3876,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<a href="#yforx">Finding Y, given X</a>
</h1>
<p>
One common task that pops up in things like CSS work, or parametric equalisers, or image leveling, or any other number of applications
One common task that pops up in things like CSS work, or parametric equalizers, or image leveling, or any other number of applications
where Bézier curves are used as control curves in a way that there is really only ever one "y" value associated with one "x" value, you
might want to cut out the middle man, as it were, and compute "y" directly based on "x". After all, the function looks simple enough,
finding the "y" value should be simple too, right? Unfortunately, not really. However, it <em>is</em> possible and as long as you have
@@ -3905,7 +3905,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Now, if you look more closely at that right graphic, you'll notice something interesting: if we treat the red line as "the x axis", then
the point where the function crosses our line is really just a root for the cubic function x(t) through a shifted "x-axis"... and
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubuc root finding - and not even the complicated
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubic root finding - and not even the complicated
cubic case either: because of the kind of curve we're starting with, we <em>know</em> there is only root, simplifying the code we need!
</p>
<p>First, let's look at the function for x(t):</p>
@@ -3974,7 +3974,7 @@ y = curve.get(t).y</textarea
</table>
<p>
So the procedure is fairly straight forward: pick an <code>x</code>, find the associted <code>t</code> value, evaluate our curve
So the procedure is fairly straight forward: pick an <code>x</code>, find the associated <code>t</code> value, evaluate our curve
<em>for</em> that <code>t</code> value, which gives us the curve's {x,y} coordinate, which means we know <code>y</code> for this
<code>x</code>. Move the slider for the following graphic to see this in action:
</p>
@@ -4211,8 +4211,8 @@ y = curve.get(t).y</textarea
<p>
However, there's a problem with this approach: if we think about this a little more, we realise that "what a curve looks like" and its
derivative values are pretty much entirely unrelated. After all, the section on <a href="#reordering">reordering curves</a> showed us that
the same looking curve can have an infinite number of curve expressions of arbitraryly high Bézier degree, and each of those will have
<em>widly</em> different derivative values.
the same looking curve can have an infinite number of curve expressions of arbitrarily high Bézier degree, and each of those will have
<em>wildly</em> different derivative values.
</p>
<p>
So what we really want is some kind of expression that's not based on any particular expression of <code>t</code>, but is based on
@@ -4256,7 +4256,7 @@ y = curve.get(t).y</textarea
</p>
<img class="LaTeX SVG" src="./images/chapters/curvature/afd8cb8b0fe291ff703752c1c9cc33d4.svg" width="239px" height="55px" loading="lazy" />
<p>
And while that's a litte more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
And while that's a little more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
(and this cannot be overstated: <em>any</em>) curve is a ratio between the first and second derivative cross product, and something that
looks oddly similar to the standard Euclidean distance function. And nothing in these functions is hard to calculate either: for Bézier
curves, simply knowing our curve coordinates means <a href="#derivatives">we know what the first and second derivatives are</a>, and so
@@ -4301,12 +4301,12 @@ function kappa(t, B):
<p>
That was easy! (Well okay, that "not a number" value will need to be taken into account by downstream code, but that's a reality of
programming anwyay)
programming anyway)
</p>
<p>
With all of that covered, let's line up some curves! The following graphic gives you two curves that look identical, but use quadratic and
cubic functions, respectively. As you can see, despite their derivatives being necessarily different, their curvature (thanks to being
derived based on maths that "ignores" specific function derivative, and instead gives a formulat that smooths out any differences) is
derived based on maths that "ignores" specific function derivative, and instead gives a formula that smooths out any differences) is
exactly the same. And because of that, we can put them together such that the point where they overlap has the same curvature for both
curves, giving us the smoothest transition.
</p>
@@ -4705,7 +4705,7 @@ lli = function(line1, line2):
a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that <code>C</code>.
</li>
<li>
for both qudratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
for both quadratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
Casteljau's algorithm: in the last step, we find <code>B</code> at <code>(1-t) * e1 + t * e2</code>.
</li>
<li>
@@ -4751,7 +4751,7 @@ lli = function(line1, line2):
<img class="LaTeX SVG" src="./images/chapters/abc/c018aab3952ea9193848564aab12b241.svg" width="223px" height="41px" loading="lazy" />
<p>
Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a
<code>t</code> value, we can <em>contruct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>t</code> value, we can <em>construct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>u(t)</code> function, and once we have <code>C</code>, we can use our on-curve point (<code>B</code>) and the
<code>ratio(t)</code> function to find <code>A</code>:
</p>
@@ -4894,7 +4894,7 @@ lli = function(line1, line2):
<label>Fitting a quadratic Bézier curve</label>
</fallback-image></graphics-element
>
<p>That looks perfectly servicable!</p>
<p>That looks perfectly serviceable!</p>
<p>
Of course, we can take this one step further: we can't just "create" curves, we also have (almost!) all the tools available to "mold"
curves, where we can reshape a curve by dragging a point on the curve around while leaving the start and end fixed, effectively molding
@@ -5004,7 +5004,7 @@ for (coordinate, index) in LUT:
</p>
<p>
For quadratic curve, this is a really simple trick: we project our cursor onto the curve, which gives us a <code>t</code> value and
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "whever the cursor is" as target
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "wherever the cursor is" as target
<code>B</code>, we can compute the associated <code>C</code>:
</p>
<img class="LaTeX SVG" src="./images/chapters/molding/70262c533569a7da06cc1b950e932d6f.svg" width="248px" height="24px" loading="lazy" />
@@ -5048,7 +5048,7 @@ for (coordinate, index) in LUT:
<p>
One way to combat this might be to combine the above approach with the approach from the
<a href="#pointcurves">creating curves</a> section: generate both the "unchanged <code>t</code>/<code>e1</code>/<code>e2</code>" curve, as
well as the "idealised" curve through the start/cursor/end points, with idealised <code>t</code> value, and then interpolating between
well as the "idealized" curve through the start/cursor/end points, with idealized <code>t</code> value, and then interpolating between
those two curves:
</p>
<graphics-element
@@ -5069,8 +5069,8 @@ for (coordinate, index) in LUT:
<p>
The slide controls the "falloff distance" relative to where the original point on the curve is, so that as we drag our point around, it
interpolates with a bias towards "preserving <code>t</code>/<code>e1</code>/<code>e2</code>" closer to the original point, and bias
towards "idealised" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealised curve. We don't even try to interpolate at that point.
towards "idealized" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealized curve. We don't even try to interpolate at that point.
</p>
<p>
A more advanced way to try to smooth things out is to implement <em>continuous</em> molding, where we constantly update the curve as we
@@ -5385,7 +5385,7 @@ for (coordinate, index) in LUT:
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be
writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you
are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to pre-specified
coordinates.
</p>
<p>
@@ -5421,7 +5421,7 @@ for (coordinate, index) in LUT:
<p>
Taking an excursion to different splines, the other common design curve is the
<a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom spline</a>, which unlike Bézier curves
pass <em>through</em> each control point, so they offer a kind of "nuilt-in" curve fitting.
pass <em>through</em> each control point, so they offer a kind of "built-in" curve fitting.
</p>
<p>
In fact, let's start with just playing with one: the following graphic has a predefined curve that you manipulate the points for, and lets
@@ -5455,7 +5455,7 @@ for (coordinate, index) in LUT:
<p>
One downside of this is that—as you may have noticed from the graphic—the first and last point of the overall curve don't actually join up
with the rest of the curve: they don't have a previous/next point respectively, and so there is no way to calculate what their tangent
should be. Which also makes it rather tricky to fit a Camull-Rom curve to three points like we were able to do for Bézier curves. More on
should be. Which also makes it rather tricky to fit a Catmull-Rom curve to three points like we were able to do for Bézier curves. More on
that in <a href="#catmullfitting">the next section</a>.
</p>
<p>
@@ -5550,14 +5550,14 @@ for p = 1 to points.length-3 (inclusive):
<p>
Now, since a Catmull-Rom curve is a form of <a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline">cubic Hermite spline</a>, and as
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can
losslessly convert between the two, and the maths for doing so is surprisingly simple!
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can convert
one to the other and back, and the maths for doing so is surprisingly simple!
</p>
<p>The main difference between Catmull-Rom curves and Bézier curves is "what the points mean":</p>
<ul>
<li>
A cubic Bézier curve is defined by a start point, a control point that implies the tangent at the start, a control point that implies
the tangent at the end, and an end point, plus a characterising matrix that we can multiply by that point vector to get on-curve
the tangent at the end, and an end point, plus a characterizing matrix that we can multiply by that point vector to get on-curve
coordinates.
</li>
<li>
@@ -7081,8 +7081,8 @@ for(let L = 1; L <= order; L++) {
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's
"Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a
ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control pointl, just
like for rational Bézier curves.
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control point, just like
for rational Bézier curves.
</p>
<graphics-element title="A (closed) rational, uniform B-Spline" width="400" height="400" src="./chapters/bsplines/rational-uniform.js">
<fallback-image>

View File

@@ -33,7 +33,7 @@
<meta property="og:locale" content="ja-JP" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:21:50+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:53:06+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -2204,7 +2204,7 @@ function drawCurve(points[], t):
Rotating coordinates is actually very easy, if you know the rule for it. You might find it explained as "applying a
<a href="https://en.wikipedia.org/wiki/Rotation_matrix">rotation matrix</a>, which is what we'll look at here, too. Essentially, the
idea is to take the circles over which we can rotate, and simply "sliding the coordinates" over these circles by the desired angle. If
we want a quarter circle turn, we take the coordinate, slide it along the cirle by a quarter turn, and done.
we want a quarter circle turn, we take the coordinate, slide it along the circle by a quarter turn, and done.
</p>
<p>
To turn any point <i>(x,y)</i> into a rotated point <i>(x',y')</i> (over 0,0) by some angle φ, we apply this nice and easy computation:
@@ -2770,12 +2770,12 @@ generateRMFrames(steps) -> frames:
<p>
We can see that the easier formula only has two constants, rather than four, and only two expressions involving <code>t</code>, rather
than three: this makes things considerably easier to solve because it lets us use
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equasion.
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equation.
</p>
<p>
Now, there is one small hitch: as a cubic function, the solutions may be
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardona realised this,
centuries befor complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardano realised this,
centuries before complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
numbers are impossible but that's okay because they disappear again in later steps", allowing him to not think about them too much, but we
have it even easier: as we're trying to find the roots for display purposes, we don't even <em>care</em> about complex numbers: we're
going to simplify Cardano's approach just that tiny bit further by throwing away any solution that's not a plain number.
@@ -3135,7 +3135,7 @@ function getCubicRoots(pa, pb, pc, pd) {
</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.
derivative of the derivative (of the derivative), just as for cubic curves.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
@@ -3156,7 +3156,7 @@ function getCubicRoots(pa, pb, pc, pd) {
step, the closer we'll get to that "perfect, precise" answer. And as it turns out, there is a really nice numerical root-finding
algorithm, called the <a href="https://en.wikipedia.org/wiki/Newton-Raphson">Newton-Raphson</a> root finding method (yes, after
<em><a href="https://en.wikipedia.org/wiki/Isaac_Newton">that</a></em> Newton), which we can make use of. The Newton-Raphson approach
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some intial value <code>x</code> (literally any value will
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some initial value <code>x</code> (literally any value will
do), and calculating <code>f(x)</code>. We can think of that value as the "height" of the function at <code>x</code>. If that height is
zero, we're done, we have found a root. If it isn't, we calculate the tangent line at <code>f(x)</code> and calculate at which
<code>x</code> value <em>its</em> height is zero (which we've already seen is very easy). That will give us a new <code>x</code> and we
@@ -3594,7 +3594,7 @@ function getCubicRoots(pa, pb, pc, pd) {
curve does over the interval t=0 to t=1.
</p>
<p>
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school precalculus, you
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school pre-calculus, you
can probably follow along with this paper, although you might have to read it a few times before all the bits "click".
</p>
</div>
@@ -3716,7 +3716,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<a href="ja-JP/index.html#yforx">Finding Y, given X</a>
</h1>
<p>
One common task that pops up in things like CSS work, or parametric equalisers, or image leveling, or any other number of applications
One common task that pops up in things like CSS work, or parametric equalizers, or image leveling, or any other number of applications
where Bézier curves are used as control curves in a way that there is really only ever one "y" value associated with one "x" value, you
might want to cut out the middle man, as it were, and compute "y" directly based on "x". After all, the function looks simple enough,
finding the "y" value should be simple too, right? Unfortunately, not really. However, it <em>is</em> possible and as long as you have
@@ -3745,7 +3745,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Now, if you look more closely at that right graphic, you'll notice something interesting: if we treat the red line as "the x axis", then
the point where the function crosses our line is really just a root for the cubic function x(t) through a shifted "x-axis"... and
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubuc root finding - and not even the complicated
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubic root finding - and not even the complicated
cubic case either: because of the kind of curve we're starting with, we <em>know</em> there is only root, simplifying the code we need!
</p>
<p>First, let's look at the function for x(t):</p>
@@ -3814,7 +3814,7 @@ y = curve.get(t).y</textarea
</table>
<p>
So the procedure is fairly straight forward: pick an <code>x</code>, find the associted <code>t</code> value, evaluate our curve
So the procedure is fairly straight forward: pick an <code>x</code>, find the associated <code>t</code> value, evaluate our curve
<em>for</em> that <code>t</code> value, which gives us the curve's {x,y} coordinate, which means we know <code>y</code> for this
<code>x</code>. Move the slider for the following graphic to see this in action:
</p>
@@ -4051,8 +4051,8 @@ y = curve.get(t).y</textarea
<p>
However, there's a problem with this approach: if we think about this a little more, we realise that "what a curve looks like" and its
derivative values are pretty much entirely unrelated. After all, the section on <a href="#reordering">reordering curves</a> showed us that
the same looking curve can have an infinite number of curve expressions of arbitraryly high Bézier degree, and each of those will have
<em>widly</em> different derivative values.
the same looking curve can have an infinite number of curve expressions of arbitrarily high Bézier degree, and each of those will have
<em>wildly</em> different derivative values.
</p>
<p>
So what we really want is some kind of expression that's not based on any particular expression of <code>t</code>, but is based on
@@ -4096,7 +4096,7 @@ y = curve.get(t).y</textarea
</p>
<img class="LaTeX SVG" src="./images/chapters/curvature/afd8cb8b0fe291ff703752c1c9cc33d4.svg" width="239px" height="55px" loading="lazy" />
<p>
And while that's a litte more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
And while that's a little more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
(and this cannot be overstated: <em>any</em>) curve is a ratio between the first and second derivative cross product, and something that
looks oddly similar to the standard Euclidean distance function. And nothing in these functions is hard to calculate either: for Bézier
curves, simply knowing our curve coordinates means <a href="#derivatives">we know what the first and second derivatives are</a>, and so
@@ -4141,12 +4141,12 @@ function kappa(t, B):
<p>
That was easy! (Well okay, that "not a number" value will need to be taken into account by downstream code, but that's a reality of
programming anwyay)
programming anyway)
</p>
<p>
With all of that covered, let's line up some curves! The following graphic gives you two curves that look identical, but use quadratic and
cubic functions, respectively. As you can see, despite their derivatives being necessarily different, their curvature (thanks to being
derived based on maths that "ignores" specific function derivative, and instead gives a formulat that smooths out any differences) is
derived based on maths that "ignores" specific function derivative, and instead gives a formula that smooths out any differences) is
exactly the same. And because of that, we can put them together such that the point where they overlap has the same curvature for both
curves, giving us the smoothest transition.
</p>
@@ -4545,7 +4545,7 @@ lli = function(line1, line2):
a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that <code>C</code>.
</li>
<li>
for both qudratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
for both quadratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
Casteljau's algorithm: in the last step, we find <code>B</code> at <code>(1-t) * e1 + t * e2</code>.
</li>
<li>
@@ -4591,7 +4591,7 @@ lli = function(line1, line2):
<img class="LaTeX SVG" src="./images/chapters/abc/c018aab3952ea9193848564aab12b241.svg" width="223px" height="41px" loading="lazy" />
<p>
Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a
<code>t</code> value, we can <em>contruct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>t</code> value, we can <em>construct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>u(t)</code> function, and once we have <code>C</code>, we can use our on-curve point (<code>B</code>) and the
<code>ratio(t)</code> function to find <code>A</code>:
</p>
@@ -4734,7 +4734,7 @@ lli = function(line1, line2):
<label>Fitting a quadratic Bézier curve</label>
</fallback-image></graphics-element
>
<p>That looks perfectly servicable!</p>
<p>That looks perfectly serviceable!</p>
<p>
Of course, we can take this one step further: we can't just "create" curves, we also have (almost!) all the tools available to "mold"
curves, where we can reshape a curve by dragging a point on the curve around while leaving the start and end fixed, effectively molding
@@ -4844,7 +4844,7 @@ for (coordinate, index) in LUT:
</p>
<p>
For quadratic curve, this is a really simple trick: we project our cursor onto the curve, which gives us a <code>t</code> value and
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "whever the cursor is" as target
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "wherever the cursor is" as target
<code>B</code>, we can compute the associated <code>C</code>:
</p>
<img class="LaTeX SVG" src="./images/chapters/molding/70262c533569a7da06cc1b950e932d6f.svg" width="248px" height="24px" loading="lazy" />
@@ -4888,7 +4888,7 @@ for (coordinate, index) in LUT:
<p>
One way to combat this might be to combine the above approach with the approach from the
<a href="#pointcurves">creating curves</a> section: generate both the "unchanged <code>t</code>/<code>e1</code>/<code>e2</code>" curve, as
well as the "idealised" curve through the start/cursor/end points, with idealised <code>t</code> value, and then interpolating between
well as the "idealized" curve through the start/cursor/end points, with idealized <code>t</code> value, and then interpolating between
those two curves:
</p>
<graphics-element
@@ -4909,8 +4909,8 @@ for (coordinate, index) in LUT:
<p>
The slide controls the "falloff distance" relative to where the original point on the curve is, so that as we drag our point around, it
interpolates with a bias towards "preserving <code>t</code>/<code>e1</code>/<code>e2</code>" closer to the original point, and bias
towards "idealised" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealised curve. We don't even try to interpolate at that point.
towards "idealized" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealized curve. We don't even try to interpolate at that point.
</p>
<p>
A more advanced way to try to smooth things out is to implement <em>continuous</em> molding, where we constantly update the curve as we
@@ -5225,7 +5225,7 @@ for (coordinate, index) in LUT:
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be
writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you
are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to pre-specified
coordinates.
</p>
<p>
@@ -5261,7 +5261,7 @@ for (coordinate, index) in LUT:
<p>
Taking an excursion to different splines, the other common design curve is the
<a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom spline</a>, which unlike Bézier curves
pass <em>through</em> each control point, so they offer a kind of "nuilt-in" curve fitting.
pass <em>through</em> each control point, so they offer a kind of "built-in" curve fitting.
</p>
<p>
In fact, let's start with just playing with one: the following graphic has a predefined curve that you manipulate the points for, and lets
@@ -5295,7 +5295,7 @@ for (coordinate, index) in LUT:
<p>
One downside of this is that—as you may have noticed from the graphic—the first and last point of the overall curve don't actually join up
with the rest of the curve: they don't have a previous/next point respectively, and so there is no way to calculate what their tangent
should be. Which also makes it rather tricky to fit a Camull-Rom curve to three points like we were able to do for Bézier curves. More on
should be. Which also makes it rather tricky to fit a Catmull-Rom curve to three points like we were able to do for Bézier curves. More on
that in <a href="#catmullfitting">the next section</a>.
</p>
<p>
@@ -5390,14 +5390,14 @@ for p = 1 to points.length-3 (inclusive):
<p>
Now, since a Catmull-Rom curve is a form of <a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline">cubic Hermite spline</a>, and as
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can
losslessly convert between the two, and the maths for doing so is surprisingly simple!
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can convert
one to the other and back, and the maths for doing so is surprisingly simple!
</p>
<p>The main difference between Catmull-Rom curves and Bézier curves is "what the points mean":</p>
<ul>
<li>
A cubic Bézier curve is defined by a start point, a control point that implies the tangent at the start, a control point that implies
the tangent at the end, and an end point, plus a characterising matrix that we can multiply by that point vector to get on-curve
the tangent at the end, and an end point, plus a characterizing matrix that we can multiply by that point vector to get on-curve
coordinates.
</li>
<li>
@@ -6921,8 +6921,8 @@ for(let L = 1; L <= order; L++) {
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's
"Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a
ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control pointl, just
like for rational Bézier curves.
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control point, just like
for rational Bézier curves.
</p>
<graphics-element title="A (closed) rational, uniform B-Spline" width="400" height="400" src="./chapters/bsplines/rational-uniform.js">
<fallback-image>

View File

@@ -27,7 +27,7 @@
<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="Thu Sep 17 2020 17:00:00 +00:00" />
<meta property="og:updated_time" content="Sat Sep 26 2020 15:21:50 +00:00" />
<meta property="og:updated_time" content="Sat Sep 26 2020 15:53:06 +00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />

View File

@@ -26,7 +26,7 @@
<meta property="og:description" content="" />
<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="Sat Sep 26 2020 15:21:50 GMT-0700 (Pacific Daylight Time)" />
<meta property="og:published_time" content="Sat Sep 26 2020 15:53:06 GMT-0700 (Pacific Daylight Time)" />
<meta property="og:updated_time" content="" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />

View File

@@ -6,7 +6,7 @@
<atom:link href="https://pomax.github.io/bezierinfo" rel="self"></atom:link>
<description>News updates for the <a href="https://pomax.github.io/bezierinfo">primer on Bézier Curves</a> by Pomax</description>
<language>en-GB</language>
<lastBuildDate>Sat Sep 26 2020 15:21:50 +00:00</lastBuildDate>
<lastBuildDate>Sat Sep 26 2020 15:53:06 +00:00</lastBuildDate>
<image>
<url>https://pomax.github.io/bezierinfo/images/og-image.png</url>
<title>A Primer on Bézier Curves</title>

View File

@@ -33,7 +33,7 @@
<meta property="og:locale" content="zh-CN" />
<meta property="og:type" content="article" />
<meta property="og:published_time" content="2013-06-13T12:00:00+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:21:50+00:00" />
<meta property="og:updated_time" content="2020-09-26T22:53:06+00:00" />
<meta property="og:author" content="Mike 'Pomax' Kamermans" />
<meta property="og:section" content="Bézier Curves" />
<meta property="og:tag" content="Bézier Curves" />
@@ -2178,7 +2178,7 @@ function drawCurve(points[], t):
Rotating coordinates is actually very easy, if you know the rule for it. You might find it explained as "applying a
<a href="https://en.wikipedia.org/wiki/Rotation_matrix">rotation matrix</a>, which is what we'll look at here, too. Essentially, the
idea is to take the circles over which we can rotate, and simply "sliding the coordinates" over these circles by the desired angle. If
we want a quarter circle turn, we take the coordinate, slide it along the cirle by a quarter turn, and done.
we want a quarter circle turn, we take the coordinate, slide it along the circle by a quarter turn, and done.
</p>
<p>
To turn any point <i>(x,y)</i> into a rotated point <i>(x',y')</i> (over 0,0) by some angle φ, we apply this nice and easy computation:
@@ -2744,12 +2744,12 @@ generateRMFrames(steps) -> frames:
<p>
We can see that the easier formula only has two constants, rather than four, and only two expressions involving <code>t</code>, rather
than three: this makes things considerably easier to solve because it lets us use
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equasion.
<a href="https://www.wolframalpha.com/input/?i=t%5E3+%2B+pt+%2B+q">regular calculus</a> to find the values that satisfy the equation.
</p>
<p>
Now, there is one small hitch: as a cubic function, the solutions may be
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardona realised this,
centuries befor complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
<a href="https://en.wikipedia.org/wiki/Complex_number">complex numbers</a> rather than plain numbers... And Cardano realised this,
centuries before complex numbers were a well-understood and established part of number theory. His interpretation of them was "these
numbers are impossible but that's okay because they disappear again in later steps", allowing him to not think about them too much, but we
have it even easier: as we're trying to find the roots for display purposes, we don't even <em>care</em> about complex numbers: we're
going to simplify Cardano's approach just that tiny bit further by throwing away any solution that's not a plain number.
@@ -3109,7 +3109,7 @@ function getCubicRoots(pa, pb, pc, pd) {
</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.
derivative of the derivative (of the derivative), just as for cubic curves.
</p>
<h3>Quintic and higher order curves: finding numerical solutions</h3>
<p>
@@ -3130,7 +3130,7 @@ function getCubicRoots(pa, pb, pc, pd) {
step, the closer we'll get to that "perfect, precise" answer. And as it turns out, there is a really nice numerical root-finding
algorithm, called the <a href="https://en.wikipedia.org/wiki/Newton-Raphson">Newton-Raphson</a> root finding method (yes, after
<em><a href="https://en.wikipedia.org/wiki/Isaac_Newton">that</a></em> Newton), which we can make use of. The Newton-Raphson approach
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some intial value <code>x</code> (literally any value will
consists of taking our impossible-to-solve function <code>f(x)</code>, picking some initial value <code>x</code> (literally any value will
do), and calculating <code>f(x)</code>. We can think of that value as the "height" of the function at <code>x</code>. If that height is
zero, we're done, we have found a root. If it isn't, we calculate the tangent line at <code>f(x)</code> and calculate at which
<code>x</code> value <em>its</em> height is zero (which we've already seen is very easy). That will give us a new <code>x</code> and we
@@ -3568,7 +3568,7 @@ function getCubicRoots(pa, pb, pc, pd) {
curve does over the interval t=0 to t=1.
</p>
<p>
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school precalculus, you
For the full details, head over to the paper and read through sections 3 and 4. If you still remember your high school pre-calculus, you
can probably follow along with this paper, although you might have to read it a few times before all the bits "click".
</p>
</div>
@@ -3690,7 +3690,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<a href="zh-CN/index.html#yforx">Finding Y, given X</a>
</h1>
<p>
One common task that pops up in things like CSS work, or parametric equalisers, or image leveling, or any other number of applications
One common task that pops up in things like CSS work, or parametric equalizers, or image leveling, or any other number of applications
where Bézier curves are used as control curves in a way that there is really only ever one "y" value associated with one "x" value, you
might want to cut out the middle man, as it were, and compute "y" directly based on "x". After all, the function looks simple enough,
finding the "y" value should be simple too, right? Unfortunately, not really. However, it <em>is</em> possible and as long as you have
@@ -3719,7 +3719,7 @@ function getCubicRoots(pa, pb, pc, pd) {
<p>
Now, if you look more closely at that right graphic, you'll notice something interesting: if we treat the red line as "the x axis", then
the point where the function crosses our line is really just a root for the cubic function x(t) through a shifted "x-axis"... and
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubuc root finding - and not even the complicated
<a href="#extremities">we've already seen</a> how to calculate roots, so let's just run cubic root finding - and not even the complicated
cubic case either: because of the kind of curve we're starting with, we <em>know</em> there is only root, simplifying the code we need!
</p>
<p>First, let's look at the function for x(t):</p>
@@ -3788,7 +3788,7 @@ y = curve.get(t).y</textarea
</table>
<p>
So the procedure is fairly straight forward: pick an <code>x</code>, find the associted <code>t</code> value, evaluate our curve
So the procedure is fairly straight forward: pick an <code>x</code>, find the associated <code>t</code> value, evaluate our curve
<em>for</em> that <code>t</code> value, which gives us the curve's {x,y} coordinate, which means we know <code>y</code> for this
<code>x</code>. Move the slider for the following graphic to see this in action:
</p>
@@ -4025,8 +4025,8 @@ y = curve.get(t).y</textarea
<p>
However, there's a problem with this approach: if we think about this a little more, we realise that "what a curve looks like" and its
derivative values are pretty much entirely unrelated. After all, the section on <a href="#reordering">reordering curves</a> showed us that
the same looking curve can have an infinite number of curve expressions of arbitraryly high Bézier degree, and each of those will have
<em>widly</em> different derivative values.
the same looking curve can have an infinite number of curve expressions of arbitrarily high Bézier degree, and each of those will have
<em>wildly</em> different derivative values.
</p>
<p>
So what we really want is some kind of expression that's not based on any particular expression of <code>t</code>, but is based on
@@ -4070,7 +4070,7 @@ y = curve.get(t).y</textarea
</p>
<img class="LaTeX SVG" src="./images/chapters/curvature/afd8cb8b0fe291ff703752c1c9cc33d4.svg" width="239px" height="55px" loading="lazy" />
<p>
And while that's a litte more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
And while that's a little more verbose, it's still just as simple to work with as the first function: the curvature at some point on any
(and this cannot be overstated: <em>any</em>) curve is a ratio between the first and second derivative cross product, and something that
looks oddly similar to the standard Euclidean distance function. And nothing in these functions is hard to calculate either: for Bézier
curves, simply knowing our curve coordinates means <a href="#derivatives">we know what the first and second derivatives are</a>, and so
@@ -4115,12 +4115,12 @@ function kappa(t, B):
<p>
That was easy! (Well okay, that "not a number" value will need to be taken into account by downstream code, but that's a reality of
programming anwyay)
programming anyway)
</p>
<p>
With all of that covered, let's line up some curves! The following graphic gives you two curves that look identical, but use quadratic and
cubic functions, respectively. As you can see, despite their derivatives being necessarily different, their curvature (thanks to being
derived based on maths that "ignores" specific function derivative, and instead gives a formulat that smooths out any differences) is
derived based on maths that "ignores" specific function derivative, and instead gives a formula that smooths out any differences) is
exactly the same. And because of that, we can put them together such that the point where they overlap has the same curvature for both
curves, giving us the smoothest transition.
</p>
@@ -4519,7 +4519,7 @@ lli = function(line1, line2):
a point that we get by projecting A, through B, onto the line between the curve's start and end points: let's call that <code>C</code>.
</li>
<li>
for both qudratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
for both quadratic and cubic curves, two points <code>e1</code> and <code>e2</code>, which represent the single-to-last step in de
Casteljau's algorithm: in the last step, we find <code>B</code> at <code>(1-t) * e1 + t * e2</code>.
</li>
<li>
@@ -4565,7 +4565,7 @@ lli = function(line1, line2):
<img class="LaTeX SVG" src="./images/chapters/abc/c018aab3952ea9193848564aab12b241.svg" width="223px" height="41px" loading="lazy" />
<p>
Which now leaves us with some powerful tools: given thee points (start, end, and "some point on the curve"), as well as a
<code>t</code> value, we can <em>contruct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>t</code> value, we can <em>construct</em> curves: we can compute <code>C</code> using the start and end points, and our
<code>u(t)</code> function, and once we have <code>C</code>, we can use our on-curve point (<code>B</code>) and the
<code>ratio(t)</code> function to find <code>A</code>:
</p>
@@ -4708,7 +4708,7 @@ lli = function(line1, line2):
<label>Fitting a quadratic Bézier curve</label>
</fallback-image></graphics-element
>
<p>That looks perfectly servicable!</p>
<p>That looks perfectly serviceable!</p>
<p>
Of course, we can take this one step further: we can't just "create" curves, we also have (almost!) all the tools available to "mold"
curves, where we can reshape a curve by dragging a point on the curve around while leaving the start and end fixed, effectively molding
@@ -4818,7 +4818,7 @@ for (coordinate, index) in LUT:
</p>
<p>
For quadratic curve, this is a really simple trick: we project our cursor onto the curve, which gives us a <code>t</code> value and
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "whever the cursor is" as target
initial <code>B</code> coordinate. We don't even need the latter: with our <code>t</code> value and "wherever the cursor is" as target
<code>B</code>, we can compute the associated <code>C</code>:
</p>
<img class="LaTeX SVG" src="./images/chapters/molding/70262c533569a7da06cc1b950e932d6f.svg" width="248px" height="24px" loading="lazy" />
@@ -4862,7 +4862,7 @@ for (coordinate, index) in LUT:
<p>
One way to combat this might be to combine the above approach with the approach from the
<a href="#pointcurves">creating curves</a> section: generate both the "unchanged <code>t</code>/<code>e1</code>/<code>e2</code>" curve, as
well as the "idealised" curve through the start/cursor/end points, with idealised <code>t</code> value, and then interpolating between
well as the "idealized" curve through the start/cursor/end points, with idealized <code>t</code> value, and then interpolating between
those two curves:
</p>
<graphics-element
@@ -4883,8 +4883,8 @@ for (coordinate, index) in LUT:
<p>
The slide controls the "falloff distance" relative to where the original point on the curve is, so that as we drag our point around, it
interpolates with a bias towards "preserving <code>t</code>/<code>e1</code>/<code>e2</code>" closer to the original point, and bias
towards "idealised" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealised curve. We don't even try to interpolate at that point.
towards "idealized" form the further away we move our point, with anything that's further than our falloff distance simply
<em>being</em> the idealized curve. We don't even try to interpolate at that point.
</p>
<p>
A more advanced way to try to smooth things out is to implement <em>continuous</em> molding, where we constantly update the curve as we
@@ -5199,7 +5199,7 @@ for (coordinate, index) in LUT:
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be
writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you
are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified
anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to pre-specified
coordinates.
</p>
<p>
@@ -5235,7 +5235,7 @@ for (coordinate, index) in LUT:
<p>
Taking an excursion to different splines, the other common design curve is the
<a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom spline</a>, which unlike Bézier curves
pass <em>through</em> each control point, so they offer a kind of "nuilt-in" curve fitting.
pass <em>through</em> each control point, so they offer a kind of "built-in" curve fitting.
</p>
<p>
In fact, let's start with just playing with one: the following graphic has a predefined curve that you manipulate the points for, and lets
@@ -5269,7 +5269,7 @@ for (coordinate, index) in LUT:
<p>
One downside of this is that—as you may have noticed from the graphic—the first and last point of the overall curve don't actually join up
with the rest of the curve: they don't have a previous/next point respectively, and so there is no way to calculate what their tangent
should be. Which also makes it rather tricky to fit a Camull-Rom curve to three points like we were able to do for Bézier curves. More on
should be. Which also makes it rather tricky to fit a Catmull-Rom curve to three points like we were able to do for Bézier curves. More on
that in <a href="#catmullfitting">the next section</a>.
</p>
<p>
@@ -5364,14 +5364,14 @@ for p = 1 to points.length-3 (inclusive):
<p>
Now, since a Catmull-Rom curve is a form of <a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline">cubic Hermite spline</a>, and as
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can
losslessly convert between the two, and the maths for doing so is surprisingly simple!
cubic Bézier curves are <em>also</em> a form of cubic Hermite spline, we run into an interesting bit of maths programming: we can convert
one to the other and back, and the maths for doing so is surprisingly simple!
</p>
<p>The main difference between Catmull-Rom curves and Bézier curves is "what the points mean":</p>
<ul>
<li>
A cubic Bézier curve is defined by a start point, a control point that implies the tangent at the start, a control point that implies
the tangent at the end, and an end point, plus a characterising matrix that we can multiply by that point vector to get on-curve
the tangent at the end, and an end point, plus a characterizing matrix that we can multiply by that point vector to get on-curve
coordinates.
</li>
<li>
@@ -6895,8 +6895,8 @@ for(let L = 1; L <= order; L++) {
While it is true that this section on B-Splines is running quite long already, there is one more thing we need to talk about, and that's
"Rational" splines, where the rationality applies to the "ratio", or relative weights, of the control points themselves. By introducing a
ratio vector with weights to apply to each control point, we greatly increase our influence over the final curve shape: the more weight a
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control pointl, just
like for rational Bézier curves.
control point carries, the closer to that point the spline curve will lie, a bit like turning up the gravity of a control point, just like
for rational Bézier curves.
</p>
<graphics-element title="A (closed) rational, uniform B-Spline" width="400" height="400" src="./chapters/bsplines/rational-uniform.js">
<fallback-image>