
Ken --

    This is the code for the curve-fitting algorithm.  There are two
parts -- the actual algorithm, and an X11 driver.  See below for details.


    Inventory :
    ---------

	2d.c, 2d.h :  Simple geometry library.  Defines points, vectors,
	              dot products, etc.

	
	fit.c      :  X11 demo program.  You should be able to run this
		      on anything that runs X programs.

		      How to use :  To sketch in the points, just hold
		      down the left mouse button, and draw away to your
		      heart's content :-).  After you've got the points,
	  	      hit the middle mouse button.  This will fit the 
		      curve to the points.  Pushing the right mouse button
		      will erase the screen, and you can then try another
		      curve.

		      Command-line arguments :

		      -display <displayname:screen> 
		          Specifies the name of the display you want to use.

		      -test
			  Non-interactive mode.  Sleeps for 5 seconds, then
			  fits a hard-coded curve.

		      -debug 
			  No effect.  Must've been used for something, once.

		      -error
			  Squared error term.  Controls the quality of the
			  fit.  Default is 16, and the units are effectively
			  in pixels (values are real numbers, though).

		      -cp
			  Tells the program to draw in the control points
			  and control polygons, in addition to the 
			  curves.

	fit_cubic.c : The actual curve-fitting algorithm.  Follows the
		      the pseudo-code and math equations found in
		      the "Graphics Gems" article.


    Remarks :
    -------	

    This is a "bare-bones" version of the algorithm.  There are two
areas which you might want to consider enhancing :

    1. Pre-processing.

       You'll notice that there is *no* pre-processing done to the input
       points.  If you read my thesis, there's a short section on some
       of the stuff I did in the "real" implementation.  Briefly, there
       are a few things you might want to do :

       i.   Remove redundant (coincident points).  This is necessary if
	    coincident successive points exist.  You'll note that
	    my X11 driver program does this.  The basic problem is
	    that the tangent-vector routines don't check for this, and
	    thus can generate vectors of the form [0, 0], which makes
	    things break later on.

       ii.  Remove points that are too close together (this is *highly*
	    implementation-dependent, but you may want to play around
	    with this). This is especially important if you want to
	    locate intentional discontinuities ("corners" -- see (iv.) below).
	    (I have code that does this -- let me know if you want it).

       iii. Filter the points with a little discrete convolution.  This,
	    too, is dependent on the implementation.  This can help
	    the fitting algorithm out by making the derivative estimations
	    more reliable, which can result in fewer Bezier curves.  This
	    is less necessary as the precision of the samples increases.

       iv.  Locate "corner" points.  There will probably be places where
	    you *want* a discontinuity, such as corners, spikes, etc.
	    This can be done by looking at the angle between successive
	    sets of points.  This step should follow the removal of
	    coincident and too-close points, and preceed the filtering.
	    The corners points break up the points into several sets of
	    of points, each of which should be fitted independently, so
	    the discontinuities are preserved.
	    (I have code that does this also -- let me know if you want it).

    2.  Derivative ("tangent") estimation.

	One of the most important determinants of the quality of the
	fit produced by the algorithm is the estimation of the derivatives
	at the "ends" of the set of points currently being fitted
	(recall that this is a recursive algorithm). 

	The algorithm attempts to fit a cubic Bezier curve to each set of
	points.  Obviously, the "outer" control points are placed at
	the ends of the current set of points.  As you well know, the
	"inner" control points of a cubic Bezier are located along a vector
	in the direction of the first derivative at each end of the curve.
	The algorithm estimates these derivative vectors (see the
	routines "ComputeLeftTangent", "ComputeRightTangent" in fit_cubic.c)
	from the sketched points.  The better the estimate corresponds
	to the "actual" derivative of the curve represented, the better the
	fit.

	The current derivative-estimator just looks at the vector between
	the endpoint and its next neighbor.  This is pretty crude.  A
	better estimator might look at the neighbor points some distance
	out from the endpoints, or use a linear least-squares fit on the
	points near the end, etc.


    Finally, note that the routine "FitCurve" has an "#ifdef OPEN_CURVE",
etc.  "OPEN_CURVE" is for curves that are open -- the tangents at each
endpoint are estimated independently of one another.  Note that
the tangent vectors point "inwards" with respect to the curve. "CLOSED_CURVE"
is for curves that are to be treated as closed -- in this case, the tangent
estimator looks at points on both "ends" of the curve to get a single
tangent vector, and just reverses it for one end, so again the tangent
vectors point "inward".  One could just as easily make this a parameter
to the routine, rather than a compile-time option.

- Philip



	





