diff --git a/docs/chapters/abc/content.en-GB.md b/docs/chapters/abc/content.en-GB.md
index 3dc17100..6c66bd75 100644
--- a/docs/chapters/abc/content.en-GB.md
+++ b/docs/chapters/abc/content.en-GB.md
@@ -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)}
diff --git a/docs/chapters/bsplines/content.en-GB.md b/docs/chapters/bsplines/content.en-GB.md
index 1bbd9bfd..c808e0ee 100644
--- a/docs/chapters/bsplines/content.en-GB.md
+++ b/docs/chapters/bsplines/content.en-GB.md
@@ -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.
To turn any point (x,y) into a rotated point (x',y') (over 0,0) by some angle φ, we apply this nice and easy computation:
@@ -2930,12 +2930,12 @@ generateRMFrames(steps) -> frames:
We can see that the easier formula only has two constants, rather than four, and only two expressions involving
Now, there is one small hitch: as a cubic function, the solutions may be
- complex numbers 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
+ complex numbers 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.
@@ -3295,7 +3295,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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.
@@ -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 Newton-Raphson root finding method (yes, after
that Newton), which we can make use of. The Newton-Raphson approach
- consists of taking our impossible-to-solve function
- 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".
- 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 is possible and as long as you have
@@ -3905,7 +3905,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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 how to calculate roots, so let's just run cubuc root finding - and not even the complicated
+ we've already seen 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):
- So the procedure is fairly straight forward: pick an t
, rather
than three: this makes things considerably easier to solve because it lets us use
- regular calculus to find the values that satisfy the equasion.
+ regular calculus to find the values that satisfy the equation.
Quintic and higher order curves: finding numerical solutions
f(x)
, picking some intial value x
(literally any value will
+ 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
@@ -3754,7 +3754,7 @@ function getCubicRoots(pa, pb, pc, pd) {
curve does over the interval t=0 to t=1.
x
, find the associted t
value, evaluate our curve
+ 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:
So what we really want is some kind of expression that's not based on any particular expression of t
, but is based on
@@ -4256,7 +4256,7 @@ y = curve.get(t).y
- 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: 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, and so @@ -4301,12 +4301,12 @@ function kappa(t, B):
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)
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.
@@ -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 thatC
.
e1
and e2
, which represent the single-to-last step in de
+ 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
.
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
+ 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
:
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 @@ -5004,7 +5004,7 @@ for (coordinate, index) in LUT:
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
+ 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
:
One way to combat this might be to combine the above approach with the approach from the
creating curves 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
+ well as the "idealized" curve through the start/cursor/end points, with idealized t
value, and then interpolating between
those two curves:
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.
+ 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 @@ -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.
@@ -5421,7 +5421,7 @@ for (coordinate, index) in LUT:
Taking an excursion to different splines, the other common design curve is the Catmull-Rom spline, which unlike Bézier curves - pass through each control point, so they offer a kind of "nuilt-in" curve fitting. + 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 @@ -5455,7 +5455,7 @@ for (coordinate, index) in LUT:
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 the next section.
@@ -5550,14 +5550,14 @@ for p = 1 to points.length-3 (inclusive):
Now, since a Catmull-Rom curve is a form of 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! + 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":
To turn any point (x,y) into a rotated point (x',y') (over 0,0) by some angle φ, we apply this nice and easy computation: @@ -2770,12 +2770,12 @@ generateRMFrames(steps) -> frames:
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 to find the values that satisfy the equasion.
+ regular calculus to find the values that satisfy the equation.
Now, there is one small hitch: as a cubic function, the solutions may be - complex numbers 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 + complex numbers 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. @@ -3135,7 +3135,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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.
@@ -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 Newton-Raphson root finding method (yes, after
that 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
+ 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
@@ -3594,7 +3594,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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 + 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".
@@ -3716,7 +3716,7 @@ function getCubicRoots(pa, pb, pc, pd) { 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 + 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 @@ -3745,7 +3745,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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 how to calculate roots, so let's just run cubuc root finding - and not even the complicated + we've already seen 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):
@@ -3814,7 +3814,7 @@ y = curve.get(t).y
- So the procedure is fairly straight forward: pick an x
, find the associted t
value, evaluate our curve
+ 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:
So what we really want is some kind of expression that's not based on any particular expression of t
, but is based on
@@ -4096,7 +4096,7 @@ y = curve.get(t).y
- 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: 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, and so @@ -4141,12 +4141,12 @@ function kappa(t, B):
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)
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.
@@ -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 thatC
.
e1
and e2
, which represent the single-to-last step in de
+ 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
.
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
+ 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
:
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 @@ -4844,7 +4844,7 @@ for (coordinate, index) in LUT:
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
+ 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
:
One way to combat this might be to combine the above approach with the approach from the
creating curves 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
+ well as the "idealized" curve through the start/cursor/end points, with idealized t
value, and then interpolating between
those two curves:
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.
+ 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 @@ -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.
@@ -5261,7 +5261,7 @@ for (coordinate, index) in LUT:
Taking an excursion to different splines, the other common design curve is the Catmull-Rom spline, which unlike Bézier curves - pass through each control point, so they offer a kind of "nuilt-in" curve fitting. + 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 @@ -5295,7 +5295,7 @@ for (coordinate, index) in LUT:
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 the next section.
@@ -5390,14 +5390,14 @@ for p = 1 to points.length-3 (inclusive):
Now, since a Catmull-Rom curve is a form of 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! + 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":
To turn any point (x,y) into a rotated point (x',y') (over 0,0) by some angle φ, we apply this nice and easy computation: @@ -2744,12 +2744,12 @@ generateRMFrames(steps) -> frames:
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 to find the values that satisfy the equasion.
+ regular calculus to find the values that satisfy the equation.
Now, there is one small hitch: as a cubic function, the solutions may be - complex numbers 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 + complex numbers 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. @@ -3109,7 +3109,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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.
@@ -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 Newton-Raphson root finding method (yes, after
that 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
+ 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
@@ -3568,7 +3568,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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 + 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".
@@ -3690,7 +3690,7 @@ function getCubicRoots(pa, pb, pc, pd) { 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 + 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 @@ -3719,7 +3719,7 @@ function getCubicRoots(pa, pb, pc, pd) {
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 how to calculate roots, so let's just run cubuc root finding - and not even the complicated + we've already seen 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):
@@ -3788,7 +3788,7 @@ y = curve.get(t).y
- So the procedure is fairly straight forward: pick an x
, find the associted t
value, evaluate our curve
+ 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:
So what we really want is some kind of expression that's not based on any particular expression of t
, but is based on
@@ -4070,7 +4070,7 @@ y = curve.get(t).y
- 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: 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, and so @@ -4115,12 +4115,12 @@ function kappa(t, B):
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)
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.
@@ -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 thatC
.
e1
and e2
, which represent the single-to-last step in de
+ 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
.
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
+ 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
:
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 @@ -4818,7 +4818,7 @@ for (coordinate, index) in LUT:
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
+ 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
:
One way to combat this might be to combine the above approach with the approach from the
creating curves 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
+ well as the "idealized" curve through the start/cursor/end points, with idealized t
value, and then interpolating between
those two curves:
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.
+ 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 @@ -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.
@@ -5235,7 +5235,7 @@ for (coordinate, index) in LUT:
Taking an excursion to different splines, the other common design curve is the Catmull-Rom spline, which unlike Bézier curves - pass through each control point, so they offer a kind of "nuilt-in" curve fitting. + 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 @@ -5269,7 +5269,7 @@ for (coordinate, index) in LUT:
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 the next section.
@@ -5364,14 +5364,14 @@ for p = 1 to points.length-3 (inclusive):
Now, since a Catmull-Rom curve is a form of 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! + 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":