Cubic Bezier splines look rather beautiful. Splines in general look great and are super useful for all sorts of cool stuff.
Creating splines usually involves defining some control points and moving those around until the desired effect is achieved. However, clicking on or interacting with the spline itself turns out to be a lot harder than just messing with control points.
In particular I want to talk about cubic Bezier splines. This kind of spline is defined as 3 * n + 1 control points, where n is the number of Bezier curves in the spline. The first four points represent the first curve, then the last point and the next three represent the next curve, and so on.
The resulting spline can look like the curved arrows in this graph:
http://www.graphviz.org/Gallery/directed/datastruct.png
Actually interacting with these splines would be pretty difficult since cartesian coordinates are not in the same basis as the spline itself. What is needed is the ability to express a relation between these two spaces.
Since I haven’t studied curves in great detail there’s much math that goes beyond me (for the time being) in solving this problem. However in Graphics Gems I there’s a section called “Solving The Nearest-Point-On-Curve Problem” where some public domain C code is given that computes the closest point on a cubic Bezier curve to a given input point. The C code is really old and actually calls malloc in a recursive function, presumably to avoid stack overflows on old machines with minimal memory. This can be converted to use a stack data structure on the process stack space, all without much work for the user.
After a few minutes I was able to set up a cool demo:
This tool would be great for some kind of interactive spline editor. It would also be perfect for colliding circles (or spheres in 3D), making the curve itself a potential candidate for a physics engine collider type. Since the curve is defined by control points, the curve can actually deform at run-time without any big complications.
Approximating the Curve
In reality a spline like this would probably just be “rasterized” to some kind of approximate shape that is easier for a physics engine to collide, rather than dealing with the exact spline itself. This is likely due to there not being a straightforward way to compute the closest point on the spline to a plane (at least, I sure don’t know how to write that code).
One tactic would be to interpolate a few points out of the curve (and by few I mean as many as needed), and then use these points as an approximate representation of the curve. This could be like converting the curve to a line strip, which might make interactivity much simpler in many cases.
Generating these points can be a piece of cake. Personally I’d just write out some lerps and call it good. If we look at the simplest Bezier curve with 2 control points it’s easy to see it’s just a single lerp (these animations were taken from the Wikipedia page for Bezier curves):
If we add a third point we can perform two lerps, and then lerp the results together. Intuitively this gives us a quadratic Bezier curve, and looks like:
Following suit we can use 4 control points and create a cubic Bezier curve:
An easy way to convert a cubic Bezier curve to a line strip involves sampling points on curve at time t, where t is on the interval of 0 to 1. Here’s some example code:
Point2 Lerp( Point2 a, Point2 b, float t ) { Point2 c; c.x = a.x * t + b.x * (1.0f - t); c.y = a.y * t + b.y * (1.0f - t); return c; } Point2 CubicBezier( Point2* bezCurve, float t ) { Point2 a0 = Lerp( bezCurve[0], bezCurve[1], t ); Point2 a1 = Lerp( bezCurve[1], bezCurve[2], t ); Point2 a2 = Lerp( bezCurve[2], bezCurve[3], t ); Point2 b0 = Lerp( a0, a1, t ); Point2 b1 = Lerp( a1, a2, t ); return Lerp( b0, b1, t ); } glBegin( GL_LINE_STRIP ); for ( int i = 0; i < NUM_STRIPS; ++i ) { float t = (float)i / NUM_STRIPS; Point2 p = CubicBezier( bezCurve, t ); glVertex2f( p.x, p.y ); } glVertex2f( bezCurve[0].x, bezCurve[0].y ); glEnd( );
For example, Box2D defines a “chain shape” as a line strip of segments. Generally this strip is used to approximate curved, non-moving surfaces. Splines can very easily be converted to strips and sent to Box2D to define a chain shape.
Some Spliney Links
- http://phildogames.com/blog/spline.html
- http://www.gamasutra.com/view/feature/131755/curved_surfaces_using_bzier_.php
- http://www.jasondavies.com/animated-bezier/
- http://pomax.github.io/bezierinfo/
- http://ciechanowski.me/blog/2014/02/18/drawing-bezier-curves/