Splines in the Freehand Drawing Library have been a challenge in two areas. First, the concept of freehand drawing is centered around strokes. Strokes are defined by sequence of mouse/touch points that begin with a press, continue with a sequence of moves (while pressing), and end with a release. An interpolative spline is defined with a series of interpolation points or vertices and continuity conditions at the join points. There is no concept of press-move-release. Fortunately, the FDL architecture is sufficiently general to allow points to be artificially added to strokes. The UI issue with splines is creating a user-friendly mechanism for identifying the last interpolation point.
The second issue is editing, both vertex and in/out tangents at each vertex. Since all strokes allow their input points to be either manually defined or cached and returned via user input, editing is not part of the core library. It’s impossible to create an editing system that is general, maintainable, and equally satisfies all prospective users. When it comes to splines, I don’t want to add more methods to the IFreehandDrawable interface simply to accommodate new features. New methods should be added only with careful thought and because it makes good, long-term sense for the library. Fortunately, ample back-doors exist to arbitrarily set and retrieve properties for various stroke engines through parameter access and mutation methods. I plan to use that existing scheme to expose parameters that facilitate spline editing.
Tangent construction is handled by a Command pattern that is applied across the engine spline or at individual vertices. The tangent command is injectable and its fully qualified class name is part of the stroke engine parameter set.
There is currently one spline-based stroke engine for the PolyLine stroke. The CubicBezierSpline engine is based on the cubic bezier spline in this TechNote. The tangent construction is handled by a NormalBisector Command that is injected into the stroke engine. I also plan to add another tangent command that uses the same approach as Catmull-Rom splines. This provides two different cubic bezier splines with a single code base. I also plan to provide a corner tangent command that allows sharp corners to be inserted into otherwise continuous splines.
One final feature of the architecture is the decoupling of the spline computations from the drawing facilities in the FDL. This is similar to how I decoupled spline computations from the drawing architecture in Degrafa. There are no actual spline computations performed in the CubicBezierSpline drawing engine. It contains a FDLCubicBezierSpline, which is a concrete implementation of the IFDLInterpolatingSpline interface. FDLCubicBezierSpline is completely independent of the FDL.
The final implementation issue is on-the-fly arc-length parameterization. In order to draw the spline with line decorators, all requests for x-y coordinates along the spline are based on normalized arc length, not the spline’s natural parameter. In other words, getX(0.5) returns an x-coordinate of a point that is approximately one-half the length along the entire spline. The arc-length parameterization is optimized for monotonically increasing or decreasing normalized arc-length queries. It presumes constant redraws of the spline, so there is no pre-computation/lookup applied in the parameterization.
A screenshot of work in progress is provided below.
During debugging, the outer edge of the convex hull of the bezier control points is drawn (in black). The red dots are points on the cubic bezier spline at (approximate) uniform normalized arc length. The next step is to apply a line decorator to actually draw the spline. Another advantage of automatic arc-length parameterization is relatively easy computation of the Δs to use in point-to-point drawing of the spline. This mimics a freehand curve like all the other engines, which allows automatic application of the existing line decorators in drawing the spline.