Remodeling Béziers
This article is about how math creates an easier-to-use version of Béziers. We'll show examples of issues with Béziers, then discuss the issues academically, then derive a curve that fixes the problems (and can draw circles!).
This is a Bézier. You can drag the handles. The green lines represent curvature.
Béziers are a little unintuitive to use, because it's not always clear where to put the control points, or how long the handles should be. This leads to a lot of trial and error. To see for yourself, use the handles below to align the black and pink curves.
Click "Show answer" when done. You probably got the curve right, but you had to go back and forth between the two handles, iterating the curve. You couldn't set each handle to the correct position on the first try. (I can't do it either.)
Here's another one:
You probably got the curve close, but your handles are way off.
Adjust the middle node:
I don't know of a reasonable way to guess the right location.
These illustrate the following problems with Béziers:
- It's hard to guess the correct handle length on the first try; you have to see the result and adjust iteratively.
- The correct value of a handle depends on too many other things. If you move a node or handle, then you have to adjust its handles, then re-adjust the neighboring handles, then re-adjust the first handles, then re-adjust the neighboring handles, etc. You can't set each handle to the correct value on its own. In mathematics, this means they are "non-orthogonal".
- Widely different inputs can lead to similar outputs, and then you have to adjust neighboring handles a lot. In mathematics, this means the output-to-input map is "poorly conditioned". This becomes an issue when it is not axis-aligned, like the second example.
- There's no obvious way to figure out where to put nodes.
And there are three more problems:
- Béziers can't represent circles. If you try to approximate one by hand, it'll look lopsided.
- Deleting existing nodes from a curve creates a mess.
- It's hard to keep curvature continuous across a node; you'd have to stare at curvature combs and fiddle a lot.
Mathematics resolves these issues.
Local and global
The words "local" and "global" appear in disparate domains in mathematics, like parallel evolution. These concepts are broadly useful.
Small and simple objects are "local". Large and complex objects are "global". For example, your house is a global object, and each room is a local object. If you want to know whether the whole house is painted white (a global property), you can check whether each individual room is painted white (a local property). This splits the global task into smaller local tasks.
The key insight is: Local properties are easy, and global properties are hard. Whenever global objects can be decomposed into local objects, it's more convenient to work with the local objects. This is the backbone of the local to global principle.
A curve is determined by a finite number of points on the screen - those points are the artist's interface. These points should control the curve locally, determining the curve in a small region.
In geometry, the standard local description of a curve is its derivatives, like a Taylor series. (We can safely ignore analyticity, thanks to Stone-Weierstrass.) The zeroth derivative is position - where the curve is. The first derivative is direction - which way the curve points. The second derivative is curvature - how curvy the curve is. These three parameters are easy for humans to understand, and artists don't use higher derivatives. So the points should control position, direction, and curvature. These three parameters produce a circular arc.
Local control is easy to draw with, because you just need to match behavior at points:
- You understand what an input point does - it chooses a circular arc for the nearby curve.
- It's easy to position input points - put them where you want to change the curve.
This explains the underlying issue with Béziers. Bézier controls are local for position and direction, but not local for curvature. Local position and local direction coincide with the easy tasks - placing the node on the curve and setting the handle direction. Non-local curvature coincides with the hard task - setting handle length. So it's failure of locality causing the issues.
Given local behavior, how do we create a global curve?
The user only inputs a finite number of points. It's best if the correct curve extends naturally between these points, requiring as few correction points as possible.
Because of locality, a curve between two points is determined solely by these two points, and ignores any points farther away.
Each point specifies either 0, 1, or 2 derivatives. Remember that the 0th derivative is position, the 1st derivative is direction, and the 2nd derivative is curvature. If you specify a higher derivative (like curvature), you specify all its lower derivatives as well (like position and direction). So given two points and a number of derivatives for each point (0 1 2), we have to come up with a curve.
If each point specifies only its position (the derivative numbers are 0 0), the curve is a line.
If one point specifies direction and the other specifies position (derivatives are 1 0), then the curve is a circle.
That's the easy cases. For the rest, we need math. We specify some possible axioms, then choose which ones to satisfy.
- Primacy of lines and circles. The desired curves are disproportionately lines and circles. If the points express a line or circle, then the curve is a line or circle.
- Affirmation invariance. Adding extra derivatives that agree with the existing curve should keep the curve the same.
- Affine transformation. Affine transformation of the points causes affine transformation of the curve. This is important for drawing 3D objects (which most things are). For example, ellipses are circles on a 3D plane, projected down to your 2D surface. They are an affine transformation of a circle.
- Subdivision (strong). If we truncate a curve specified by points with N and M derivatives, then the two endpoints of the snipped curve have N and M derivatives.
- Subdivision (weak). If we truncate a curve specified by points with N and M derivatives, then the two endpoints of the snipped curve have at most max(N, M) derivatives.
- Finite length. Curves don't travel to infinity. (For example, a parabola does.)
- Non-singularity. When the input varies smoothly, the output also varies smoothly.
Unfortunately, not all these axioms can be satisfied simultaneously. So we have to make some tradeoffs, just like Arrow's impossibility theorem. An example is that axioms 1 + 3 together create the conic sections - whose paths travel to infinity, and hence cannot satisfy axioms 6 or 7.
If both points specify direction (1 1), then a parabola satisfies axioms 3, 4, and 5. It's equivalent to a quadratic Bézier. The best UI control is the intersection of the handles, which satisfies axiom 7.
An alternative is to choose sin(angle/2)
as the weight for a rational quadratic Bézier. This satisfies axioms 1, 2, and 6 (and 7 with the right UI). The curve is always an ellipse. If the directions are perpendicular, then the points are the maximum and minimum curvature points of an ellipse.
If one point specifies direction and curvature, and the other point specifies only position (2 0), then the previous choices are still available: ellipses and parabolas. There's an additional alternative, where the direction and curvature are mirrored to the other point across the line between them. This satisfies axioms 1, 2, 3, and symmetry.
If one point specifies direction and curvature, and the other point specifies direction (2 1), then we get a uniquely determined conic section, and axioms 1, 2, 3, 4, and 5 are satisfied. These are ellipses, parabolas, and hyperbolas. Curves which travel to infinity should have their handles flipped.
If both points specify direction and curvature (2 2), then the curve is overspecified for a conic, so we have choices to make.
Here's one possible solution. Blue circles control curvature.
Now blue circular handles control radius:The handles determine the radius of a circular arc near the point. The in-between is interpolated.
There are better curves; I didn't look too hard. This one respects axioms 1, 2, 3, and 6. 4 and 5 are probably achievable with a week of work. This one happens to be computationally efficient. It flips when the handles cross parallel, but this flip can be adjusted with more work (letting you represent a whole circle with one point).
This curve is a rational cubic Bézier. You can read it in final3()
in the source code, or follow these steps:
- Affine transform the two input points so that the intersection of their directions creates an isosceles right triangle, with the two points forming the hypotenuse. (Like the first of the two diagrams above.)
- Let the curvatures of the points be
c
andk
. Draw a rational cubic Bézier. Letr = √(8c)/3/(c + k)
,s = √(8k)/3/(c + k)
. The second control point has weightw = 3krr/2 + s
and is proportionr/w
between the first and second control points. The third control point is calculated symmetrically by exchanging the input points. - Affine transform the curve back to the original position.
The old list of problems with Béziers is fixed:
- The handle length can be set on the first or second try; just match a small arc around the point.
- The correct handle length only depends on a small region around the point, not anything else. After it's set, moving nodes and other handles won't interfere; it'll still be correct.
- High condition numbers for the inverse map are axis-aligned.
- Placing nodes is simple. (Put them at the largest deviations, and on inflection points.)
- Circles are easy to draw.
- Deleting existing nodes from a curve leaves the curve in good shape.
- The curvature is well behaved (the green lines). At a smooth node, the curvature is equal on both sides - no jump discontinuities.
Here's a trick that turns curve-finding from a boring math problem into an interesting geometry problem. The subdivision axiom is secretly reversible:
- Extension axiom: Any curve has a unique curve that extends it, which produces the original curve when subdivided. This extension continues forever, so the curve must either loop back to itself or approach a singularity. This infinitely-continuing curve is called a "parent curve".
Finding a set of parent curves is a very intuitive process. Each parent curve extends infinitely, ending in loops or singularities, and every curve is just a short snippet of a parent curve. For example, the parent curves for conics are the parabolas and hyperbolas (which extend forever) and the ellipses (which loop). You can try to construct your own parent curves, using these constraints as a guide:
- Any pair of points, containing the information {position, direction, curvature}, must belong to exactly one parent curve.
- Since conics create every valid symmetric tuple, your parent curves can have no axis of symmetry unless they are conics.
- Parent curves should be useful curves to draw with.
- The curves should behave well with inflection points; this is necessary to cover the lack of inflection points with conic sections. So the curve should be non-degenerate when one point has curvature 0.
Which curves do you think are elegant or useful? Is there an optimal set of parent curves?
Here are some possible approaches:
- Try existing classes of curves with nice properties, like cardioids, conic generalizations, or other geometric shapes. See if any of them form parametrized families. Here's a list.
- Create parametrized families of curves directly. The interesting case is with singularities at infinity, where curvatures approach 0. Some promising classes are complex rational Béziers, rational Béziers, and curves on the complex plane. For example, conic sections have a nice polar form.
- Eyeball the loop case, where one curvature is zero and the other is not.
Future work
These control systems can be extended to curves and surfaces in 3D. And to curves on 3D surfaces.
With a suitable UI, animations should be convenient. Conics express the sine wave, which is the physically natural "ease-in-out", a spring force. They also express the parabola, the physically natural "ease-in", a constant force (like gravity).
2 2's curve can be improved. There's also alternative UIs for the node handles that allow inflection points away from input points.
If you develop drawing software, you can implement these curves. They are easier to learn than Béziers, and are easier to be accurate with. No more staring at curvature combs needed.
Discussion: Hacker News
Author: Kevin Yin
Visualizations are thanks to G9 and took minimal effort.