/*
 *  FitCubic :
 *  	Fit a Bezier curve to a (sub)set of digitized points
 *  
 *  Vec2f	*d              Array of digitized points
 *  int		first, last     Indices of first and last pts in region
 *  Vec2f	tHat1, tHat2	Unit tangent vectors at endpoints
 *  double	error           User-defined error squared
 */
void PathFitter::FitCubic(vector<Vec2f> const &d, vector<BezierCurve> *bezCurves, int first, int last, Vec2f tHat1, Vec2f tHat2, double error)

{
    vector<Vec2f>	bezCurve(4);                                    // Control points of fitted Bezier curve
    vector<double>	*u = new vector<double>(last-first+1);          // Parameter values for point
    vector<double>	*uPrime = new vector<double>(last-first+1);     // Improved parameter values 
    double	maxError;           // Maximum fitting error	 
    int		splitPoint;         // Point to split point set at	 
    int		nPts;               // Number of points in subset 
    double	iterationError;     // Error below which you try iterating
    int		maxIterations = 4;  // Max times to try iterating
    Vec2f	tHatCenter;         // Unit tangent vector at splitPoint
    int		i;		
    
    iterationError = error * error;
    nPts = last - first + 1;
    
    //  Use heuristic if region only has two points in it
    if (nPts == 2) {
        float dist = d[last].distance(d[first]) / 3.0;

		bezCurve[0] = d[first];
		bezCurve[3] = d[last];
        bezCurve[1] = bezCurve[0] + v2Scale(tHat1, dist);
        bezCurve[2] = bezCurve[3] + v2Scale(tHat2, dist);
		addBezierCurve(bezCurve, bezCurves);
		return;
    }
    
    //  Parameterize points, and attempt to fit curve
    chordLengthParameterize(d, first, last, u);
    generateBezier(d, &bezCurve, first, last, *u, tHat1, tHat2);
    
    //  Find max deviation of points to fitted curve    
    maxError = computeMaxError(d, first, last, &bezCurve, *u, &splitPoint);
    if (maxError < error) {
		addBezierCurve(bezCurve, bezCurves);
		return;
    }
    
    
    //  If error not too large, try some reparameterization
    //  and iteration 
    if (maxError < iterationError) {
		for (i = 0; i < maxIterations; i++) {
	    	uPrime = reparameterize(d, first, last, *u, &bezCurve);
	    	generateBezier(d, &bezCurve, first, last, *uPrime, tHat1, tHat2);
	    	maxError = computeMaxError(d, first, last, &bezCurve, *uPrime, &splitPoint);
	    	if (maxError < error) {
                addBezierCurve(bezCurve, bezCurves);
                return;
            }
            u = uPrime;
        }
    }
    
    // Fitting failed -- split at max error point and fit recursively
    tHatCenter = computeCenterTangent(d, splitPoint);
    FitCubic(d, bezCurves, first, splitPoint, tHat1, tHatCenter, error);
    FitCubic(d, bezCurves, splitPoint, last, tHatCenter.inverse(), tHat2, error);
}