mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-12 11:44:22 +02:00
Fix spelling mistakes (#122)
This commit is contained in:
committed by
Mike Kamermans
parent
7e1cefa73f
commit
d395c261bc
@@ -66,7 +66,7 @@ And that's it, all values found.
|
|||||||
|
|
||||||
<div className="note">
|
<div className="note">
|
||||||
|
|
||||||
Much like the *u(t)* function in the above note, the *ratio(t)* function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the *u(t)* function in that they both come rolling out of the same function evalution, explained over on [MathOverflow](http://mathoverflow.net/questions/122257/finding-the-formula-for-Bézier-curve-ratios-hull-point-point-baseline) by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow.
|
Much like the *u(t)* function in the above note, the *ratio(t)* function depends on whether we're looking at quadratic or cubic curves. Their form is intrinsically related to the *u(t)* function in that they both come rolling out of the same function evaluation, explained over on [MathOverflow](http://mathoverflow.net/questions/122257/finding-the-formula-for-Bézier-curve-ratios-hull-point-point-baseline) by Boris Zbarsky and myself. The ratio functions are the "s(t)" functions from the answers there, while the "u(t)" functions have the same name both here and on MathOverflow.
|
||||||
|
|
||||||
\[
|
\[
|
||||||
ratio(t)_{quadratic} = \left | \frac{t^2 + (1-t)^2 - 1}{t^2 + (1-t)^2} \right |
|
ratio(t)_{quadratic} = \left | \frac{t^2 + (1-t)^2 - 1}{t^2 + (1-t)^2} \right |
|
||||||
|
@@ -16,7 +16,7 @@ The following graphic shows this procedure with a different colour for each chor
|
|||||||
|
|
||||||
So, with the procedure on how to find a circle through three points, finding the arc through those points is straight-forward: pick one of the three points as start point, pick another as an end point, and the arc has to necessarily go from the start point, over the remaining point, to the end point.
|
So, with the procedure on how to find a circle through three points, finding the arc through those points is straight-forward: pick one of the three points as start point, pick another as an end point, and the arc has to necessarily go from the start point, over the remaining point, to the end point.
|
||||||
|
|
||||||
So how can we convert a Bezier curve into a (sequence of) circular arc(s)?
|
So how can we convert a Bézier curve into a (sequence of) circular arc(s)?
|
||||||
|
|
||||||
- Start at <em>t=0</em>
|
- Start at <em>t=0</em>
|
||||||
- Pick two points further down the curve at some value <em>m = t + n</em> and <em>e = t + 2n</em>
|
- Pick two points further down the curve at some value <em>m = t + n</em> and <em>e = t + 2n</em>
|
||||||
@@ -46,4 +46,4 @@ With that in place, all that's left now is to "restart" the procedure by treatin
|
|||||||
|
|
||||||
<Graphic title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawArcs} onKeyDown={this.props.onKeyDown} />
|
<Graphic title="Arc approximation of a Bézier curve" setup={this.setupCubic} draw={this.drawArcs} onKeyDown={this.props.onKeyDown} />
|
||||||
|
|
||||||
So... what is this good for? Obviously, If you're working with technologies that can't do curves, but can do lines and circles, then the answer is pretty straight-forward, but what else? There are some reasons why you might need this technique: using circular arcs means you can determine whether a coordinate lies "on" your curve really easily: simply compute the distance to each circular arc center, and if any of those are close to the arc radii, at an angle betwee the arc start and end: bingo, this point can be treated as lying "on the curve". Another benefit is that this approximation is "linear": you can almost trivially travel along the arcs at fixed speed. You can also trivially compute the arc length of the approximated curve (it's a bit like curve flattening). The only thing to bear in mind is that this is a lossy equivalence: things that you compute based on the approximation are guaranteed "off" by some small value, and depending on how much precision you need, arc approximation is either going to be super useful, or completely useless. It's up to you to decide which, based on your application!
|
So... what is this good for? Obviously, If you're working with technologies that can't do curves, but can do lines and circles, then the answer is pretty straight-forward, but what else? There are some reasons why you might need this technique: using circular arcs means you can determine whether a coordinate lies "on" your curve really easily: simply compute the distance to each circular arc center, and if any of those are close to the arc radii, at an angle between the arc start and end: bingo, this point can be treated as lying "on the curve". Another benefit is that this approximation is "linear": you can almost trivially travel along the arcs at fixed speed. You can also trivially compute the arc length of the approximated curve (it's a bit like curve flattening). The only thing to bear in mind is that this is a lossy equivalence: things that you compute based on the approximation are guaranteed "off" by some small value, and depending on how much precision you need, arc approximation is either going to be super useful, or completely useless. It's up to you to decide which, based on your application!
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
# B-Spline derivatives
|
# B-Spline derivatives
|
||||||
|
|
||||||
|
|
||||||
One last section specific to B-Splines: in order to apply the same procedures to B-Splines as we've looked at for BEzier curves, we'll need to know the first and second derivative. But... what is the derivative of a B-Spline?
|
One last section specific to B-Splines: in order to apply the same procedures to B-Splines as we've looked at for Bézier curves, we'll need to know the first and second derivative. But... what is the derivative of a B-Spline?
|
||||||
|
|
||||||
Thankfully, much like as was the case for Bezier curves, the derivative of a B-Spline is itself a (lower order) B-Spline. The following two functions specify the general B-Spline formula for a B-Spline of degree <em>d</em> with <em>n</em> points, and knot vector of length <em>d+n+1</em>, and its derivative:
|
Thankfully, much like as was the case for Bézier curves, the derivative of a B-Spline is itself a (lower order) B-Spline. The following two functions specify the general B-Spline formula for a B-Spline of degree <em>d</em> with <em>n</em> points, and knot vector of length <em>d+n+1</em>, and its derivative:
|
||||||
|
|
||||||
\[
|
\[
|
||||||
C(t) = \sum_{i=0}^n P_i \cdot N_{i,k}(t)
|
C(t) = \sum_{i=0}^n P_i \cdot N_{i,k}(t)
|
||||||
@@ -20,7 +20,7 @@ where
|
|||||||
\]
|
\]
|
||||||
|
|
||||||
|
|
||||||
So, much as for Bezier derivatives, we see a derivative function that is simply a new interpolation function, with interpolated weights. With this information, we can do things like draw tangents and normals, as well as determine the curvature function, draw inflection points, and all those lovely things.
|
So, much as for Bézier derivatives, we see a derivative function that is simply a new interpolation function, with interpolated weights. With this information, we can do things like draw tangents and normals, as well as determine the curvature function, draw inflection points, and all those lovely things.
|
||||||
|
|
||||||
As a concrete example, let's look at cubic (=degree 3) B-Spline with five coordinates, and with uniform knot vector of length 3 + 5 + 1 = 9:
|
As a concrete example, let's look at cubic (=degree 3) B-Spline with five coordinates, and with uniform knot vector of length 3 + 5 + 1 = 9:
|
||||||
|
|
||||||
|
@@ -19,13 +19,13 @@ In order to make this interpolation of curves work, the maths is necessarily mor
|
|||||||
|
|
||||||
## How to compute a B-Spline curve: some maths
|
## How to compute a B-Spline curve: some maths
|
||||||
|
|
||||||
Given a B-Spline of degree `d` and thus order `k=d+1` (so a quadratic B-Spline is degree 2 and order 3, a cubic B-Spline is degree 3 and order 4, etc) and `n` control points `P<sub>0</sub>` through `P<sub>n-1</sub>`, we can compute a point on the curve for some value `t` in the interval [0,1] (where 0 is the start of the curve, and 1 the end, just like for Bézier curves), by evaluting the following function:
|
Given a B-Spline of degree `d` and thus order `k=d+1` (so a quadratic B-Spline is degree 2 and order 3, a cubic B-Spline is degree 3 and order 4, etc) and `n` control points `P<sub>0</sub>` through `P<sub>n-1</sub>`, we can compute a point on the curve for some value `t` in the interval [0,1] (where 0 is the start of the curve, and 1 the end, just like for Bézier curves), by evaluating the following function:
|
||||||
|
|
||||||
\[
|
\[
|
||||||
Point(t) = \sum^n_{i=0} P_i \cdot N_{i,k}(t)
|
Point(t) = \sum^n_{i=0} P_i \cdot N_{i,k}(t)
|
||||||
\]
|
\]
|
||||||
|
|
||||||
Which, honestly, doesn't tell us all that much. All we can see is that a point on a B-Spline curve is defined as "a mix of all the control points, weighted somehow", where the weighting is achieved through the *N(...)* function, subscipted with an obvious parameter `i`, which comes from our summation, and some magical parameter `k`. So we need to know two things: 1. what does N(t) do, and 2. what is that `k`? Let's cover both, in reverse order.
|
Which, honestly, doesn't tell us all that much. All we can see is that a point on a B-Spline curve is defined as "a mix of all the control points, weighted somehow", where the weighting is achieved through the *N(...)* function, subscripted with an obvious parameter `i`, which comes from our summation, and some magical parameter `k`. So we need to know two things: 1. what does N(t) do, and 2. what is that `k`? Let's cover both, in reverse order.
|
||||||
|
|
||||||
The parameter `k` represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or tops influencing the total curvature as a "knot on the curve".
|
The parameter `k` represents the "knot interval" over which a section of curve is defined. As we learned earlier, a B-Spline curve is itself an interpoliation of curves, and we can treat each transition where a control point starts or tops influencing the total curvature as a "knot on the curve".
|
||||||
Doing so for a degree `d` B-Spline with `n` control point gives us `d + n + 1` knots, defining `d + n` intervals along the curve, and it is these intervals that the above `k` subscript to the N() function applies to.
|
Doing so for a degree `d` B-Spline with `n` control point gives us `d + n + 1` knots, defining `d + n` intervals along the curve, and it is these intervals that the above `k` subscript to the N() function applies to.
|
||||||
@@ -94,9 +94,9 @@ Thanks to Cox and de Boor, we can compute points on a B-Spline pretty easily: we
|
|||||||
|
|
||||||
That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal!
|
That is, we compute d(3,3) as a mixture of d(2,3) and d(2,2): d(3,3) = a(3,3) x d(2,3) + (1-a(3,3)) x d(2,2)... and we simply keep expanding our triangle until we reach the terminating function parameters. Done deal!
|
||||||
|
|
||||||
One thing we need to keep in mind is that we're working with a spline that is contrained by its control points, so even though the `d(..., k)` values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential!
|
One thing we need to keep in mind is that we're working with a spline that is constrained by its control points, so even though the `d(..., k)` values are zero or one at the lowest level, they are really "zero or one, times their respective control point", so in the next section you'll see the algorithm for running through the computation in a way that starts with a copy of the control point vector and then works its way up to that single point: that's pretty essential!
|
||||||
|
|
||||||
If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplcations, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!
|
If we run this computation "down", starting at d(3,3), then without special code in place we would be computing quite a few terms multiple times at each step. On the other hand, we can also start with that last "column", we can generate the terminating d() values first, then compute the a() constants, perform our multiplications, generate the previous step's d() values, compute their a() constants, do the multiplications, etc. until we end up all the way back at the top. If we run our computation this way, we don't need any explicit caching, we can just "recycle" the list of numbers we start with and simply update them as we move up the triangle. So, let's implement that!
|
||||||
|
|
||||||
## Cool, cool... but I don't know what to do with that information
|
## Cool, cool... but I don't know what to do with that information
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ Of course if we want to manipulate these kind of curves we need to make sure to
|
|||||||
The most important thing to understand when it comes to B-Splines is that they work *because* of the concept of a knot vector. As mentioned above, knots represent "where individual control points start/stop influencing the curve", but we never looked at the *values* that go in the knot vector. If you look back at the N() and a() functions, you see that interpolations are based on intervals in the knot vector, rather than the actual values in the knot vector, and we can exploit this to do some pretty interesting things with clever manipulation of the knot vector. Specifically there are four things we can do that are worth looking at:
|
The most important thing to understand when it comes to B-Splines is that they work *because* of the concept of a knot vector. As mentioned above, knots represent "where individual control points start/stop influencing the curve", but we never looked at the *values* that go in the knot vector. If you look back at the N() and a() functions, you see that interpolations are based on intervals in the knot vector, rather than the actual values in the knot vector, and we can exploit this to do some pretty interesting things with clever manipulation of the knot vector. Specifically there are four things we can do that are worth looking at:
|
||||||
|
|
||||||
1. we can use a uniform knot vector, with equally spaced intervals,
|
1. we can use a uniform knot vector, with equally spaced intervals,
|
||||||
2. we can use a non-uniform knot vector, without enforcing equally spaced internvals,
|
2. we can use a non-uniform knot vector, without enforcing equally spaced intervals,
|
||||||
3. we can collapse sequential knots to the same value, locally lowering curve complexity using "null" intervals, and
|
3. we can collapse sequential knots to the same value, locally lowering curve complexity using "null" intervals, and
|
||||||
4. we can form a special case non-uniform vector, by combining (1) and (3) to for a vector with collapsed start and end knots, with a uniform vector in between.
|
4. we can form a special case non-uniform vector, by combining (1) and (3) to for a vector with collapsed start and end knots, with a uniform vector in between.
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ For any curve of degree `D` with control points `N`, we can define a knot vector
|
|||||||
|
|
||||||
### Non-uniform B-Splines
|
### Non-uniform B-Splines
|
||||||
|
|
||||||
This is essentialy the "free form" version of a B-Spline, and also the least interesting to look at, as without any specific reason to pick specific knot intervals, there is nothing particularly interesting going on. There is one constraint to the knot vector, and that is that any value `knots[k+1]` should be equal to, or greater than `knots[k]`.
|
This is essentially the "free form" version of a B-Spline, and also the least interesting to look at, as without any specific reason to pick specific knot intervals, there is nothing particularly interesting going on. There is one constraint to the knot vector, and that is that any value `knots[k+1]` should be equal to, or greater than `knots[k]`.
|
||||||
|
|
||||||
## One last thing: Rational B-Splines
|
## One last thing: Rational B-Splines
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ The first observation that makes things work is that if we have a cubic curve wi
|
|||||||
|
|
||||||
This is a fairly funky image, so let's see how it breaks down. We see the three fixed points at (0,0), (0,1) and (1,1), and then the fourth point is somewhere. Depending on where it is, our curve will have certain features. Namely, if the fourth point is...
|
This is a fairly funky image, so let's see how it breaks down. We see the three fixed points at (0,0), (0,1) and (1,1), and then the fourth point is somewhere. Depending on where it is, our curve will have certain features. Namely, if the fourth point is...
|
||||||
|
|
||||||
1. anywhere on and in the red zone, the curve will be self-intersecting, yielding either a cusp or a loop. Anywhere inside the the red zone, this will be a loop. We won't know <i>where</i> that loop is (in terms of <i>t</i> values), but we are guaranteed that there is one.
|
1. anywhere on and in the red zone, the curve will be self-intersecting, yielding either a cusp or a loop. Anywhere inside the red zone, this will be a loop. We won't know <i>where</i> that loop is (in terms of <i>t</i> values), but we are guaranteed that there is one.
|
||||||
2. on the left (red) edge, the curve will have a cusp. We again don't know <em>where</em>, just that it
|
2. on the left (red) edge, the curve will have a cusp. We again don't know <em>where</em>, just that it
|
||||||
has one. This edge is described by the function:
|
has one. This edge is described by the function:
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ For the full details, head over to the paper and read through sections 3 and 4.
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
So now the question becomes: how do we manipulate our curve so that it fits this canonical form, with three fixed points, and one "free" point? Enter linear algerba. Don't worry, I'll be doing all the math for you, as well as show you what the effect is on our curves, but basically we're going to be using linear algebra, rather than calculus, because "it's way easier". Sometimes a calculus approach is very hard to work with, when the equivalent geometrical solution is super obvious.
|
So now the question becomes: how do we manipulate our curve so that it fits this canonical form, with three fixed points, and one "free" point? Enter linear algebra. Don't worry, I'll be doing all the math for you, as well as show you what the effect is on our curves, but basically we're going to be using linear algebra, rather than calculus, because "it's way easier". Sometimes a calculus approach is very hard to work with, when the equivalent geometrical solution is super obvious.
|
||||||
|
|
||||||
The approach is going to start with a curve that doesn't have all-colinear points (so we need to make sure the points don't all fall on a straight line), and then applying four graphics operations that you will probably have heard of: translation (moving all points by some fixed x- and y-distance), scaling (multiplying all points by some x and y scale factor), and shearing (an operation that turns rectangles into parallelograms).
|
The approach is going to start with a curve that doesn't have all-colinear points (so we need to make sure the points don't all fall on a straight line), and then applying four graphics operations that you will probably have heard of: translation (moving all points by some fixed x- and y-distance), scaling (multiplying all points by some x and y scale factor), and shearing (an operation that turns rectangles into parallelograms).
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ Running all our coordinates through this transformation gives a new set of coord
|
|||||||
\right ]
|
\right ]
|
||||||
\]
|
\]
|
||||||
|
|
||||||
So we want some shearing value that, when multiplied by <i>y</i>, yields <i>-x</i>, so our x coordinate becomes zero. That value is simpy <i>-x/y</i>, because <i>-x/y * y = -x</i>. Done:
|
So we want some shearing value that, when multiplied by <i>y</i>, yields <i>-x</i>, so our x coordinate becomes zero. That value is simply <i>-x/y</i>, because <i>-x/y * y = -x</i>. Done:
|
||||||
|
|
||||||
\[
|
\[
|
||||||
T_2 =
|
T_2 =
|
||||||
@@ -274,7 +274,7 @@ That's kind of super-simple to write out in code, I think you'll agree. Coding m
|
|||||||
|
|
||||||
Doing maths can be a pain, so whenever possible, I like to make computers do the work for me. Especially for things like this, I simply use [Mathematica](http://www.wolfram.com/mathematica). Tracking all this math by hand is insane, and we invented computers, literally, to do this for us. I have no reason to use pen and paper when I can write out what I want to do in a program, and have the program do the math for me. And real math, too, with symbols, not with numbers. In fact, [here's](http://pomax.github.io/gh-weblog/downloads/canonical-curve.nb) the Mathematica notebook if you want to see how this works for yourself.
|
Doing maths can be a pain, so whenever possible, I like to make computers do the work for me. Especially for things like this, I simply use [Mathematica](http://www.wolfram.com/mathematica). Tracking all this math by hand is insane, and we invented computers, literally, to do this for us. I have no reason to use pen and paper when I can write out what I want to do in a program, and have the program do the math for me. And real math, too, with symbols, not with numbers. In fact, [here's](http://pomax.github.io/gh-weblog/downloads/canonical-curve.nb) the Mathematica notebook if you want to see how this works for yourself.
|
||||||
|
|
||||||
Now, I know, you're thinking "but Mathematica is super expensive!" and that's true, it's [$295 for home use](http://www.wolfram.com/mathematica-home-edition), but it's **also** [free when you buy a $35 raspberry pi](http://www.wolfram.com/raspberry-pi). Obviously, I bought a raspberry pi, and I encourage you to do the same. With that, as long as you know what you want to *do*, Mathematica can just do it for you. And we don't have to be geniusses to work out what the maths looks like. That's what we have computers for.
|
Now, I know, you're thinking "but Mathematica is super expensive!" and that's true, it's [$295 for home use](http://www.wolfram.com/mathematica-home-edition), but it's **also** [free when you buy a $35 raspberry pi](http://www.wolfram.com/raspberry-pi). Obviously, I bought a raspberry pi, and I encourage you to do the same. With that, as long as you know what you want to *do*, Mathematica can just do it for you. And we don't have to be geniuses to work out what the maths looks like. That's what we have computers for.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
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). Now, a Catmull-Rom spline is a form of cubic Hermite spline, and as it so happens the cubic Bézier curve is also a cubic Hermite spline, so maybe... maybe we can convert one into the other, and back, with some simple substitutions?
|
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). Now, a Catmull-Rom spline is a form of cubic Hermite spline, and as it so happens the cubic Bézier curve is also a cubic Hermite spline, so maybe... maybe we can convert one into the other, and back, with some simple substitutions?
|
||||||
|
|
||||||
Unlike Bézier curves, Catmull-Rom splines pass through each point used to define the curve, except the first and last, which makes sense if you read the "natural language" descriptionfor how a Catmull-Rom spline works: a Catmull-Rom spline is a curve that, at each point P<sub>x</sub>, has a tangent along the line P<sub>x-1</sub> to P<sub>x+1</sub>. The curve runs from points P<sub>2</sub> to P<sub>n-1</sub>, and has a "tension" that determines how fast the curve passes through each point. The lower the tension, the faster the curve goes through each point, and the bigger its local tangent is.
|
Unlike Bézier curves, Catmull-Rom splines pass through each point used to define the curve, except the first and last, which makes sense if you read the "natural language" description for how a Catmull-Rom spline works: a Catmull-Rom spline is a curve that, at each point P<sub>x</sub>, has a tangent along the line P<sub>x-1</sub> to P<sub>x+1</sub>. The curve runs from points P<sub>2</sub> to P<sub>n-1</sub>, and has a "tension" that determines how fast the curve passes through each point. The lower the tension, the faster the curve goes through each point, and the bigger its local tangent is.
|
||||||
|
|
||||||
I'll be showing the conversion to and from Catmull-Rom curves for the tension that the Processing language uses for its Catmull-Rom algorithm.
|
I'll be showing the conversion to and from Catmull-Rom curves for the tension that the Processing language uses for its Catmull-Rom algorithm.
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ So let's find out which transformation matrix we need in order to convert from C
|
|||||||
\end{bmatrix}
|
\end{bmatrix}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
The difference is somewhere in the actual hermite matrix, since the <em>t</em> and coordinate values are identical, so let's solve that matrix equasion:
|
The difference is somewhere in the actual Hermite matrix, since the <em>t</em> and coordinate values are identical, so let's solve that matrix equation:
|
||||||
|
|
||||||
\[
|
\[
|
||||||
\frac{1}{2}
|
\frac{1}{2}
|
||||||
|
@@ -12,7 +12,7 @@ So have a graphical look at a "bad" guess versus the true fit, where we'll be us
|
|||||||
|
|
||||||
We see two curves here; in blue, our "guessed" curve and its control points, and in grey/black, the true curve fit, with proper control points that were shifted in, along line between our guessed control points, such that the derivatives at the start and end points are correct.
|
We see two curves here; in blue, our "guessed" curve and its control points, and in grey/black, the true curve fit, with proper control points that were shifted in, along line between our guessed control points, such that the derivatives at the start and end points are correct.
|
||||||
|
|
||||||
We can already seethat cubic curves are a lot better than quadratic curves, and don't look all that wrong until we go well past a quarter circle; ⅜th starts to hint at problems, and half a circle has an obvious "gap" between the real circle and the cubic approximation. Anything past that just looks plain ridiculous... but quarter curves actually look pretty okay!
|
We can already see that cubic curves are a lot better than quadratic curves, and don't look all that wrong until we go well past a quarter circle; ⅜th starts to hint at problems, and half a circle has an obvious "gap" between the real circle and the cubic approximation. Anything past that just looks plain ridiculous... but quarter curves actually look pretty okay!
|
||||||
|
|
||||||
So, maths time again: how okay is "okay"? Let's apply some more maths to find out.
|
So, maths time again: how okay is "okay"? Let's apply some more maths to find out.
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ where "a" is some scaling factor, and:
|
|||||||
|
|
||||||
where "b" is also some scaling factor.
|
where "b" is also some scaling factor.
|
||||||
|
|
||||||
Starting with this information, we slowly maths our way to success, but I won't lie: the maths for this is pretty trig-heavy, and it's easy to get lost if you remember (or know!) some of the core trigonoetric identities, so if you just want to see the final result just skip past the next section!
|
Starting with this information, we slowly maths our way to success, but I won't lie: the maths for this is pretty trig-heavy, and it's easy to get lost if you remember (or know!) some of the core trigonometric identities, so if you just want to see the final result just skip past the next section!
|
||||||
|
|
||||||
<div className="note">
|
<div className="note">
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ Starting with this information, we slowly maths our way to success, but I won't
|
|||||||
|
|
||||||
Unlike for the quadratic case, we need some more information in order to compute <i>a</i> and <i>b</i>, since they're no longer dependent variables. First, we observe that the curve is symmetrical, so whatever values we end up finding for C<sub>1</sub> will apply to C<sub>2</sub> as well (rotated along its tangent), so we'll focus on finding the location of C<sub>1</sub> only. So here's where we do something that you might not expect: we're going to ignore for a moment, because we're going to have a much easier time if we just solve this problem with geometry first, then move to calculus to solve a much simpler problem.
|
Unlike for the quadratic case, we need some more information in order to compute <i>a</i> and <i>b</i>, since they're no longer dependent variables. First, we observe that the curve is symmetrical, so whatever values we end up finding for C<sub>1</sub> will apply to C<sub>2</sub> as well (rotated along its tangent), so we'll focus on finding the location of C<sub>1</sub> only. So here's where we do something that you might not expect: we're going to ignore for a moment, because we're going to have a much easier time if we just solve this problem with geometry first, then move to calculus to solve a much simpler problem.
|
||||||
|
|
||||||
If we look at the triangle that is formed between our starting point, or initial guess C<sub>1</sub> and our real C<sub>1</sub>, there's something funny going on: if we treat the line {start,guess} as our opposite side, the line {guess,real} as our adjacent side, with {start,real} our hypothenuse, then the angle for the corner hypothenuse/adjacent is half that of the arc we're covering. Try it: if you place the end point at a quarter circle (pi/2, or 90 degrees), the angle in our triangle is half a quarter (pi/4, or 45 degrees). With that knowledge, and a knowledge of what the length of any of our lines segments are (as a function), we can determine where our control points are, and thus have everything we need to find the error distance function. Of the three lines, the one we can easiest determine is {start,guess}, so let's find out what the guessed control point is. Again geometrically, because we have the benefit of an on-curve <i>t=0.5</i> value.
|
If we look at the triangle that is formed between our starting point, or initial guess C<sub>1</sub> and our real C<sub>1</sub>, there's something funny going on: if we treat the line {start,guess} as our opposite side, the line {guess,real} as our adjacent side, with {start,real} our hypotenuse, then the angle for the corner hypotenuse/adjacent is half that of the arc we're covering. Try it: if you place the end point at a quarter circle (pi/2, or 90 degrees), the angle in our triangle is half a quarter (pi/4, or 45 degrees). With that knowledge, and a knowledge of what the length of any of our lines segments are (as a function), we can determine where our control points are, and thus have everything we need to find the error distance function. Of the three lines, the one we can easiest determine is {start,guess}, so let's find out what the guessed control point is. Again geometrically, because we have the benefit of an on-curve <i>t=0.5</i> value.
|
||||||
|
|
||||||
The distance from our guessed point to the start point is exactly the same as the projection distance we looked at earlier. Using <i>t=0.5</i> as our point "B" in the "A,B,C" projection, then we know the length of the line segment {C,A}, since it's d<sub>1</sub> = {A,B} + d<sub>2</sub> = {B,C}:
|
The distance from our guessed point to the start point is exactly the same as the projection distance we looked at earlier. Using <i>t=0.5</i> as our point "B" in the "A,B,C" projection, then we know the length of the line segment {C,A}, since it's d<sub>1</sub> = {A,B} + d<sub>2</sub> = {B,C}:
|
||||||
|
|
||||||
|
@@ -58,7 +58,7 @@ Now, the trick is to turn this expression into something that has binomial coeff
|
|||||||
\end{array}
|
\end{array}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
And that's the first part done: the two components inside the parentheses are actually regular, lower order Bezier expressions:
|
And that's the first part done: the two components inside the parentheses are actually regular, lower order Bézier expressions:
|
||||||
|
|
||||||
\[\begin{array}{l}
|
\[\begin{array}{l}
|
||||||
... = n \left (
|
... = n \left (
|
||||||
@@ -70,7 +70,7 @@ And that's the first part done: the two components inside the parentheses are ac
|
|||||||
\end{array}
|
\end{array}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
Now to apply this to our weighted Bezier curves. We'll write out the plain curve formula that we saw earlier, and then work our way through to its derivative:
|
Now to apply this to our weighted Bézier curves. We'll write out the plain curve formula that we saw earlier, and then work our way through to its derivative:
|
||||||
|
|
||||||
\[\begin{array}{lcl}
|
\[\begin{array}{lcl}
|
||||||
Bézier_{n,k}(t) &=& B_{n,0}(t) \cdot w_0 + B_{n,1}(t) \cdot w_1 + B_{n,2}(t) \cdot w_2 + B_{n,3}(t) \cdot w_3 + ... \\
|
Bézier_{n,k}(t) &=& B_{n,0}(t) \cdot w_0 + B_{n,1}(t) \cdot w_1 + B_{n,2}(t) \cdot w_2 + B_{n,3}(t) \cdot w_3 + ... \\
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Graduated curve offsetting
|
# Graduated curve offsetting
|
||||||
|
|
||||||
What if we want to do graduated offsetting, starting at some distance `s` but ending at some other distance `e`? well, if we can compute the length of a curve (which we can if we use the Legendre-Gauss quadrature approach) then we can also determine how far "along the line" any point on the curve is. With that knowledge, we can offset a curve so that its offset curve is not uniformly wide, but graduated between with two different offset widths at the start and end.
|
What if we want to do graduated offsetting, starting at some distance `s` but ending at some other distance `e`? Well, if we can compute the length of a curve (which we can if we use the Legendre-Gauss quadrature approach) then we can also determine how far "along the line" any point on the curve is. With that knowledge, we can offset a curve so that its offset curve is not uniformly wide, but graduated between with two different offset widths at the start and end.
|
||||||
|
|
||||||
Like normal offsetting we cut up our curve in sub-curves, and then check at which distance along the original curve each sub-curve starts and ends, as well as to which point on the curve each of the control points map. This gives us the distance-along-the-curve for each interesting point in the sub-curve. If we call the total length of all sub-curves seen prior to seeing "the current" sub-curve `S` (and if the current sub-curve is the first one, `S` is zero), and we call the full length of our original curve `L`, then we get the following graduation values:
|
Like normal offsetting we cut up our curve in sub-curves, and then check at which distance along the original curve each sub-curve starts and ends, as well as to which point on the curve each of the control points map. This gives us the distance-along-the-curve for each interesting point in the sub-curve. If we call the total length of all sub-curves seen prior to seeing "the current" sub-curve `S` (and if the current sub-curve is the first one, `S` is zero), and we call the full length of our original curve `L`, then we get the following graduation values:
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ What we're saying here is that given the curvature function *C(t)*, we want to k
|
|||||||
|
|
||||||
So the function *C(t)* is wholly defined by the first and second derivative functions for the parametric dimensions of our curve. And as already shown, derivatives of Bézier curves are just simpler Bézier curves, with very easy to compute new coefficients, so this should be pretty easy.
|
So the function *C(t)* is wholly defined by the first and second derivative functions for the parametric dimensions of our curve. And as already shown, derivatives of Bézier curves are just simpler Bézier curves, with very easy to compute new coefficients, so this should be pretty easy.
|
||||||
|
|
||||||
However as we've seen in the section on aligning, aligning lets us simplify things *a lot*, by completely removing the contributions of the first coordinate from most mathematical evaluations, and removing the last *y* coordinate as well by virtue of the last point lying on the x-axis. So, while we can evaluate *C(t) = 0* for our curve, it'll be much easier to first axis-align the curve and *then* evalutating the curvature function.
|
However as we've seen in the section on aligning, aligning lets us simplify things *a lot*, by completely removing the contributions of the first coordinate from most mathematical evaluations, and removing the last *y* coordinate as well by virtue of the last point lying on the x-axis. So, while we can evaluate *C(t) = 0* for our curve, it'll be much easier to first axis-align the curve and *then* evaluating the curvature function.
|
||||||
|
|
||||||
<div className="note">
|
<div className="note">
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ This is a plain quadratic curve, and we know how to solve *C(t) = 0*; we use the
|
|||||||
\ C(t) = 0 \ \Rightarrow\ t = \frac{-y \pm \sqrt{y^2 - 4 x z}}{2x}
|
\ C(t) = 0 \ \Rightarrow\ t = \frac{-y \pm \sqrt{y^2 - 4 x z}}{2x}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
We can easily compute this value *if* the descriminator isn't a negative number (because we only want real roots, not complex roots), and *if* *x* is not zero, because divisions by zero are rather useless.
|
We can easily compute this value *if* the discriminator isn't a negative number (because we only want real roots, not complex roots), and *if* *x* is not zero, because divisions by zero are rather useless.
|
||||||
|
|
||||||
Taking that into account, we compute *t*, we disregard any *t* value that isn't in the Bézier interval [0,1], and we now know at which *t* value(s) our curve will inflect.
|
Taking that into account, we compute *t*, we disregard any *t* value that isn't in the Bézier interval [0,1], and we now know at which *t* value(s) our curve will inflect.
|
||||||
|
|
||||||
|
@@ -113,7 +113,7 @@ We can perform the same trick for the quadratic curve, in which case we end up w
|
|||||||
\end{bmatrix}
|
\end{bmatrix}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
If we plug in a `t` value, and then multiply the matrices, we will get exactly the same values as when we evaluate the original polynomial function, or as when we evaluate the curve using progessive linear interpolation.
|
If we plug in a `t` value, and then multiply the matrices, we will get exactly the same values as when we evaluate the original polynomial function, or as when we evaluate the curve using progressive linear interpolation.
|
||||||
|
|
||||||
**So: why would we bother with matrices?** Matrix representations allow us to discover things about functions that would otherwise be hard to tell. It turns out that the curves form [triangular matrices](https://en.wikipedia.org/wiki/Triangular_matrix), and they have a determinant equal to the product of the actual coordinates we use for our curve. It's also invertible, which means there's [a ton of properties](https://en.wikipedia.org/wiki/Invertible_matrix#The_invertible_matrix_theorem) that are all satisfied. Of course, the main question is: "Why is this useful to us, now?", and the answer to that is that it's not immediately useful, but you'll be seeing some instances where certain curve properties can be either computed via function manipulation, or via clever use of matrices, and sometimes the matrix approach can be (drastically) faster.
|
**So: why would we bother with matrices?** Matrix representations allow us to discover things about functions that would otherwise be hard to tell. It turns out that the curves form [triangular matrices](https://en.wikipedia.org/wiki/Triangular_matrix), and they have a determinant equal to the product of the actual coordinates we use for our curve. It's also invertible, which means there's [a ton of properties](https://en.wikipedia.org/wiki/Invertible_matrix#The_invertible_matrix_theorem) that are all satisfied. Of course, the main question is: "Why is this useful to us, now?", and the answer to that is that it's not immediately useful, but you'll be seeing some instances where certain curve properties can be either computed via function manipulation, or via clever use of matrices, and sometimes the matrix approach can be (drastically) faster.
|
||||||
|
|
||||||
|
@@ -290,7 +290,7 @@ Excellent! Now we can form our new quadratic curve:
|
|||||||
|
|
||||||
***Brilliant***: if we want a subcurve from `t = 0` to `t = z`, we can keep the first coordinate the same (which makes sense), our control point becomes a z-ratio mixture of the original control point and the start point, and the new end point is a mixture that looks oddly similar to a [Bernstein polynomial](https://en.wikipedia.org/wiki/Bernstein_polynomial) of degree two, except it uses (z-1) rather than (1-z)... These new coordinates are actually really easy to compute directly!
|
***Brilliant***: if we want a subcurve from `t = 0` to `t = z`, we can keep the first coordinate the same (which makes sense), our control point becomes a z-ratio mixture of the original control point and the start point, and the new end point is a mixture that looks oddly similar to a [Bernstein polynomial](https://en.wikipedia.org/wiki/Bernstein_polynomial) of degree two, except it uses (z-1) rather than (1-z)... These new coordinates are actually really easy to compute directly!
|
||||||
|
|
||||||
Of course, that's only one of the two curves. Getting the section from `t = z` to `t = 1` requires doing this again. We first observe what we just did is actually evaluate the general interval [0,`z`], which we wrote down simplified becuase of that zero, but we actually evaluated this:
|
Of course, that's only one of the two curves. Getting the section from `t = z` to `t = 1` requires doing this again. We first observe what we just did is actually evaluate the general interval [0,`z`], which we wrote down simplified because of that zero, but we actually evaluated this:
|
||||||
|
|
||||||
\[
|
\[
|
||||||
B(t) =
|
B(t) =
|
||||||
|
@@ -53,7 +53,7 @@ And that's one reason why Bézier curves are tricky: there are actually a `lot`
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
So, you cannot offset a Bézier curve perfectly with another Bézier curve, no matter how high-order you make that other Bézier curve. However, we can chop up a curve into "safe" sub-curves (where safe means that all the control points are always on a single side of the baseline, and the midpoint of the curve at `t=0.5` is roughly in the centre of the polygon defined by the curve coordinates) and then point-scale each sub-curve with respect to its scaling origin (which is the intersection of the point normals at the start and end points).
|
So, you cannot offset a Bézier curve perfectly with another Bézier curve, no matter how high-order you make that other Bézier curve. However, we can chop up a curve into "safe" sub-curves (where safe means that all the control points are always on a single side of the baseline, and the midpoint of the curve at `t=0.5` is roughly in the center of the polygon defined by the curve coordinates) and then point-scale each sub-curve with respect to its scaling origin (which is the intersection of the point normals at the start and end points).
|
||||||
|
|
||||||
A good way to do this reduction is to first find the curve's extreme points, as explained in the earlier section on curve extremities, and use these as initial splitting points. After this initial split, we can check each individual segment to see if it's "safe enough" based on where the center of the curve is. If the on-curve point for `t=0.5` is too far off from the center, we simply split the segment down the middle. Generally this is more than enough to end up with safe segments.
|
A good way to do this reduction is to first find the curve's extreme points, as explained in the earlier section on curve extremities, and use these as initial splitting points. After this initial split, we can check each individual segment to see if it's "safe enough" based on where the center of the curve is. If the on-curve point for `t=0.5` is too far off from the center, we simply split the segment down the middle. Generally this is more than enough to end up with safe segments.
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ Before we move on to the next section we need to spend a little bit of time on t
|
|||||||
|
|
||||||
Getting normals in 3D is in principle the same as in 2D: we need to take the normalised tangent vector, and then rotate it by a quarter turn. However, this is where things get that little more complex: we can turn in quite a few directions, so we need to restrict the rotation to the plane that the tangent lies on. That might sound strange: tangents are themselves lines and lines simultaneously lie on an infinite number of planes, so what's up with that?
|
Getting normals in 3D is in principle the same as in 2D: we need to take the normalised tangent vector, and then rotate it by a quarter turn. However, this is where things get that little more complex: we can turn in quite a few directions, so we need to restrict the rotation to the plane that the tangent lies on. That might sound strange: tangents are themselves lines and lines simultaneously lie on an infinite number of planes, so what's up with that?
|
||||||
|
|
||||||
Well, we know more about the tangent: we also know its rate of change. Think of the Bezier curve as the path of a car. The curve itself tells us the "place in space" at any given time, and the first derivative at any point tells us the "speed of the car at that point". However, we know more: we also know the tangent at "some next moment in time", the second derivative tells us the "accelleration of the car at that point", and if we add the accelleration to the velocity, we know where the car will be "if the curve stopped changing": as long as the curve we're dealing with is not degenerate (that is to say: it isn't actually a pure 2D curve that we simply rotated in 3D) then at any point in time we know two vectors in the same plane, with a third vector in that same plane, and a fourth vector perpendicular that we don't know yet:
|
Well, we know more about the tangent: we also know its rate of change. Think of the Bézier curve as the path of a car. The curve itself tells us the "place in space" at any given time, and the first derivative at any point tells us the "speed of the car at that point". However, we know more: we also know the tangent at "some next moment in time", the second derivative tells us the "acceleration of the car at that point", and if we add the acceleration to the velocity, we know where the car will be "if the curve stopped changing": as long as the curve we're dealing with is not degenerate (that is to say: it isn't actually a pure 2D curve that we simply rotated in 3D) then at any point in time we know two vectors in the same plane, with a third vector in that same plane, and a fourth vector perpendicular that we don't know yet:
|
||||||
|
|
||||||
- **t**, the (normalized) vector for the direction of travel at some point B(t),
|
- **t**, the (normalized) vector for the direction of travel at some point B(t),
|
||||||
- **a**, the difference vector between "the tangent here" to what "the tangent at the next point" would be,
|
- **a**, the difference vector between "the tangent here" to what "the tangent at the next point" would be,
|
||||||
|
@@ -51,4 +51,4 @@ Finally, we also want to make sure that moving the on-curve coordinates preserve
|
|||||||
|
|
||||||
Again, we see that cubic curves are now rather nice to work with, but quadratic curves have a new, very serious problem: we can move an on-curve point in such a way that we can't compute what needs to "happen next". Move the top point down, below the left and right points, for instance. There is no way to preserve correct control points without a kink at the bottom point. Quadratic curves: just not that good...
|
Again, we see that cubic curves are now rather nice to work with, but quadratic curves have a new, very serious problem: we can move an on-curve point in such a way that we can't compute what needs to "happen next". Move the top point down, below the left and right points, for instance. There is no way to preserve correct control points without a kink at the bottom point. Quadratic curves: just not that good...
|
||||||
|
|
||||||
A final improvement is to offer fine-level control over which points behave which, so that you can have "kinks" or individually controlled segments when you need them, with nicely well-behaved curves for the rest of the path. Implementing that, is left as an excercise for the reader.
|
A final improvement is to offer fine-level control over which points behave which, so that you can have "kinks" or individually controlled segments when you need them, with nicely well-behaved curves for the rest of the path. Implementing that, is left as an exercise for the reader.
|
||||||
|
@@ -29,7 +29,7 @@ function drawCurve(points[], t):
|
|||||||
drawCurve(newpoints, t)
|
drawCurve(newpoints, t)
|
||||||
```
|
```
|
||||||
|
|
||||||
After running this function for some value `t`, the `left` and `right` arrays will contain all the coordinates for two new curves - one to the "left" of our `t` value, the other on the "right", of the same order as the original curve, and overlayed exactly on the original curve.
|
After running this function for some value `t`, the `left` and `right` arrays will contain all the coordinates for two new curves - one to the "left" of our `t` value, the other on the "right", of the same order as the original curve, and overlaid exactly on the original curve.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -5,4 +5,4 @@ With our knowledge of bounding boxes, and curve alignment, We can now form the "
|
|||||||
<Graphic title="Aligning a quadratic curve" setup={this.setupQuadratic} draw={this.draw} />
|
<Graphic title="Aligning a quadratic curve" setup={this.setupQuadratic} draw={this.draw} />
|
||||||
<Graphic title="Aligning a cubic curve" setup={this.setupCubic} draw={this.draw} />
|
<Graphic title="Aligning a cubic curve" setup={this.setupCubic} draw={this.draw} />
|
||||||
|
|
||||||
These are, strictly speaking, not necessarily the tightest possible bounding boxes. It is possible to compute the optimal bounding box by determining which spanning lines we need to effect a minimal box area, but because of the parametric nature of Bézier curves this is actually a rather costly operation, and the gain in bounding precision is often not worth it. If there is high demand for it, I'll add a section on how to precisely compute the best fit bounding box, but the maths is fairly gruelling and just not really worth spending time on.
|
These are, strictly speaking, not necessarily the tightest possible bounding boxes. It is possible to compute the optimal bounding box by determining which spanning lines we need to effect a minimal box area, but because of the parametric nature of Bézier curves this is actually a rather costly operation, and the gain in bounding precision is often not worth it. If there is high demand for it, I'll add a section on how to precisely compute the best fit bounding box, but the maths is fairly grueling and just not really worth spending time on.
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Playing with the points for curves may have given you a feel for how Bézier curves behave, but what *are* Bézier curves, really? There are two ways to explain what a Bézier curve is, and they turn out to be the entirely equivalent, but one of them uses complicated maths, and the other uses really simple maths. So... let's start with the simple explanation:
|
Playing with the points for curves may have given you a feel for how Bézier curves behave, but what *are* Bézier curves, really? There are two ways to explain what a Bézier curve is, and they turn out to be the entirely equivalent, but one of them uses complicated maths, and the other uses really simple maths. So... let's start with the simple explanation:
|
||||||
|
|
||||||
Bezier curves are the result of [linear interpolations](https://en.wikipedia.org/wiki/Linear_interpolation). That sounds complicated but you've been doing linear interpolation since you were very young: any time you had to point at something between two other things, you've been applying linear interpolation. It's simply "picking a point between two points".
|
Bézier curves are the result of [linear interpolations](https://en.wikipedia.org/wiki/Linear_interpolation). That sounds complicated but you've been doing linear interpolation since you were very young: any time you had to point at something between two other things, you've been applying linear interpolation. It's simply "picking a point between two points".
|
||||||
|
|
||||||
If we know the distance between those two points, and we want a new point that is, say, 20% the distance away from the first point (and thus 80% the distance away from the second point) then we can compute that really easily:
|
If we know the distance between those two points, and we want a new point that is, say, 20% the distance away from the first point (and thus 80% the distance away from the second point) then we can compute that really easily:
|
||||||
|
|
||||||
|
@@ -137,7 +137,7 @@
|
|||||||
<section className="preface"><h2>Preface</h2>
|
<section className="preface"><h2>Preface</h2>
|
||||||
<p>In order to draw things in 2D, we usually rely on lines, which typically get classified into two categories: straight lines, and curves. The first of these are as easy to draw as they are easy to make a computer draw. Give a computer the first and last point in the line, and BAM! straight line. No questions asked.</p>
|
<p>In order to draw things in 2D, we usually rely on lines, which typically get classified into two categories: straight lines, and curves. The first of these are as easy to draw as they are easy to make a computer draw. Give a computer the first and last point in the line, and BAM! straight line. No questions asked.</p>
|
||||||
<p>Curves, however, are a much bigger problem. While we can draw curves with ridiculous ease freehand, computers are a bit handicapped in that they can't draw curves unless there is a mathematical function that describes how it should be drawn. In fact, they even need this for straight lines, but the function is ridiculously easy, so we tend to ignore that as far as computers are concerned, all lines are "functions", regardless of whether they're straight or curves. However, that does mean that we need to come up with fast-to-compute functions that lead to nice looking curves on a computer. There's a number of these, and in this article we'll focus on a particular function that has received quite a bit of attention, and is used in pretty much anything that can draw curves: "Bézier" curves</p>
|
<p>Curves, however, are a much bigger problem. While we can draw curves with ridiculous ease freehand, computers are a bit handicapped in that they can't draw curves unless there is a mathematical function that describes how it should be drawn. In fact, they even need this for straight lines, but the function is ridiculously easy, so we tend to ignore that as far as computers are concerned, all lines are "functions", regardless of whether they're straight or curves. However, that does mean that we need to come up with fast-to-compute functions that lead to nice looking curves on a computer. There's a number of these, and in this article we'll focus on a particular function that has received quite a bit of attention, and is used in pretty much anything that can draw curves: "Bézier" curves</p>
|
||||||
<p>They're named after <a href="https://en.wikipedia.org/wiki/Pierre_B%C3%A9zier">Pierre Bézier</a>, who is principally responsible for getting them known to the world as a curve well-suited for design work (working for Renault and publishing his investigations in 1962), although he was not the first, or only one, to "invent" these type of curves. One might be tempted to say that the mathematician <a href="https://en.wikipedia.org/wiki/Paul_de_Casteljau">Paul de Casteljau</a> was first, investigating the nature of these curves in 1959 while working at Citroën, coming up with a really elegant way of figuring out how to draw them. However, de Casteljau did not publish his work, making the question "who was first" hard to answer in any absolute sense. Or is it? Bézier curves are, at their core, "Bernstein polynomials", a family of mathematical functions investigated by <a href="https://en.wikipedia.org/wiki/Sergei_Natanovich_Bernstein">Sergei Natanovich Bernstein</a>, with publications on them at least as far back as 1912. Anyway, that's mostly trivia, what you are more likely to care about is that these curves are handy: you can link up multiple Bézier curves so that the combination looks like a single curve. If you've ever drawn Photoshop "paths" or worked with vector drawing programs like Flash, Illustrator or nkscape, those curves you've been drawing are Bézier curves.</p>
|
<p>They're named after <a href="https://en.wikipedia.org/wiki/Pierre_B%C3%A9zier">Pierre Bézier</a>, who is principally responsible for getting them known to the world as a curve well-suited for design work (working for Renault and publishing his investigations in 1962), although he was not the first, or only one, to "invent" these type of curves. One might be tempted to say that the mathematician <a href="https://en.wikipedia.org/wiki/Paul_de_Casteljau">Paul de Casteljau</a> was first, investigating the nature of these curves in 1959 while working at Citroën, coming up with a really elegant way of figuring out how to draw them. However, de Casteljau did not publish his work, making the question "who was first" hard to answer in any absolute sense. Or is it? Bézier curves are, at their core, "Bernstein polynomials", a family of mathematical functions investigated by <a href="https://en.wikipedia.org/wiki/Sergei_Natanovich_Bernstein">Sergei Natanovich Bernstein</a>, with publications on them at least as far back as 1912. Anyway, that's mostly trivia, what you are more likely to care about is that these curves are handy: you can link up multiple Bézier curves so that the combination looks like a single curve. If you've ever drawn Photoshop "paths" or worked with vector drawing programs like Flash, Illustrator or Inkscape, those curves you've been drawing are Bézier curves.</p>
|
||||||
<p>So, what if you need to program them yourself? What are the pitfalls? How do you draw them? What are the bounding boxes, how do you determine intersections, how can you extrude a curve, in short: how do you do everything that you might want when you do with these curves? That's what this page is for. Prepare to be mathed!</p>
|
<p>So, what if you need to program them yourself? What are the pitfalls? How do you draw them? What are the bounding boxes, how do you determine intersections, how can you extrude a curve, in short: how do you do everything that you might want when you do with these curves? That's what this page is for. Prepare to be mathed!</p>
|
||||||
|
|
||||||
<div className="print">
|
<div className="print">
|
||||||
|
Reference in New Issue
Block a user