I wrote a class library for function graphing and exploration (with several complex, interactive features) years ago that uses XML for most of the graphing setup. The library dealt with functions defined by a modest number of parameters whose values may change, but the basic functional representation is immutable.
A student might explore x2 + 3x + 2 over a variety of intervals, or may even study ax2 + bx + c, where the constants a, b, and c are interactively varied. The function is still defined, in advance, by a small, finite number of parameters.
My client recently wished to use this engine to explore functions that arise from physical simulations over time. The ‘function’ is not mathematically defined by a formula. Instead, its values are sampled over time. Data points are plotted either separately, or connected by lines. Two new low-level functions, ScatterPlot and LinePlot were introduced into the defined functions library and new methods were added to the function graphing class to allow data that ‘defines’ a function to be updated at runtime. New methods to adjust the graph range and tic increments were also added.
The function graphing engine now supports panning restrictions by quadrant. Many physical simulations are sampled over time intervals, so the graph x-coordinate is a time sample (always greater than or equal to zero). For simulations whose y-axis values are positive, restricting graph panning to the first quadrant is desirable. One or two-quadrant combination panning restrictions are now allowed in the function graph XML.
<learningObject id=”simulationGraph” class=”display.graphing.functions.FunctionPlot” x=”35″ y=”40″ width=”350″ height=”280″ display=”f1,f2,f3″ pannable=”true” restrictPan=”quadrant:1″ >
Here’s a screenshot of the engine in action,
The modifications have been quite interesting, but would have been far more difficult without the extreme level of internal and external documentation generated for this complex class library. Over two years went by between modifications. So, think about the next person that comes along after your code is written.
It might be you
The full 1.0 release of the Freehand Drawing Library will contain classes for both fixed- and variable-wdith strokes. The latter was the most challenging in terms of implementation, so it was the first entry into the library. Now, my attention is turning towards constant-stroke classes.
Unlike its fixed-width counterpart, I anticipate a wide variety of both algorithms and stroke styles to be employed with fixed-width strokes. In order to avoid bloating the library with one class for every conceivable type of fixed-width stroke, a different architecture is used inside the library.
A BasicStroke class that implements IFreehandDrawable was created that serves as the constant-width counterpart to the Freehand class. Both fixed- and variable-width stroke classes accept the same data providers. To accommodate a wide variety of fixed-width strokes, the StrokeDataVO now contains a reference to a drawing engine of type IDrawingEngine. The fully qualified class name of the IDrawingEngine implementation is assigned before setting the data provider for a BasicStroke. This engine performs the actual drawing while the application interfaces with an IFreehandDrawable instance. The application is thus agnostic to both fixed- and variable-width strokes. A Factory may be employed to return the desired type of stroke based on input properties.
The basic drawing engine is based on simple line segments, i.e. point-to-point. Although occasionally useful, this engine (net.algorithmist.freehand.engine.LineSegments) is intended to serve as a base class for other engines. A smoothed-stroke engine is currently under development, net.algorithmist.freehand.engine.SmoothedStroke . It uses the same smoothing algorithm as the variable-width Freehand stroke, but without the overhead for computing the two splines used to vary stroke width. A screen shot is shown below.
Lang Simplification, discussed in the previous post, should be useful for strokes containing a significant number of straight or nearly-straight sections, such as the rough ’4′ in the above example.
The drawing engine may be changed during runtime simply by altering the engine reference and re-assigning the stroke data provider. The new engine is applied to all subsequently drawn strokes.