/* * GenerateBezier : * Use least-squares method to find Bezier control points for region. * */ static BezierCurve GenerateBezier(Point2 *d, int first, int last, double *uPrime, Vector2 tHat1, Vector2 tHat2) { int i; Vector2 A[MAXPOINTS][2]; /* Precomputed rhs for eqn */ int nPts; /* Number of pts in sub-curve */ double C[2][2]; /* Matrix C */ double X[2]; /* Matrix X */ double det_C0_C1, /* Determinants of matrices */ det_C0_X, det_X_C1; double alpha_l, /* Alpha values, left and right */ alpha_r; Vector2 tmp; /* Utility variable */ BezierCurve bezCurve; /* RETURN bezier curve ctl pts */ bezCurve = (Point2 *)malloc(4 * sizeof(Point2)); nPts = last - first + 1; /* Compute the A's */ for (i = 0; i < nPts; i++) { Vector2 v1, v2; v1 = tHat1; v2 = tHat2; V2Scale(&v1, B1(uPrime[i])); V2Scale(&v2, B2(uPrime[i])); A[i][0] = v1; A[i][1] = v2; } /* Create the C and X matrices */ C[0][0] = 0.0; C[0][1] = 0.0; C[1][0] = 0.0; C[1][1] = 0.0; X[0] = 0.0; X[1] = 0.0; for (i = 0; i < nPts; i++) { C[0][0] += V2Dot(&A[i][0], &A[i][0]); C[0][1] += V2Dot(&A[i][0], &A[i][1]); /* C[1][0] += V2Dot(&A[i][0], &A[i][1]);*/ C[1][0] = C[0][1]; C[1][1] += V2Dot(&A[i][1], &A[i][1]); tmp = V2SubII(d[first + i], V2AddII( V2ScaleIII(d[first], B0(uPrime[i])), V2AddII( V2ScaleIII(d[first], B1(uPrime[i])), V2AddII( V2ScaleIII(d[last], B2(uPrime[i])), V2ScaleIII(d[last], B3(uPrime[i])))))); X[0] += V2Dot(&A[i][0], &tmp); X[1] += V2Dot(&A[i][1], &tmp); } /* Compute the determinants of C and X */ det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]; det_C0_X = C[0][0] * X[1] - C[1][0] * X[0]; det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]; /* Finally, derive alpha values */ alpha_l = (det_C0_C1 < ZERO_TOLERANCE) ? 0.0 : det_X_C1 / det_C0_C1; alpha_r = (det_C0_C1 < ZERO_TOLERANCE) ? 0.0 : det_C0_X / det_C0_C1; /* If alpha negative, use the Wu/Barsky heuristic (see text) */ /* (if alpha is 0, you get coincident control points that lead to * divide by zero in any subsequent NewtonRaphsonRootFind() call. */ double segLength = V2DistanceBetween2Points(&d[last], &d[first]); double epsilon = 1.0e-6 * segLength; if (alpha_l < epsilon || alpha_r < epsilon) { /* fall back on standard (probably inaccurate) formula, and subdivide further if needed. */ double dist = segLength / 3.0; bezCurve[0] = d[first]; bezCurve[3] = d[last]; V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]); V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]); return (bezCurve); } /* First and last control points of the Bezier curve are */ /* positioned exactly at the first and last data points */ /* Control points 1 and 2 are positioned an alpha distance out */ /* on the tangent vectors, left and right, respectively */ bezCurve[0] = d[first]; bezCurve[3] = d[last]; V2Add(&bezCurve[0], V2Scale(&tHat1, alpha_l), &bezCurve[1]); V2Add(&bezCurve[3], V2Scale(&tHat2, alpha_r), &bezCurve[2]); return (bezCurve); }
/* ** Compute uv-coordinates of the point ** Return TRUE if the point is in the quadrangle */ static boolean point_in_quad (QUAD *Quad, HIT *Hit) { char LargestComponent; /* of the normal vector */ Point2 A, B, C, D; /* Projected vertices */ Point2 M; /* Projected intersection point */ Vector2 AB, BC, CD, AD, AM, AE; /* AE = DC - AB = DA - CB */ REAL u, v; /* Parametric coordinates */ REAL a, b, c, SqrtDelta; /* for the quadratic equation */ boolean IsInside = FALSE; /* if u and v are inside */ Vector2 Vector; /* temporary 2D-vector */ /* ** Projection on the plane that is most parallel to the facet */ LargestComponent = LARGEST_COMPONENT(Quad->Normal); if (LargestComponent == 'x') { A.x = Quad->A.y; B.x = Quad->B.y; C.x = Quad->C.y; D.x = Quad->D.y; M.x = Hit->Point.y; } else { A.x = Quad->A.x; B.x = Quad->B.x; C.x = Quad->C.x; D.x = Quad->D.x; M.x = Hit->Point.x; } if (LargestComponent == 'z') { A.y = Quad->A.y; B.y = Quad->B.y; C.y = Quad->C.y; D.y = Quad->D.y; M.y = Hit->Point.y; } else { A.y = Quad->A.z; B.y = Quad->B.z; C.y = Quad->C.z; D.y = Quad->D.z; M.y = Hit->Point.z; } V2Sub (&B, &A, &AB); V2Sub (&C, &B, &BC); V2Sub (&D, &C, &CD); V2Sub (&D, &A, &AD); V2Add (&CD, &AB, &AE); V2Negate (&AE); V2Sub (&M, &A, &AM); if (fabs(DETERMINANT(AB, CD)) < EPSILON) /* case AB // CD */ { V2Sub (&AB, &CD, &Vector); v = DETERMINANT(AM, Vector) / DETERMINANT(AD, Vector); if ((v >= 0.0) && (v <= 1.0)) { b = DETERMINANT(AB, AD) - DETERMINANT(AM, AE); c = DETERMINANT (AM, AD); u = ABS(b) < EPSILON ? -1 : c/b; IsInside = ((u >= 0.0) && (u <= 1.0)); } } else if (fabs(DETERMINANT(BC, AD)) < EPSILON) /* case AD // BC */ { V2Add (&AD, &BC, &Vector); u = DETERMINANT(AM, Vector) / DETERMINANT(AB, Vector); if ((u >= 0.0) && (u <= 1.0)) { b = DETERMINANT(AD, AB) - DETERMINANT(AM, AE); c = DETERMINANT (AM, AB); v = ABS(b) < EPSILON ? -1 : c/b; IsInside = ((v >= 0.0) && (v <= 1.0)); } } else /* general case */ { a = DETERMINANT(AB, AE); c = - DETERMINANT (AM,AD); b = DETERMINANT(AB, AD) - DETERMINANT(AM, AE); a = -0.5/a; b *= a; c *= (a + a); SqrtDelta = b*b + c; if (SqrtDelta >= 0.0) { SqrtDelta = sqrt(SqrtDelta); u = b - SqrtDelta; if ((u < 0.0) || (u > 1.0)) /* to choose u between 0 and 1 */ u = b + SqrtDelta; if ((u >= 0.0) && (u <= 1.0)) { v = AD.x + u * AE.x; if (ABS(v) < EPSILON) v = (AM.y - u * AB.y) / (AD.y + u * AE.y); else v = (AM.x - u * AB.x) / v; IsInside = ((v >= 0.0) && (v <= 1.0)); } } } if (IsInside) { Hit->u = u; Hit->v = v; } return (IsInside); }
/* * FitCubic : * Fit a Bezier curve to a (sub)set of digitized points */ static void FitCubic(Point2 *d, int first, int last, Vector2 tHat1, Vector2 tHat2, double error, BezierContour &bezContour) { BezierCurve bezCurve; /*Control points of fitted Bezier curve*/ double *u; /* Parameter values for point */ double *uPrime; /* 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 */ Vector2 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) { double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0; bezCurve = (Point2 *)malloc(4 * sizeof(Point2)); bezCurve[0] = d[first]; bezCurve[3] = d[last]; V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]); V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]); DrawBezierCurve(3, bezCurve, bezContour); free((void *)bezCurve); return; } /* Parameterize points, and attempt to fit curve */ u = ChordLengthParameterize(d, first, last); bezCurve = GenerateBezier(d, first, last, u, tHat1, tHat2); /* Find max deviation of points to fitted curve */ maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint); if (maxError < error) { DrawBezierCurve(3, bezCurve, bezContour); free((void *)u); free((void *)bezCurve); 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); free((void *)bezCurve); bezCurve = GenerateBezier(d, first, last, uPrime, tHat1, tHat2); maxError = ComputeMaxError(d, first, last, bezCurve, uPrime, &splitPoint); if (maxError < error) { DrawBezierCurve(3, bezCurve, bezContour); free((void *)u); free((void *)bezCurve); free((void *)uPrime); return; } free((void *)u); u = uPrime; } } /* Fitting failed -- split at max error point and fit recursively */ free((void *)u); free((void *)bezCurve); tHatCenter = ComputeCenterTangent(d, splitPoint); FitCubic(d, first, splitPoint, tHat1, tHatCenter, error, bezContour); V2Negate(&tHatCenter); FitCubic(d, splitPoint, last, tHatCenter, tHat2, error, bezContour); }
/* * GenerateBezier : * Use least-squares method to find Bezier control points for region. * */ static BezierCurve GenerateBezier( Point2 *d, /* Array of digitized points */ int first, int last, /* Indices defining region */ double *uPrime, /* Parameter values for region */ Vector2 tHat1, Vector2 tHat2) /* Unit tangents at endpoints */ { int i; // Vector2 A[MAXPOINTS][2]; /* Precomputed rhs for eqn */ int nPts; /* Number of pts in sub-curve */ double C[2][2]; /* Matrix C */ double X[2]; /* Matrix X */ double det_C0_C1, /* Determinants of matrices */ det_C0_X, det_X_C1; double alpha_l, /* Alpha values, left and right */ alpha_r; Vector2 tmp; /* Utility variable */ BezierCurve bezCurve; /* RETURN bezier curve ctl pts */ bezCurve = (Point2 *)malloc(4 * sizeof(Point2)); nPts = last - first + 1; Vector2 (*A)[2]; A = new Vector2[nPts][2]; /* Precomputed rhs for eqn */ /* Compute the A's */ for (i = 0; i < nPts; i++) { Vector2 v1, v2; v1 = tHat1; v2 = tHat2; V2Scale(&v1, B1(uPrime[i])); V2Scale(&v2, B2(uPrime[i])); A[i][0] = v1; A[i][1] = v2; } /* Create the C and X matrices */ C[0][0] = 0.0; C[0][1] = 0.0; C[1][0] = 0.0; C[1][1] = 0.0; X[0] = 0.0; X[1] = 0.0; for (i = 0; i < nPts; i++) { C[0][0] += V2Dot(&A[i][0], &A[i][0]); C[0][1] += V2Dot(&A[i][0], &A[i][1]); /* C[1][0] += V2Dot(&A[i][0], &A[i][1]);*/ C[1][0] = C[0][1]; C[1][1] += V2Dot(&A[i][1], &A[i][1]); tmp = V2SubII(d[first + i], V2AddII( V2ScaleIII(d[first], B0(uPrime[i])), V2AddII( V2ScaleIII(d[first], B1(uPrime[i])), V2AddII( V2ScaleIII(d[last], B2(uPrime[i])), V2ScaleIII(d[last], B3(uPrime[i])))))); X[0] += V2Dot(&A[i][0], &tmp); X[1] += V2Dot(&A[i][1], &tmp); } /* Compute the determinants of C and X */ det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]; det_C0_X = C[0][0] * X[1] - C[0][1] * X[0]; det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]; /* Finally, derive alpha values */ if (det_C0_C1 == 0.0) { det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12; } alpha_l = det_X_C1 / det_C0_C1; alpha_r = det_C0_X / det_C0_C1; /* If alpha negative, use the Wu/Barsky heuristic (see text) */ if (alpha_l < 0.0 || alpha_r < 0.0) { double dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0; bezCurve[0] = d[first]; bezCurve[3] = d[last]; V2Add(&bezCurve[0], V2Scale(&tHat1, dist), &bezCurve[1]); V2Add(&bezCurve[3], V2Scale(&tHat2, dist), &bezCurve[2]); delete[] A; return (bezCurve); } /* First and last control points of the Bezier curve are */ /* positioned exactly at the first and last data points */ /* Control points 1 and 2 are positioned an alpha distance out */ /* on the tangent vectors, left and right, respectively */ bezCurve[0] = d[first]; bezCurve[3] = d[last]; V2Add(&bezCurve[0], V2Scale(&tHat1, alpha_l), &bezCurve[1]); V2Add(&bezCurve[3], V2Scale(&tHat2, alpha_r), &bezCurve[2]); delete[] A; return (bezCurve); }