craig@weedeater.math.yale.edu (Craig Kolb) (10/15/90)
Posting-number: Volume 15, Issue 27 Submitted-by: Craig Kolb <craig@weedeater.math.yale.edu> Archive-name: ggems/part05 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 5 (of 5)." # Contents: AALines/FastMatMul.c FitCurves.c GGVecLib.c NearestPoint.c # Wrapped by craig@weedeater on Fri Oct 12 15:53:15 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'AALines/FastMatMul.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'AALines/FastMatMul.c'\" else echo shar: Extracting \"'AALines/FastMatMul.c'\" \(12335 characters\) sed "s/^X//" >'AALines/FastMatMul.c' <<'END_OF_FILE' X/* FILENAME: FastMatMul.c [revised 18 AUG 90] X X AUTHOR: Kelvin Thompson X X DESCRIPTION: Routines to multiply different kinds of 4x4 X matrices as fast as possible. Based on ideas on pages 454, X 460-461, and 646 of _Graphics_Gems_. X X These routines offer two advantages over the standard X V3MatMul in the _Graphics_Gems_ vector library GGVecLib.c. X First, the routines are faster. Second, they can handle X input matrices that are the same as the output matrix. X The routines have the disadvantage of taking more code X space (from unwound loops). X X The routines are about as fast as you can get for X general-purpose multiplication. If you have special X knowledge about your system, you may be able to improve X them a little more: X X [1] If you know that your input and output matrices will X never overlap: remove the tests near the beginning and end of X each routine, and just #define 'mptr' to 'result'. (The X standard library's V3MatMul makes this assumption.) X X [2] If you know that your compiler supports more than X three pointer-to-double registers in a subroutine: make X 'result' in each routine a register variable. You might X also make the 'usetemp' boolean a register. X X [3] If you have limited stack space, or your system can X access global memory faster than stack: make each routine's X 'tempx' a static, or let all routines share a global 'tempx'. X (This is useless if assumption [1] holds.) X*/ X X/* definitions from "GraphicsGems.h" */ Xtypedef struct Matrix3Struct { /* 3-by-3 matrix */ X double element[3][3]; X } Matrix3; Xtypedef struct Matrix4Struct { /* 4-by-4 matrix */ X double element[4][4]; X } Matrix4; X X/* routines in this file */ XMatrix3 *V2MatMul(); /* general 3x3 matrix multiplier */ XMatrix4 *V3MatMul(); /* general 4x4 matrix multiplier */ XMatrix4 *V3AffMatMul(); /* affine 4x4 matrix multiplier */ XMatrix4 *V3LinMatMul(); /* linear 4x4 matrix multiplier */ X X/* macro to access matrix element */ X#define MVAL(mptr,row,col) ((mptr)->element[row][col]) X X X X X X/* ************************************ X * * X * V2MatMul * X * * X ************************************ X X DESCRIPTION: Multiply two general 3x3 matricies. If one of X the input matrices is the same as the output, write the X result to a temporary matrix during multiplication, then X copy to the output matrix. X X ENTRY: X a -- pointer to left matrix X b -- pointer to right matrix X result -- result matrix X X EXIT: returns 'result' X*/ X XMatrix3 *V2MatMul ( a, b, result ) Xregister Matrix3 *a,*b; XMatrix3 *result; X{ Xregister Matrix3 *mptr; Xint usetemp; /* boolean */ XMatrix3 tempx; X X/* decide where intermediate result goes */ Xusetemp = ( a == result || b == result ); Xif ( usetemp ) X mptr = & tempx; Xelse X mptr = result; X XMVAL(mptr,0,0) = X MVAL(a,0,0) * MVAL(b,0,0) X + MVAL(a,0,1) * MVAL(b,1,0) X + MVAL(a,0,2) * MVAL(b,2,0); X XMVAL(mptr,0,1) = X MVAL(a,0,0) * MVAL(b,0,1) X + MVAL(a,0,1) * MVAL(b,1,1) X + MVAL(a,0,2) * MVAL(b,2,1); X XMVAL(mptr,0,2) = X MVAL(a,0,0) * MVAL(b,0,2) X + MVAL(a,0,1) * MVAL(b,1,2) X + MVAL(a,0,2) * MVAL(b,2,2); X XMVAL(mptr,1,0) = X MVAL(a,1,0) * MVAL(b,0,0) X + MVAL(a,1,1) * MVAL(b,1,0) X + MVAL(a,1,2) * MVAL(b,2,0); X XMVAL(mptr,1,1) = X MVAL(a,1,0) * MVAL(b,0,1) X + MVAL(a,1,1) * MVAL(b,1,1) X + MVAL(a,1,2) * MVAL(b,2,1); X XMVAL(mptr,1,2) = X MVAL(a,1,0) * MVAL(b,0,2) X + MVAL(a,1,1) * MVAL(b,1,2) X + MVAL(a,1,2) * MVAL(b,2,2); X XMVAL(mptr,2,0) = X MVAL(a,2,0) * MVAL(b,0,0) X + MVAL(a,2,1) * MVAL(b,1,0) X + MVAL(a,2,2) * MVAL(b,2,0); X XMVAL(mptr,2,1) = X MVAL(a,2,0) * MVAL(b,0,1) X + MVAL(a,2,1) * MVAL(b,1,1) X + MVAL(a,2,2) * MVAL(b,2,1); X XMVAL(mptr,2,2) = X MVAL(a,2,0) * MVAL(b,0,2) X + MVAL(a,2,1) * MVAL(b,1,2) X + MVAL(a,2,2) * MVAL(b,2,2); X X/* copy temp matrix to result if needed */ Xif ( usetemp ) X *result = *mptr; X Xreturn result; X} X X X X X/* ************************************ X * * X * V3MatMul * X * * X ************************************ X X DESCRIPTION: Multiply two general 4x4 matricies. If one of X the input matrices is the same as the output, write the X result to a temporary matrix during multiplication, then X copy to the output matrix. X X ENTRY: X a -- pointer to left matrix X b -- pointer to right matrix X result -- result matrix X X EXIT: returns 'result' X*/ X XMatrix4 *V3MatMul ( a, b, result ) Xregister Matrix4 *a,*b; XMatrix4 *result; X{ Xregister Matrix4 *mptr; Xint usetemp; /* boolean */ XMatrix4 tempx; X X/* decide where intermediate result goes */ Xusetemp = ( a == result || b == result ); Xif ( usetemp ) X mptr = & tempx; Xelse X mptr = result; X XMVAL(mptr,0,0) = X MVAL(a,0,0) * MVAL(b,0,0) X + MVAL(a,0,1) * MVAL(b,1,0) X + MVAL(a,0,2) * MVAL(b,2,0) X + MVAL(a,0,3) * MVAL(b,3,0); X XMVAL(mptr,0,1) = X MVAL(a,0,0) * MVAL(b,0,1) X + MVAL(a,0,1) * MVAL(b,1,1) X + MVAL(a,0,2) * MVAL(b,2,1) X + MVAL(a,0,3) * MVAL(b,3,1); X XMVAL(mptr,0,2) = X MVAL(a,0,0) * MVAL(b,0,2) X + MVAL(a,0,1) * MVAL(b,1,2) X + MVAL(a,0,2) * MVAL(b,2,2) X + MVAL(a,0,3) * MVAL(b,3,2); X XMVAL(mptr,0,3) = X MVAL(a,0,0) * MVAL(b,0,3) X + MVAL(a,0,1) * MVAL(b,1,3) X + MVAL(a,0,2) * MVAL(b,2,3) X + MVAL(a,0,3) * MVAL(b,3,3); X XMVAL(mptr,1,0) = X MVAL(a,1,0) * MVAL(b,0,0) X + MVAL(a,1,1) * MVAL(b,1,0) X + MVAL(a,1,2) * MVAL(b,2,0) X + MVAL(a,1,3) * MVAL(b,3,0); X XMVAL(mptr,1,1) = X MVAL(a,1,0) * MVAL(b,0,1) X + MVAL(a,1,1) * MVAL(b,1,1) X + MVAL(a,1,2) * MVAL(b,2,1) X + MVAL(a,1,3) * MVAL(b,3,1); X XMVAL(mptr,1,2) = X MVAL(a,1,0) * MVAL(b,0,2) X + MVAL(a,1,1) * MVAL(b,1,2) X + MVAL(a,1,2) * MVAL(b,2,2) X + MVAL(a,1,3) * MVAL(b,3,2); X XMVAL(mptr,1,3) = X MVAL(a,1,0) * MVAL(b,0,3) X + MVAL(a,1,1) * MVAL(b,1,3) X + MVAL(a,1,2) * MVAL(b,2,3) X + MVAL(a,1,3) * MVAL(b,3,3); X XMVAL(mptr,2,0) = X MVAL(a,2,0) * MVAL(b,0,0) X + MVAL(a,2,1) * MVAL(b,1,0) X + MVAL(a,2,2) * MVAL(b,2,0) X + MVAL(a,2,3) * MVAL(b,3,0); X XMVAL(mptr,2,1) = X MVAL(a,2,0) * MVAL(b,0,1) X + MVAL(a,2,1) * MVAL(b,1,1) X + MVAL(a,2,2) * MVAL(b,2,1) X + MVAL(a,2,3) * MVAL(b,3,1); X XMVAL(mptr,2,2) = X MVAL(a,2,0) * MVAL(b,0,2) X + MVAL(a,2,1) * MVAL(b,1,2) X + MVAL(a,2,2) * MVAL(b,2,2) X + MVAL(a,2,3) * MVAL(b,3,2); X XMVAL(mptr,2,3) = X MVAL(a,2,0) * MVAL(b,0,3) X + MVAL(a,2,1) * MVAL(b,1,3) X + MVAL(a,2,2) * MVAL(b,2,3) X + MVAL(a,2,3) * MVAL(b,3,3); X XMVAL(mptr,3,0) = X MVAL(a,3,0) * MVAL(b,0,0) X + MVAL(a,3,1) * MVAL(b,1,0) X + MVAL(a,3,2) * MVAL(b,2,0) X + MVAL(a,3,3) * MVAL(b,3,0); X XMVAL(mptr,3,1) = X MVAL(a,3,0) * MVAL(b,0,1) X + MVAL(a,3,1) * MVAL(b,1,1) X + MVAL(a,3,2) * MVAL(b,2,1) X + MVAL(a,3,3) * MVAL(b,3,1); X XMVAL(mptr,3,2) = X MVAL(a,3,0) * MVAL(b,0,2) X + MVAL(a,3,1) * MVAL(b,1,2) X + MVAL(a,3,2) * MVAL(b,2,2) X + MVAL(a,3,3) * MVAL(b,3,2); X XMVAL(mptr,3,3) = X MVAL(a,3,0) * MVAL(b,0,3) X + MVAL(a,3,1) * MVAL(b,1,3) X + MVAL(a,3,2) * MVAL(b,2,3) X + MVAL(a,3,3) * MVAL(b,3,3); X X/* copy temp matrix to result if needed */ Xif ( usetemp ) X *result = *mptr; X Xreturn result; X} X X X X X X/* ************************************ X * * X * V3AffMatMul * X * * X ************************************ X X DESCRIPTION: Multiply two affine 4x4 matricies. The X routine assumes the rightmost column of each input X matrix is [0 0 0 1]. The output matrix will have the X same property. X X If one of the input matrices is the same as the output, X write the result to a temporary matrix during multiplication, X then copy to the output matrix. X X ENTRY: X a -- pointer to left matrix X b -- pointer to right matrix X result -- result matrix X X EXIT: returns 'result' X*/ X XMatrix4 *V3AffMatMul ( a, b, result ) Xregister Matrix4 *a,*b; XMatrix4 *result; X{ Xregister Matrix4 *mptr; Xint usetemp; /* boolean */ XMatrix4 tempx; X X/* decide where intermediate result goes */ Xusetemp = ( a == result || b == result ); Xif ( usetemp ) X mptr = & tempx; Xelse X mptr = result; X XMVAL(mptr,0,0) = X MVAL(a,0,0) * MVAL(b,0,0) X + MVAL(a,0,1) * MVAL(b,1,0) X + MVAL(a,0,2) * MVAL(b,2,0); X XMVAL(mptr,0,1) = X MVAL(a,0,0) * MVAL(b,0,1) X + MVAL(a,0,1) * MVAL(b,1,1) X + MVAL(a,0,2) * MVAL(b,2,1); X XMVAL(mptr,0,2) = X MVAL(a,0,0) * MVAL(b,0,2) X + MVAL(a,0,1) * MVAL(b,1,2) X + MVAL(a,0,2) * MVAL(b,2,2); X XMVAL(mptr,0,3) = 0.0; X XMVAL(mptr,1,0) = X MVAL(a,1,0) * MVAL(b,0,0) X + MVAL(a,1,1) * MVAL(b,1,0) X + MVAL(a,1,2) * MVAL(b,2,0); X XMVAL(mptr,1,1) = X MVAL(a,1,0) * MVAL(b,0,1) X + MVAL(a,1,1) * MVAL(b,1,1) X + MVAL(a,1,2) * MVAL(b,2,1); X XMVAL(mptr,1,2) = X MVAL(a,1,0) * MVAL(b,0,2) X + MVAL(a,1,1) * MVAL(b,1,2) X + MVAL(a,1,2) * MVAL(b,2,2); X XMVAL(mptr,1,3) = 0.0; X XMVAL(mptr,2,0) = X MVAL(a,2,0) * MVAL(b,0,0) X + MVAL(a,2,1) * MVAL(b,1,0) X + MVAL(a,2,2) * MVAL(b,2,0); X XMVAL(mptr,2,1) = X MVAL(a,2,0) * MVAL(b,0,1) X + MVAL(a,2,1) * MVAL(b,1,1) X + MVAL(a,2,2) * MVAL(b,2,1); X XMVAL(mptr,2,2) = X MVAL(a,2,0) * MVAL(b,0,2) X + MVAL(a,2,1) * MVAL(b,1,2) X + MVAL(a,2,2) * MVAL(b,2,2); X XMVAL(mptr,2,3) = 0.0; X XMVAL(mptr,3,0) = X MVAL(a,3,0) * MVAL(b,0,0) X + MVAL(a,3,1) * MVAL(b,1,0) X + MVAL(a,3,2) * MVAL(b,2,0) X + MVAL(b,3,0); X XMVAL(mptr,3,1) = X MVAL(a,3,0) * MVAL(b,0,1) X + MVAL(a,3,1) * MVAL(b,1,1) X + MVAL(a,3,2) * MVAL(b,2,1) X + MVAL(b,3,1); X XMVAL(mptr,3,2) = X MVAL(a,3,0) * MVAL(b,0,2) X + MVAL(a,3,1) * MVAL(b,1,2) X + MVAL(a,3,2) * MVAL(b,2,2) X + MVAL(b,3,2); X XMVAL(mptr,3,3) = 1.0; X X/* copy temp matrix to result if needed */ Xif ( usetemp ) X *result = *mptr; X Xreturn result; X} X X X X X X/* ************************************ X * * X * V3LinMatMul * X * * X ************************************ X X DESCRIPTION: Multiply two affine 4x4 matricies. The X routine assumes the right column and bottom line X of each input matrix is [0 0 0 1]. The output matrix X will have the same property. This is pretty much the X same thing as multiplying two 3x3 matrices. X X If one of the input matrices is the same as the output, X write the result to a temporary matrix during multiplication, X then copy to the output matrix. X X ENTRY: X a -- pointer to left matrix X b -- pointer to right matrix X result -- result matrix X X EXIT: returns 'result' X*/ X XMatrix4 *V3LinMatMul ( a, b, result ) Xregister Matrix4 *a,*b; XMatrix4 *result; X{ Xregister Matrix4 *mptr; Xint usetemp; /* boolean */ XMatrix4 tempx; X X/* decide where intermediate result goes */ Xusetemp = ( a == result || b == result ); Xif ( usetemp ) X mptr = & tempx; Xelse X mptr = result; X XMVAL(mptr,0,0) = X MVAL(a,0,0) * MVAL(b,0,0) X + MVAL(a,0,1) * MVAL(b,1,0) X + MVAL(a,0,2) * MVAL(b,2,0); X XMVAL(mptr,0,1) = X MVAL(a,0,0) * MVAL(b,0,1) X + MVAL(a,0,1) * MVAL(b,1,1) X + MVAL(a,0,2) * MVAL(b,2,1); X XMVAL(mptr,0,2) = X MVAL(a,0,0) * MVAL(b,0,2) X + MVAL(a,0,1) * MVAL(b,1,2) X + MVAL(a,0,2) * MVAL(b,2,2); X XMVAL(mptr,0,3) = 0.0; X XMVAL(mptr,1,0) = X MVAL(a,1,0) * MVAL(b,0,0) X + MVAL(a,1,1) * MVAL(b,1,0) X + MVAL(a,1,2) * MVAL(b,2,0); X XMVAL(mptr,1,1) = X MVAL(a,1,0) * MVAL(b,0,1) X + MVAL(a,1,1) * MVAL(b,1,1) X + MVAL(a,1,2) * MVAL(b,2,1); X XMVAL(mptr,1,2) = X MVAL(a,1,0) * MVAL(b,0,2) X + MVAL(a,1,1) * MVAL(b,1,2) X + MVAL(a,1,2) * MVAL(b,2,2); X XMVAL(mptr,1,3) = 0.0; X XMVAL(mptr,2,0) = X MVAL(a,2,0) * MVAL(b,0,0) X + MVAL(a,2,1) * MVAL(b,1,0) X + MVAL(a,2,2) * MVAL(b,2,0); X XMVAL(mptr,2,1) = X MVAL(a,2,0) * MVAL(b,0,1) X + MVAL(a,2,1) * MVAL(b,1,1) X + MVAL(a,2,2) * MVAL(b,2,1); X XMVAL(mptr,2,2) = X MVAL(a,2,0) * MVAL(b,0,2) X + MVAL(a,2,1) * MVAL(b,1,2) X + MVAL(a,2,2) * MVAL(b,2,2); X XMVAL(mptr,2,3) = 0.0; X XMVAL(mptr,3,0) = 0.0; XMVAL(mptr,3,1) = 0.0; XMVAL(mptr,3,2) = 0.0; XMVAL(mptr,3,3) = 1.0; X X/* copy temp matrix to result if needed */ Xif ( usetemp ) X *result = *mptr; X Xreturn result; X} END_OF_FILE if test 12335 -ne `wc -c <'AALines/FastMatMul.c'`; then echo shar: \"'AALines/FastMatMul.c'\" unpacked with wrong size! fi # end of 'AALines/FastMatMul.c' fi if test -f 'FitCurves.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'FitCurves.c'\" else echo shar: Extracting \"'FitCurves.c'\" \(14420 characters\) sed "s/^X//" >'FitCurves.c' <<'END_OF_FILE' X/* XAn Algorithm for Automatically Fitting Digitized Curves Xby Philip J. Schneider Xfrom "Graphics Gems", Academic Press, 1990 X*/ X X#define TESTMODE X X/* fit_cubic.c */ X/* Piecewise cubic fitting code */ X X#include "GraphicsGems.h" X#include <stdio.h> X#include <malloc.h> X#include <math.h> X Xtypedef Point2 *BezierCurve; X X/* Forward declarations */ Xvoid FitCurve(); Xstatic void FitCubic(); Xstatic double *Reparameterize(); Xstatic double NewtonRaphsonRootFind(); Xstatic Point2 Bezier(); Xstatic double B0(), B1(), B2(), B3(); Xstatic Vector2 ComputeLeftTangent(); Xstatic Vector2 ComputeRightTangent(); Xstatic Vector2 ComputeCenterTangent(); Xstatic double ComputeMaxError(); Xstatic double *ChordLengthParameterize(); Xstatic BezierCurve GenerateBezier(); Xstatic Vector2 V2AddII(); Xstatic Vector2 V2ScaleII(); Xstatic Vector2 V2SubII(); X X#define MAXPOINTS 1000 /* The most points you can have */ X X#ifdef TESTMODE X XDrawBezierCurve(n, curve) Xint n; XBezierCurve curve; X{ X /* You'll have to write this yourself. */ X} X X/* X * main: X * Example of how to use the curve-fitting code. Given an array X * of points and a tolerance (squared error between points and X * fitted curve), the algorithm will generate a piecewise X * cubic Bezier representation that approximates the points. X * When a cubic is generated, the routine "DrawBezierCurve" X * is called, which outputs the Bezier curve just created X * (arguments are the degree and the control points, respectively). X * Users will have to implement this function themselves X * ascii output, etc. X * X */ Xmain() X{ X static Point2 d[7] = { /* Digitized points */ X { 0.0, 0.0 }, X { 0.0, 0.5 }, X { 1.1, 1.4 }, X { 2.1, 1.6 }, X { 3.2, 1.1 }, X { 4.0, 0.2 }, X { 4.0, 0.0 }, X }; X double error = 4.0; /* Squared error */ X FitCurve(d, 7, error); /* Fit the Bezier curves */ X} X#endif /* TESTMODE */ X X/* X * FitCurve : X * Fit a Bezier curve to a set of digitized points X */ Xvoid FitCurve(d, nPts, error) X Point2 *d; /* Array of digitized points */ X int nPts; /* Number of digitized points */ X double error; /* User-defined error squared */ X{ X Vector2 tHat1, tHat2; /* Unit tangent vectors at endpoints */ X X tHat1 = ComputeLeftTangent(d, 0); X tHat2 = ComputeRightTangent(d, nPts - 1); X FitCubic(d, 0, nPts - 1, tHat1, tHat2, error); X} X X X X/* X * FitCubic : X * Fit a Bezier curve to a (sub)set of digitized points X */ Xstatic void FitCubic(d, first, last, tHat1, tHat2, error) X Point2 *d; /* Array of digitized points */ X int first, last; /* Indices of first and last pts in region */ X Vector2 tHat1, tHat2; /* Unit tangent vectors at endpoints */ X double error; /* User-defined error squared */ X{ X BezierCurve bezCurve; /*Control points of fitted Bezier curve*/ X double *u; /* Parameter values for point */ X double *uPrime; /* Improved parameter values */ X double maxError; /* Maximum fitting error */ X int splitPoint; /* Point to split point set at */ X int nPts; /* Number of points in subset */ X double iterationError; /*Error below which you try iterating */ X int maxIterations = 4; /* Max times to try iterating */ X Vector2 tHatCenter; /* Unit tangent vector at splitPoint */ X int i; X X iterationError = error * error; X nPts = last - first + 1; X X /* Use heuristic if region only has two points in it */ X if (nPts == 2) { X double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0; X X bezCurve = (Point2 *)malloc(4 * sizeof(Point2)); X bezCurve[0] = d[first]; X bezCurve[3] = d[last]; X V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]); X V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]); X DrawBezierCurve(3, bezCurve); X return; X } X X /* Parameterize points, and attempt to fit curve */ X u = ChordLengthParameterize(d, first, last); X bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2); X X /* Find max deviation of points to fitted curve */ X maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint); X if (maxError < error) { X DrawBezierCurve(3, bezCurve); X return; X } X X X /* If error not too large, try some reparameterization */ X /* and iteration */ X if (maxError < iterationError) { X for (i = 0; i < maxIterations; i++) { X uPrime = Reparameterize(d, first, last, u, bezCurve); X bezCurve = GenerateBezier(d, first, last, uPrime, tHat1, tHat2); X maxError = ComputeMaxError(d, first, last, X bezCurve, uPrime, &splitPoint); X if (maxError < error) { X DrawBezierCurve(3, bezCurve); X return; X } X free((char *)u); X u = uPrime; X } X} X X /* Fitting failed -- split at max error point and fit recursively */ X tHatCenter = ComputeCenterTangent(d, splitPoint); X FitCubic(d, first, splitPoint, tHat1, tHatCenter, error); X V2Negate(&tHatCenter); X FitCubic(d, splitPoint, last, tHatCenter, tHat2, error); X} X X X/* X * GenerateBezier : X * Use least-squares method to find Bezier control points for region. X * X */ Xstatic BezierCurve GenerateBezier(d, first, last, uPrime, tHat1, tHat2) X Point2 *d; /* Array of digitized points */ X int first, last; /* Indices defining region */ X double *uPrime; /* Parameter values for region */ X Vector2 tHat1, tHat2; /* Unit tangents at endpoints */ X{ X int i; X Vector2 A[MAXPOINTS][2]; /* Precomputed rhs for eqn */ X int nPts; /* Number of pts in sub-curve */ X double C[2][2]; /* Matrix C */ X double X[2]; /* Matrix X */ X double det_C0_C1, /* Determinants of matrices */ X det_C0_X, X det_X_C1; X double alpha_l, /* Alpha values, left and right */ X alpha_r; X Vector2 tmp; /* Utility variable */ X BezierCurve bezCurve; /* RETURN bezier curve ctl pts */ X X bezCurve = (Point2 *)malloc(4 * sizeof(Point2)); X nPts = last - first + 1; X X X /* Compute the A's */ X for (i = 0; i < nPts; i++) { X Vector2 v1, v2; X v1 = tHat1; X v2 = tHat2; X V2Scale(&v1, B1(uPrime[i])); X V2Scale(&v2, B2(uPrime[i])); X A[i][0] = v1; X A[i][1] = v2; X } X X /* Create the C and X matrices */ X C[0][0] = 0.0; X C[0][1] = 0.0; X C[1][0] = 0.0; X C[1][1] = 0.0; X X[0] = 0.0; X X[1] = 0.0; X X for (i = 0; i < nPts; i++) { X C[0][0] += V2Dot(&A[i][0], &A[i][0]); X C[0][1] += V2Dot(&A[i][0], &A[i][1]); X/* C[1][0] += V2Dot(&A[i][0], &A[i][1]);*/ X C[1][0] = C[0][1]; X C[1][1] += V2Dot(&A[i][1], &A[i][1]); X X tmp = V2SubII(d[first + i], X V2AddII( X V2ScaleII(d[first], B0(uPrime[i])), X V2AddII( X V2ScaleII(d[first], B1(uPrime[i])), X V2AddII( X V2ScaleII(d[last], B2(uPrime[i])), X V2ScaleII(d[last], B3(uPrime[i])))))); X X X X[0] += V2Dot(&A[i][0], &tmp); X X[1] += V2Dot(&A[i][1], &tmp); X } X X /* Compute the determinants of C and X */ X det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]; X det_C0_X = C[0][0] * X[1] - C[0][1] * X[0]; X det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]; X X /* Finally, derive alpha values */ X if (det_C0_C1 == 0.0) { X det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12; X } X alpha_l = det_X_C1 / det_C0_C1; X alpha_r = det_C0_X / det_C0_C1; X X X /* If alpha negative, use the Wu/Barsky heuristic (see text) */ X if (alpha_l < 0.0 || alpha_r < 0.0) { X double dist = V2DistanceBetween2Points(&d[last], &d[first]) / X 3.0; X X bezCurve[0] = d[first]; X bezCurve[3] = d[last]; X V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]); X V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]); X return (bezCurve); X } X X /* First and last control points of the Bezier curve are */ X /* positioned exactly at the first and last data points */ X /* Control points 1 and 2 are positioned an alpha distance out */ X /* on the tangent vectors, left and right, respectively */ X bezCurve[0] = d[first]; X bezCurve[3] = d[last]; X V2Add(&bezCurve[0], V2Scale(&tHat1, alpha_l), &bezCurve[1]); X V2Add(&bezCurve[3], V2Scale(&tHat2, alpha_r), &bezCurve[2]); X return (bezCurve); X} X X X/* X * Reparameterize: X * Given set of points and their parameterization, try to find X * a better parameterization. X * X */ Xstatic double *Reparameterize(d, first, last, u, bezCurve) X Point2 *d; /* Array of digitized points */ X int first, last; /* Indices defining region */ X double *u; /* Current parameter values */ X BezierCurve bezCurve; /* Current fitted curve */ X{ X int nPts = last-first+1; X int i; X double *uPrime; /* New parameter values */ X X uPrime = (double *)malloc(nPts * sizeof(double)); X for (i = first; i <= last; i++) { X uPrime[i-first] = NewtonRaphsonRootFind(bezCurve, d[i], u[i- X first]); X } X return (uPrime); X} X X X X/* X * NewtonRaphsonRootFind : X * Use Newton-Raphson iteration to find better root. X */ Xstatic double NewtonRaphsonRootFind(Q, P, u) X BezierCurve Q; /* Current fitted curve */ X Point2 P; /* Digitized point */ X double u; /* Parameter value for "P" */ X{ X double numerator, denominator; X Point2 Q1[3], Q2[2]; /* Q' and Q'' */ X Point2 Q_u, Q1_u, Q2_u; /*u evaluated at Q, Q', & Q'' */ X double uPrime; /* Improved u */ X int i; X X /* Compute Q(u) */ X Q_u = Bezier(3, Q, u); X X /* Generate control vertices for Q' */ X for (i = 0; i <= 2; i++) { X Q1[i].x = (Q[i+1].x - Q[i].x) * 3.0; X Q1[i].y = (Q[i+1].y - Q[i].y) * 3.0; X } X X /* Generate control vertices for Q'' */ X for (i = 0; i <= 1; i++) { X Q2[i].x = (Q1[i+1].x - Q1[i].x) * 2.0; X Q2[i].y = (Q1[i+1].y - Q1[i].y) * 2.0; X } X X /* Compute Q'(u) and Q''(u) */ X Q1_u = Bezier(2, Q1, u); X Q2_u = Bezier(1, Q2, u); X X /* Compute f(u)/f'(u) */ X numerator = (Q_u.x - P.x) * (Q1_u.x) + (Q_u.y - P.y) * (Q1_u.y); X denominator = (Q1_u.x) * (Q1_u.x) + (Q1_u.y) * (Q1_u.y) + X (Q_u.x - P.x) * (Q2_u.x) + (Q_u.y - P.y) * (Q2_u.y); X X /* u = u - f(u)/f'(u) */ X uPrime = u - (numerator/denominator); X return (uPrime); X} X X X X/* X * Bezier : X * Evaluate a Bezier curve at a particular parameter value X * X */ Xstatic Point2 Bezier(degree, V, t) X int degree; /* The degree of the bezier curve */ X Point2 *V; /* Array of control points */ X double t; /* Parametric value to find point for */ X{ X int i, j; X Point2 Q; /* Point on curve at parameter t */ X Point2 *Vtemp; /* Local copy of control points */ X X /* Copy array */ X Vtemp = (Point2 *)malloc((unsigned)((degree+1) X * sizeof (Point2))); X for (i = 0; i <= degree; i++) { X Vtemp[i] = V[i]; X } X X /* Triangle computation */ X for (i = 1; i <= degree; i++) { X for (j = 0; j <= degree-i; j++) { X Vtemp[j].x = (1.0 - t) * Vtemp[j].x + t * Vtemp[j+1].x; X Vtemp[j].y = (1.0 - t) * Vtemp[j].y + t * Vtemp[j+1].y; X } X } X X Q = Vtemp[0]; X free((char *)Vtemp); X return Q; X} X X X/* X * B0, B1, B2, B3 : X * Bezier multipliers X */ Xstatic double B0(u) X double u; X{ X double tmp = 1.0 - u; X return (tmp * tmp * tmp); X} X X Xstatic double B1(u) X double u; X{ X double tmp = 1.0 - u; X return (3 * u * (tmp * tmp)); X} X Xstatic double B2(u) X double u; X{ X double tmp = 1.0 - u; X return (3 * u * u * tmp); X} X Xstatic double B3(u) X double u; X{ X return (u * u * u); X} X X X X/* X * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent : X *Approximate unit tangents at endpoints and "center" of digitized curve X */ Xstatic Vector2 ComputeLeftTangent(d, end) X Point2 *d; /* Digitized points*/ X int end; /* Index to "left" end of region */ X{ X Vector2 tHat1; X tHat1 = V2SubII(d[end+1], d[end]); X tHat1 = *V2Normalize(&tHat1); X return tHat1; X} X Xstatic Vector2 ComputeRightTangent(d, end) X Point2 *d; /* Digitized points */ X int end; /* Index to "right" end of region */ X{ X Vector2 tHat2; X tHat2 = V2SubII(d[end-1], d[end]); X tHat2 = *V2Normalize(&tHat2); X return tHat2; X} X X Xstatic Vector2 ComputeCenterTangent(d, center) X Point2 *d; /* Digitized points */ X int center; /* Index to point inside region */ X{ X Vector2 V1, V2, tHatCenter; X X V1 = V2SubII(d[center-1], d[center]); X V2 = V2SubII(d[center], d[center+1]); X tHatCenter.x = (V1.x + V2.x)/2.0; X tHatCenter.y = (V1.y + V2.y)/2.0; X tHatCenter = *V2Normalize(&tHatCenter); X return tHatCenter; X} X X X/* X * ChordLengthParameterize : X * Assign parameter values to digitized points X * using relative distances between points. X */ Xstatic double *ChordLengthParameterize(d, first, last) X Point2 *d; /* Array of digitized points */ X int first, last; /* Indices defining region */ X{ X int i; X double *u; /* Parameterization */ X X u = (double *)malloc((unsigned)(last-first+1) * sizeof(double)); X X u[0] = 0.0; X for (i = first+1; i <= last; i++) { X u[i-first] = u[i-first-1] + X V2DistanceBetween2Points(&d[i], &d[i-1]); X } X X for (i = first + 1; i <= last; i++) { X u[i-first] = u[i-first] / u[last-first]; X } X X return(u); X} X X X X X/* X * ComputeMaxError : X * Find the maximum squared distance of digitized points X * to fitted curve. X*/ Xstatic double ComputeMaxError(d, first, last, bezCurve, u, splitPoint) X Point2 *d; /* Array of digitized points */ X int first, last; /* Indices defining region */ X BezierCurve bezCurve; /* Fitted Bezier curve */ X double *u; /* Parameterization of points */ X int *splitPoint; /* Point of maximum error */ X{ X int i; X double maxDist; /* Maximum error */ X double dist; /* Current error */ X Point2 P; /* Point on curve */ X Vector2 v; /* Vector from point to curve */ X X *splitPoint = (last - first + 1)/2; X maxDist = 0.0; X for (i = first + 1; i < last; i++) { X P = Bezier(3, bezCurve, u[i-first]); X v = V2SubII(P, d[i]); X dist = V2SquaredLength(&v); X if (dist >= maxDist) { X maxDist = dist; X *splitPoint = i; X } X } X return (maxDist); X} Xstatic Vector2 V2AddII(a, b) X Vector2 a, b; X{ X Vector2 c; X c.x = a.x + b.x; c.y = a.y + b.y; X return (c); X} Xstatic Vector2 V2ScaleII(v, s) X Vector2 v; X double s; X{ X Vector2 result; X result.x = v.x * s; result.y = v.y * s; X return (result); X} X Xstatic Vector2 V2SubII(a, b) X Vector2 a, b; X{ X Vector2 c; X c.x = a.x - b.x; c.y = a.y - b.y; X return (c); X} END_OF_FILE if test 14420 -ne `wc -c <'FitCurves.c'`; then echo shar: \"'FitCurves.c'\" unpacked with wrong size! fi # end of 'FitCurves.c' fi if test -f 'GGVecLib.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'GGVecLib.c'\" else echo shar: Extracting \"'GGVecLib.c'\" \(10092 characters\) sed "s/^X//" >'GGVecLib.c' <<'END_OF_FILE' X/* X2d and 3d Vector C Library Xby Andrew Glassner Xfrom "Graphics Gems", Academic Press, 1990 X*/ X X#include <math.h> X#include "GraphicsGems.h" X X/******************/ X/* 2d Library */ X/******************/ X X/* returns squared length of input vector */ Xdouble V2SquaredLength(a) XVector2 *a; X{ return((a->x * a->x)+(a->y * a->y)); X }; X X/* returns length of input vector */ Xdouble V2Length(a) XVector2 *a; X{ X return(sqrt(V2SquaredLength(a))); X }; X X/* negates the input vector and returns it */ XVector2 *V2Negate(v) XVector2 *v; X{ X v->x = -v->x; v->y = -v->y; X return(v); X }; X X/* normalizes the input vector and returns it */ XVector2 *V2Normalize(v) XVector2 *v; X{ Xdouble len = V2Length(v); X if (len != 0.0) { v->x /= len; v->y /= len; }; X return(v); X }; X X X/* scales the input vector to the new length and returns it */ XVector2 *V2Scale(v, newlen) XVector2 *v; Xdouble newlen; X{ Xdouble len = V2Length(v); X if (len != 0.0) { v->x *= newlen/len; v->y *= newlen/len; }; X return(v); X }; X X/* return vector sum c = a+b */ XVector2 *V2Add(a, b, c) XVector2 *a, *b, *c; X{ X c->x = a->x+b->x; c->y = a->y+b->y; X return(c); X }; X X/* return vector difference c = a-b */ XVector2 *V2Sub(a, b, c) XVector2 *a, *b, *c; X{ X c->x = a->x-b->x; c->y = a->y-b->y; X return(c); X }; X X/* return the dot product of vectors a and b */ Xdouble V2Dot(a, b) XVector2 *a, *b; X{ X return((a->x*b->x)+(a->y*b->y)); X }; X X/* linearly interpolate between vectors by an amount alpha */ X/* and return the resulting vector. */ X/* When alpha=0, result=lo. When alpha=1, result=hi. */ XVector2 *V2Lerp(lo, hi, alpha, result) XVector2 *lo, *hi, *result; Xdouble alpha; X{ X result->x = LERP(alpha, lo->x, hi->x); X result->y = LERP(alpha, lo->y, hi->y); X return(result); X }; X X X/* make a linear combination of two vectors and return the result. */ X/* result = (a * ascl) + (b * bscl) */ XVector2 *V2Combine (a, b, result, ascl, bscl) XVector2 *a, *b, *result; Xdouble ascl, bscl; X{ X result->x = (ascl * a->x) + (bscl * b->x); X result->y = (ascl * a->y) + (bscl * b->y); X return(result); X }; X X/* multiply two vectors together component-wise */ XVector2 *V2Mul (a, b, result) XVector2 *a, *b, *result; X{ X result->x = a->x * b->x; X result->y = a->y * b->y; X return(result); X }; X X/* return the distance between two points */ Xdouble V2DistanceBetween2Points(a, b) XPoint2 *a, *b; X{ Xdouble dx = a->x - b->x; Xdouble dy = a->y - b->y; X return(sqrt((dx*dx)+(dy*dy))); X }; X X/* return the vector perpendicular to the input vector a */ XVector2 *V2MakePerpendicular(a, ap) XVector2 *a, *ap; X{ X ap->x = -a->y; X ap->y = a->x; X return(ap); X }; X X/* create, initialize, and return a new vector */ XVector2 *V2New(x, y) Xdouble x, y; X{ XVector2 *v = NEWTYPE(Vector2); X v->x = x; v->y = y; X return(v); X }; X X X/* create, initialize, and return a duplicate vector */ XVector2 *V2Duplicate(a) XVector2 *a; X{ XVector2 *v = NEWTYPE(Vector2); X v->x = a->x; v->y = a->y; X return(v); X }; X X/* multiply a point by a matrix and return the transformed point */ XPoint2 *V2MulPointByMatrix(p, m) XPoint2 *p; XMatrix3 *m; X{ Xdouble w; X p->x = (p->x * m->element[0][0]) + X (p->y * m->element[1][0]) + m->element[2][0]; X p->y = (p->x * m->element[0][1]) + X (p->y * m->element[1][1]) + m->element[2][1]; X w = (p->x * m->element[0][2]) + X (p->y * m->element[1][2]) + m->element[2][2]; X if (w != 0.0) { p->x /= w; p->y /= w; } X return(p); X }; X X/* multiply together matrices c = ab */ X/* note that c must not point to either of the input matrices */ XMatrix3 *V2MatMul(a, b, c) XMatrix3 *a, *b, *c; X{ Xint i, j, k; X for (i=0; i<3; i++) { X for (j=0; j<3; j++) { X c->element[i][j] = 0; X for (k=0; k<3; k++) c->element[i][j] += X a->element[i][k] * b->element[k][j]; X }; X }; X return(c); X }; X X X X X/******************/ X/* 3d Library */ X/******************/ X X/* returns squared length of input vector */ Xdouble V3SquaredLength(a) XVector3 *a; X{ X return((a->x * a->x)+(a->y * a->y)+(a->z * a->z)); X }; X X/* returns length of input vector */ Xdouble V3Length(a) XVector3 *a; X{ X return(sqrt(V3SquaredLength(a))); X }; X X/* negates the input vector and returns it */ XVector3 *V3Negate(v) XVector3 *v; X{ X v->x = -v->x; v->y = -v->y; v->z = -v->z; X return(v); X }; X X/* normalizes the input vector and returns it */ XVector3 *V3Normalize(v) XVector3 *v; X{ Xdouble len = V3Length(v); X if (len != 0.0) { v->x /= len; v->y /= len; v->z /= len; }; X return(v); X }; X X/* scales the input vector to the new length and returns it */ XVector3 *V3Scale(v, newlen) XVector3 *v; Xdouble newlen; X{ Xdouble len = V3Length(v); X if (len != 0.0) { X v->x *= newlen/len; v->y *= newlen/len; v->z *= newlen/len; X }; X return(v); X }; X X X/* return vector sum c = a+b */ XVector3 *V3Add(a, b, c) XVector3 *a, *b, *c; X{ X c->x = a->x+b->x; c->y = a->y+b->y; c->z = a->z+b->z; X return(c); X }; X X/* return vector difference c = a-b */ XVector3 *V3Sub(a, b, c) XVector3 *a, *b, *c; X{ X c->x = a->x-b->x; c->y = a->y-b->y; c->z = a->z-b->z; X return(c); X }; X X/* return the dot product of vectors a and b */ Xdouble V3Dot(a, b) XVector3 *a, *b; X{ X return((a->x*b->x)+(a->y*b->y)+(a->z*b->z)); X }; X X/* linearly interpolate between vectors by an amount alpha */ X/* and return the resulting vector. */ X/* When alpha=0, result=lo. When alpha=1, result=hi. */ XVector3 *V3Lerp(lo, hi, alpha, result) XVector3 *lo, *hi, *result; Xdouble alpha; X{ X result->x = LERP(alpha, lo->x, hi->x); X result->y = LERP(alpha, lo->y, hi->y); X result->z = LERP(alpha, lo->z, hi->z); X return(result); X }; X X/* make a linear combination of two vectors and return the result. */ X/* result = (a * ascl) + (b * bscl) */ XVector3 *V3Combine (a, b, result, ascl, bscl) XVector3 *a, *b, *result; Xdouble ascl, bscl; X{ X result->x = (ascl * a->x) + (bscl * b->x); X result->y = (ascl * a->y) + (bscl * b->y); X result->y = (ascl * a->z) + (bscl * b->z); X return(result); X }; X X X/* multiply two vectors together component-wise and return the result */ XVector3 *V3Mul (a, b, result) XVector3 *a, *b, *result; X{ X result->x = a->x * b->x; X result->y = a->y * b->y; X result->z = a->z * b->z; X return(result); X }; X X/* return the distance between two points */ Xdouble V3DistanceBetween2Points(a, b) XPoint3 *a, *b; X{ Xdouble dx = a->x - b->x; Xdouble dy = a->y - b->y; Xdouble dz = a->z - b->z; X return(sqrt((dx*dx)+(dy*dy)+(dz*dz))); X }; X X/* return the cross product c = a cross b */ XVector3 *V3Cross(a, b, c) XVector3 *a, *b, *c; X{ X c->x = (a->y*b->z) - (a->z*b->y); X c->y = (a->z*b->x) - (a->x*b->z); X c->z = (a->x*b->y) - (a->y*b->x); X return(c); X }; X X/* create, initialize, and return a new vector */ XVector3 *V3New(x, y, z) Xdouble x, y, z; X{ XVector3 *v = NEWTYPE(Vector3); X v->x = x; v->y = y; v->z = z; X return(v); X }; X X/* create, initialize, and return a duplicate vector */ XVector3 *V3Duplicate(a) XVector3 *a; X{ XVector3 *v = NEWTYPE(Vector3); X v->x = a->x; v->y = a->y; v->z = a->z; X return(v); X }; X X X/* multiply a point by a matrix and return the transformed point */ XPoint3 *V3MulPointByMatrix(p, m) XPoint3 *p; XMatrix4 *m; X{ Xdouble w; X p->x = (p->x * m->element[0][0]) + (p->y * m->element[1][0]) + X (p->z * m->element[2][0]) + m->element[3][0]; X p->y = (p->x * m->element[0][1]) + (p->y * m->element[1][1]) + X (p->z * m->element[2][1]) + m->element[3][1]; X p->z = (p->x * m->element[0][2]) + (p->y * m->element[1][2]) + X (p->z * m->element[2][2]) + m->element[3][2]; X w = (p->x * m->element[0][3]) + (p->y * m->element[1][3]) + X (p->z * m->element[2][3]) + m->element[3][3]; X if (w != 0.0) { p->x /= w; p->y /= w; p->z /= w; } X return(p); X }; X X/* multiply together matrices c = ab */ X/* note that c must not point to either of the input matrices */ XMatrix4 *V3MatMul(a, b, c) XMatrix4 *a, *b, *c; X{ Xint i, j, k; X for (i=0; i<4; i++) { X for (j=0; j<4; j++) { X c->element[i][j] = 0; X for (k=0; k<4; k++) c->element[i][j] += X a->element[i][k] * b->element[k][j]; X }; X }; X return(c); X }; X X/* binary greatest common divisor by Silver and Terzian. See Knuth */ X/* both inputs must be >= 0 */ Xgcd(u, v) Xint u, v; X{ Xint k, t, f; X if ((u<0) || (v<0)) return(1); /* error if u<0 or v<0 */ X k = 0; f = 1; X while ((0 == (u%2)) && (0 == (v%2))) { X k++; u>>=1; v>>=1, f*=2; X }; X if (u&01) { t = -v; goto B4; } else { t = u; } X B3: if (t > 0) { t >>= 1; } else { t = -((-t) >> 1); }; X B4: if (0 == (t%2)) goto B3; X X if (t > 0) u = t; else v = -t; X if (0 != (t = u - v)) goto B3; X return(u*f); X }; X X/***********************/ X/* Useful Routines */ X/***********************/ X X/* return roots of ax^2+bx+c */ X/* stable algebra derived from Numerical Recipes by Press et al.*/ Xint quadraticRoots(a, b, c, roots) Xdouble a, b, c, *roots; X{ Xdouble d, q; Xint count = 0; X d = (b*b)-(4*a*c); X if (d < 0.0) { *roots = *(roots+1) = 0.0; return(0); }; X q = -0.5 * (b + (SGN(b)*sqrt(d))); X if (a != 0.0) { *roots++ = q/a; count++; } X if (q != 0.0) { *roots++ = c/q; count++; } X return(count); X }; X X X/* generic 1d regula-falsi step. f is function to evaluate */ X/* interval known to contain root is given in left, right */ X/* returns new estimate */ Xdouble RegulaFalsi(f, left, right) Xdouble (*f)(), left, right; X{ Xdouble d = (*f)(right) - (*f)(left); X if (d != 0.0) return (right - (*f)(right)*(right-left)/d); X return((left+right)/2.0); X }; X X/* generic 1d Newton-Raphson step. f is function, df is derivative */ X/* x is current best guess for root location. Returns new estimate */ Xdouble NewtonRaphson(f, df, x) Xdouble (*f)(), (*df)(), x; X{ Xdouble d = (*df)(x); X if (d != 0.0) return (x-((*f)(x)/d)); X return(x-1.0); X }; X X X/* hybrid 1d Newton-Raphson/Regula Falsi root finder. */ X/* input function f and its derivative df, an interval */ X/* left, right known to contain the root, and an error tolerance */ X/* Based on Blinn */ Xdouble findroot(left, right, tolerance, f, df) Xdouble left, right, tolerance; Xdouble (*f)(), (*df)(); X{ Xdouble newx = left; X while (ABS((*f)(newx)) > tolerance) { X newx = NewtonRaphson(f, df, newx); X if (newx < left || newx > right) X newx = RegulaFalsi(f, left, right); X if ((*f)(newx) * (*f)(left) <= 0.0) right = newx; X else left = newx; X }; X return(newx); X }; END_OF_FILE if test 10092 -ne `wc -c <'GGVecLib.c'`; then echo shar: \"'GGVecLib.c'\" unpacked with wrong size! fi # end of 'GGVecLib.c' fi if test -f 'NearestPoint.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'NearestPoint.c'\" else echo shar: Extracting \"'NearestPoint.c'\" \(12269 characters\) sed "s/^X//" >'NearestPoint.c' <<'END_OF_FILE' X/* XSolving the Nearest Point-on-Curve Problem Xand XA Bezier Curve-Based Root-Finder Xby Philip J. Schneider Xfrom "Graphics Gems", Academic Press, 1990 X*/ X X /* point_on_curve.c */ X X#include <stdio.h> X#include <malloc.h> X#include <math.h> X#include "GraphicsGems.h" X X#define TESTMODE X X/* X * Forward declarations X */ XPoint2 NearestPointOnCurve(); Xstatic int FindRoots(); Xstatic Point2 *ConvertToBezierForm(); Xstatic double ComputeXIntercept(); Xstatic int ControlPolygonFlatEnough(); Xstatic int CrossingCount(); Xstatic Point2 Bezier(); Xstatic Vector2 V2ScaleII(); X Xint MAXDEPTH = 64; /* Maximum depth for recursion */ X X#define EPSILON (ldexp(1.0,-MAXDEPTH-1)) /*Flatness control value */ X#define DEGREE 3 /* Cubic Bezier curve */ X#define W_DEGREE 5 /* Degree of eqn to find roots of */ X X#ifdef TESTMODE X/* X * main : X * Given a cubic Bezier curve (i.e., its control points), and some X * arbitrary point in the plane, find the point on the curve X * closest to that arbitrary point. X */ Xmain() X{ X X static Point2 bezCurve[4] = { /* A cubic Bezier curve */ X { 0.0, 0.0 }, X { 1.0, 2.0 }, X { 3.0, 3.0 }, X { 4.0, 2.0 }, X }; X static Point2 arbPoint = { 3.5, 2.0 }; /*Some arbitrary point*/ X Point2 pointOnCurve; /* Nearest point on the curve */ X X /* Find the closest point */ X pointOnCurve = NearestPointOnCurve(arbPoint, bezCurve); X printf("pointOnCurve : (%4.4f, %4.4f)\n", pointOnCurve.x, X pointOnCurve.y); X} X#endif /* TESTMODE */ X X X/* X * NearestPointOnCurve : X * Compute the parameter value of the point on a Bezier X * curve segment closest to some arbtitrary, user-input point. X * Return the point on the curve at that parameter value. X * X */ XPoint2 NearestPointOnCurve(P, V) X Point2 P; /* The user-supplied point */ X Point2 *V; /* Control points of cubic Bezier */ X{ X Point2 *w; /* Ctl pts for 5th-degree eqn */ X double t_candidate[W_DEGREE]; /* Possible roots */ X int n_solutions; /* Number of roots found */ X double t; /* Parameter value of closest pt*/ X X /* Convert problem to 5th-degree Bezier form */ X w = ConvertToBezierForm(P, V); X X /* Find all possible roots of 5th-degree equation */ X n_solutions = FindRoots(w, W_DEGREE, t_candidate, 0); X free((char *)w); X X /* Compare distances of P to all candidates, and to t=0, and t=1 */ X { X double dist, new_dist; X Point2 p; X Vector2 v; X int i; X X X /* Check distance to beginning of curve, where t = 0 */ X dist = V2SquaredLength(V2Sub(&P, &V[0], &v)); X t = 0.0; X X /* Find distances for candidate points */ X for (i = 0; i < n_solutions; i++) { X p = Bezier(V, DEGREE, t_candidate[i], NULL, NULL); X new_dist = V2SquaredLength(V2Sub(&P, &p, &v)); X if (new_dist < dist) { X dist = new_dist; X t = t_candidate[i]; X } X } X X /* Finally, look at distance to end point, where t = 1.0 */ X new_dist = V2SquaredLength(V2Sub(&P, &V[DEGREE], &v)); X if (new_dist < dist) { X dist = new_dist; X t = 1.0; X } X } X X /* Return the point on the curve at parameter value t */ X printf("t : %4.12f\n", t); X return (Bezier(V, DEGREE, t, NULL, NULL)); X} X X X/* X * ConvertToBezierForm : X * Given a point and a Bezier curve, generate a 5th-degree X * Bezier-format equation whose solution finds the point on the X * curve nearest the user-defined point. X */ Xstatic Point2 *ConvertToBezierForm(P, V) X Point2 P; /* The point to find t for */ X Point2 *V; /* The control points */ X{ X int i, j, k, m, n, ub, lb; X double t; /* Value of t for point P */ X int row, column; /* Table indices */ X Vector2 c[DEGREE+1]; /* V(i)'s - P */ X Vector2 d[DEGREE]; /* V(i+1) - V(i) */ X Point2 *w; /* Ctl pts of 5th-degree curve */ X double cdTable[3][4]; /* Dot product of c, d */ X static double z[3][4] = { /* Precomputed "z" for cubics */ X {1.0, 0.6, 0.3, 0.1}, X {0.4, 0.6, 0.6, 0.4}, X {0.1, 0.3, 0.6, 1.0}, X }; X X X /*Determine the c's -- these are vectors created by subtracting*/ X /* point P from each of the control points */ X for (i = 0; i <= DEGREE; i++) { X V2Sub(&V[i], &P, &c[i]); X } X /* Determine the d's -- these are vectors created by subtracting*/ X /* each control point from the next */ X for (i = 0; i <= DEGREE - 1; i++) { X d[i] = V2ScaleII(V2Sub(&V[i+1], &V[i], &d[i]), 3.0); X } X X /* Create the c,d table -- this is a table of dot products of the */ X /* c's and d's */ X for (row = 0; row <= DEGREE - 1; row++) { X for (column = 0; column <= DEGREE; column++) { X cdTable[row][column] = V2Dot(&d[row], &c[column]); X } X } X X /* Now, apply the z's to the dot products, on the skew diagonal*/ X /* Also, set up the x-values, making these "points" */ X w = (Point2 *)malloc((unsigned)(W_DEGREE+1) * sizeof(Point2)); X for (i = 0; i <= W_DEGREE; i++) { X w[i].y = 0.0; X w[i].x = (double)(i) / W_DEGREE; X } X X n = DEGREE; X m = DEGREE-1; X for (k = 0; k <= n + m; k++) { X lb = MAX(0, k - m); X ub = MIN(k, n); X for (i = lb; i <= ub; i++) { X j = k - i; X w[i+j].y += cdTable[j][i] * z[j][i]; X } X } X X return (w); X} X X X/* X * FindRoots : X * Given a 5th-degree equation in Bernstein-Bezier form, find X * all of the roots in the interval [0, 1]. Return the number X * of roots found. X */ Xstatic int FindRoots(w, degree, t, depth) X Point2 *w; /* The control points */ X int degree; /* The degree of the polynomial */ X double *t; /* RETURN candidate t-values */ X int depth; /* The depth of the recursion */ X{ X int i; X Point2 Left[W_DEGREE+1], /* New left and right */ X Right[W_DEGREE+1]; /* control polygons */ X int left_count, /* Solution count from */ X right_count; /* children */ X double left_t[W_DEGREE+1], /* Solutions from kids */ X right_t[W_DEGREE+1]; X X switch (CrossingCount(w, degree)) { X case 0 : { /* No solutions here */ X return 0; X break; X } X case 1 : { /* Unique solution */ X /* Stop recursion when the tree is deep enough */ X /* if deep enough, return 1 solution at midpoint */ X if (depth >= MAXDEPTH) { X t[0] = (w[0].x + w[W_DEGREE].x) / 2.0; X return 1; X } X if (ControlPolygonFlatEnough(w, degree)) { X t[0] = ComputeXIntercept(w, degree); X return 1; X } X break; X } X} X X /* Otherwise, solve recursively after */ X /* subdividing control polygon */ X Bezier(w, degree, 0.5, Left, Right); X left_count = FindRoots(Left, degree, left_t, depth+1); X right_count = FindRoots(Right, degree, right_t, depth+1); X X X /* Gather solutions together */ X for (i = 0; i < left_count; i++) { X t[i] = left_t[i]; X } X for (i = 0; i < right_count; i++) { X t[i+left_count] = right_t[i]; X } X X /* Send back total number of solutions */ X return (left_count+right_count); X} X X X/* X * CrossingCount : X * Count the number of times a Bezier control polygon X * crosses the 0-axis. This number is >= the number of roots. X * X */ Xstatic int CrossingCount(V, degree) X Point2 *V; /* Control pts of Bezier curve */ X int degree; /* Degreee of Bezier curve */ X{ X int i; X int n_crossings = 0; /* Number of zero-crossings */ X int sign, old_sign; /* Sign of coefficients */ X X sign = old_sign = SGN(V[0].y); X for (i = 1; i <= degree; i++) { X sign = SGN(V[i].y); X if (sign != old_sign) n_crossings++; X old_sign = sign; X } X return n_crossings; X} X X X X/* X * ControlPolygonFlatEnough : X * Check if the control polygon of a Bezier curve is flat enough X * for recursive subdivision to bottom out. X * X */ Xstatic int ControlPolygonFlatEnough(V, degree) X Point2 *V; /* Control points */ X int degree; /* Degree of polynomial */ X{ X int i; /* Index variable */ X double *distance; /* Distances from pts to line */ X double max_distance_above; /* maximum of these */ X double max_distance_below; X double error; /* Precision of root */ X Vector2 t; /* Vector from V[0] to V[degree]*/ X double intercept_1, X intercept_2, X left_intercept, X right_intercept; X double a, b, c; /* Coefficients of implicit */ X /* eqn for line from V[0]-V[deg]*/ X X /* Find the perpendicular distance */ X /* from each interior control point to */ X /* line connecting V[0] and V[degree] */ X distance = (double *)malloc((unsigned)(degree + 1) * sizeof(double)); X { X double abSquared; X X /* Derive the implicit equation for line connecting first *' X /* and last control points */ X a = V[0].y - V[degree].y; X b = V[degree].x - V[0].x; X c = V[0].x * V[degree].y - V[degree].x * V[0].y; X X abSquared = (a * a) + (b * b); X X for (i = 1; i < degree; i++) { X /* Compute distance from each of the points to that line */ X distance[i] = a * V[i].x + b * V[i].y + c; X if (distance[i] > 0.0) { X distance[i] = (distance[i] * distance[i]) / abSquared; X } X if (distance[i] < 0.0) { X distance[i] = -((distance[i] * distance[i]) / abSquared); X } X } X } X X X /* Find the largest distance */ X max_distance_above = 0.0; X max_distance_below = 0.0; X for (i = 1; i < degree; i++) { X if (distance[i] < 0.0) { X max_distance_below = MIN(max_distance_below, distance[i]); X }; X if (distance[i] > 0.0) { X max_distance_above = MAX(max_distance_above, distance[i]); X } X } X free((char *)distance); X X { X double det, dInv; X double a1, b1, c1, a2, b2, c2; X X /* Implicit equation for zero line */ X a1 = 0.0; X b1 = 1.0; X c1 = 0.0; X X /* Implicit equation for "above" line */ X a2 = a; X b2 = b; X c2 = c + max_distance_above; X X det = a1 * b2 - a2 * b1; X dInv = 1.0/det; X X intercept_1 = (b1 * c2 - b2 * c1) * dInv; X X /* Implicit equation for "below" line */ X a2 = a; X b2 = b; X c2 = c + max_distance_below; X X det = a1 * b2 - a2 * b1; X dInv = 1.0/det; X X intercept_2 = (b1 * c2 - b2 * c1) * dInv; X } X X /* Compute intercepts of bounding box */ X left_intercept = MIN(intercept_1, intercept_2); X right_intercept = MAX(intercept_1, intercept_2); X X error = 0.5 * (right_intercept-left_intercept); X if (error < EPSILON) { X return 1; X } X else { X return 0; X } X} X X X X/* X * ComputeXIntercept : X * Compute intersection of chord from first control point to last X * with 0-axis. X * X */ Xstatic double ComputeXIntercept(V, degree) X Point2 *V; /* Control points */ X int degree; /* Degree of curve */ X{ X double XLK, YLK, XNM, YNM, XMK, YMK; X double det, detInv; X double S, T; X double X, Y; X X XLK = 1.0 - 0.0; X YLK = 0.0 - 0.0; X XNM = V[degree].x - V[0].x; X YNM = V[degree].y - V[0].y; X XMK = V[0].x - 0.0; X YMK = V[0].y - 0.0; X X det = XNM*YLK - YNM*XLK; X detInv = 1.0/det; X X S = (XNM*YMK - YNM*XMK) * detInv; X T = (XLK*YMK - YLK*XMK) * detInv; X X X = 0.0 + XLK * S; X Y = 0.0 + YLK * S; X X return X; X} X X X/* X * Bezier : X * Evaluate a Bezier curve at a particular parameter value X * Fill in control points for resulting sub-curves if "Left" and X * "Right" are non-null. X * X */ Xstatic Point2 Bezier(V, degree, t, Left, Right) X int degree; /* Degree of bezier curve */ X Point2 *V; /* Control pts */ X double t; /* Parameter value */ X Point2 *Left; /* RETURN left half ctl pts */ X Point2 *Right; /* RETURN right half ctl pts */ X{ X int i, j; /* Index variables */ X Point2 Vtemp[W_DEGREE+1][W_DEGREE+1]; X X X /* Copy control points */ X for (j =0; j <= degree; j++) { X Vtemp[0][j] = V[j]; X } X X /* Triangle computation */ X for (i = 1; i <= degree; i++) { X for (j =0 ; j <= degree - i; j++) { X Vtemp[i][j].x = X (1.0 - t) * Vtemp[i-1][j].x + t * Vtemp[i-1][j+1].x; X Vtemp[i][j].y = X (1.0 - t) * Vtemp[i-1][j].y + t * Vtemp[i-1][j+1].y; X } X } X X if (Left != NULL) { X for (j = 0; j <= degree; j++) { X Left[j] = Vtemp[j][0]; X } X } X if (Right != NULL) { X for (j = 0; j <= degree; j++) { X Right[j] = Vtemp[degree-j][j]; X } X } X X return (Vtemp[degree][0]); X} X Xstatic Vector2 V2ScaleII(v, s) X Vector2 *v; X double s; X{ X Vector2 result; X X result.x = v->x * s; result.y = v->y * s; X return (result); X} END_OF_FILE if test 12269 -ne `wc -c <'NearestPoint.c'`; then echo shar: \"'NearestPoint.c'\" unpacked with wrong size! fi # end of 'NearestPoint.c' fi echo shar: End of archive 5 \(of 5\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 5 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0