/** From Numerical Recipes in C. Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q */ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkASSERT(roots); if (A == 0) { return valid_unit_divide(-C, B, roots); } SkScalar* r = roots; SkScalar R = B*B - 4*A*C; if (R < 0 || SkScalarIsNaN(R)) { // complex roots return 0; } R = SkScalarSqrt(R); SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; r += valid_unit_divide(Q, A, r); r += valid_unit_divide(C, Q, r); if (r - roots == 2) { if (roots[0] > roots[1]) SkTSwap<SkScalar>(roots[0], roots[1]); else if (roots[0] == roots[1]) // nearly-equal? r -= 1; // skip the double root } return (int)(r - roots); }
/** From Numerical Recipes in C. Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q */ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkASSERT(roots); if (A == 0) { return valid_unit_divide(-C, B, roots); } SkScalar* r = roots; SkScalar R = B*B - 4*A*C; if (R < 0 || !SkScalarIsFinite(R)) { // complex roots // if R is infinite, it's possible that it may still produce // useful results if the operation was repeated in doubles // the flipside is determining if the more precise answer // isn't useful because surrounding machinery (e.g., subtracting // the axis offset from C) already discards the extra precision // more investigation and unit tests required... return 0; } R = SkScalarSqrt(R); SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; r += valid_unit_divide(Q, A, r); r += valid_unit_divide(C, Q, r); if (r - roots == 2) { if (roots[0] > roots[1]) SkTSwap<SkScalar>(roots[0], roots[1]); else if (roots[0] == roots[1]) // nearly-equal? r -= 1; // skip the double root } return (int)(r - roots); }
/** Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1 */ int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) { /* At + B == 0 t = -B / A */ #ifdef SK_SCALAR_IS_FIXED return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue); #else return valid_unit_divide(a - b, a - b - b + c, tValue); #endif }
/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is stored in dst[]. Guarantees that the 1/2 quads will be monotonic. */ int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]) { SkASSERT(src); SkASSERT(dst); SkScalar a = src[0].fX; SkScalar b = src[1].fX; SkScalar c = src[2].fX; if (is_not_monotonic(a, b, c)) { SkScalar tValue; if (valid_unit_divide(a - b, a - b - b + c, &tValue)) { SkChopQuadAt(src, dst, tValue); flatten_double_quad_extrema(&dst[0].fX); return 1; } // if we get here, we need to force dst to be monotonic, even though // we couldn't compute a unit_divide value (probably underflow). b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c; } dst[0].set(a, src[0].fY); dst[1].set(b, src[1].fY); dst[2].set(c, src[2].fY); return 0; }
/** Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1 */ int SkDQuad::FindExtrema(const double src[], double tValue[1]) { /* At + B == 0 t = -B / A */ double a = src[0]; double b = src[2]; double c = src[4]; return valid_unit_divide(a - b, a - b - b + c, tValue); }
// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2 // F'(t) = 2 (b - a) + 2 (a - 2b + c) t // F''(t) = 2 (a - 2b + c) // // A = 2 (b - a) // B = 2 (a - 2b + c) // // Maximum curvature for a quadratic means solving // Fx' Fx'' + Fy' Fy'' = 0 // // t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2) // SkScalar SkFindQuadMaxCurvature(const SkPoint src[3]) { SkScalar Ax = src[1].fX - src[0].fX; SkScalar Ay = src[1].fY - src[0].fY; SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX; SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY; SkScalar t = 0; // 0 means don't chop (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t); return t; }
/** From Numerical Recipes in C. Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q */ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { SkASSERT(roots); if (A == 0) return valid_unit_divide(-C, B, roots); SkScalar* r = roots; #ifdef SK_SCALAR_IS_FLOAT float R = B*B - 4*A*C; if (R < 0 || SkScalarIsNaN(R)) { // complex roots return 0; } R = sk_float_sqrt(R); #else Sk64 RR, tmp; RR.setMul(B,B); tmp.setMul(A,C); tmp.shiftLeft(2); RR.sub(tmp); if (RR.isNeg()) return 0; SkFixed R = RR.getSqrt(); #endif SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; r += valid_unit_divide(Q, A, r); r += valid_unit_divide(C, Q, r); if (r - roots == 2) { if (roots[0] > roots[1]) SkTSwap<SkScalar>(roots[0], roots[1]); else if (roots[0] == roots[1]) // nearly-equal? r -= 1; // skip the double root } return (int)(r - roots); }
// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2 // F'(t) = 2 (b - a) + 2 (a - 2b + c) t // F''(t) = 2 (a - 2b + c) // // A = 2 (b - a) // B = 2 (a - 2b + c) // // Maximum curvature for a quadratic means solving // Fx' Fx'' + Fy' Fy'' = 0 // // t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2) // int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) { SkScalar Ax = src[1].fX - src[0].fX; SkScalar Ay = src[1].fY - src[0].fY; SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX; SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY; SkScalar t = 0; // 0 means don't chop #ifdef SK_SCALAR_IS_FLOAT (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t); #else // !!! should I use SkFloat here? seems like it Sk64 numer, denom, tmp; numer.setMul(Ax, -Bx); tmp.setMul(Ay, -By); numer.add(tmp); if (numer.isPos()) // do nothing if numer <= 0 { denom.setMul(Bx, Bx); tmp.setMul(By, By); denom.add(tmp); SkASSERT(!denom.isNeg()); if (numer < denom) { t = numer.getFixedDiv(denom); SkASSERT(t >= 0 && t <= SK_Fixed1); // assert that we're numerically stable (ha!) if ((unsigned)t >= SK_Fixed1) // runtime check for numerical stability t = 0; // ignore the chop } } #endif if (t == 0) { memcpy(dst, src, 3 * sizeof(SkPoint)); return 1; } else { SkChopQuadAt(src, dst, t); return 2; } }
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots) { #ifdef SK_DEBUG { for (int i = 0; i < roots - 1; i++) { SkASSERT(is_unit_interval(tValues[i])); SkASSERT(is_unit_interval(tValues[i+1])); SkASSERT(tValues[i] < tValues[i+1]); } } #endif if (dst) { if (roots == 0) // nothing to chop memcpy(dst, src, 4*sizeof(SkPoint)); else { SkScalar t = tValues[0]; SkPoint tmp[4]; for (int i = 0; i < roots; i++) { SkChopCubicAt(src, dst, t); if (i == roots - 1) break; dst += 3; // have src point to the remaining cubic (after the chop) memcpy(tmp, dst, 4 * sizeof(SkPoint)); src = tmp; // watch out in case the renormalized t isn't in range if (!valid_unit_divide(tValues[i+1] - tValues[i], SK_Scalar1 - tValues[i], &t)) { // if we can't, just create a degenerate cubic dst[4] = dst[5] = dst[6] = src[3]; break; } } } } }
/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is stored in dst[]. Guarantees that the 1/2 quads will be monotonic. */ int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) { SkASSERT(src); SkASSERT(dst); #if 0 static bool once = true; if (once) { once = false; SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 }; SkPoint d[6]; int n = SkChopQuadAtYExtrema(s, d); SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY); } #endif SkScalar a = src[0].fY; SkScalar b = src[1].fY; SkScalar c = src[2].fY; if (is_not_monotonic(a, b, c)) { SkScalar tValue; if (valid_unit_divide(a - b, a - b - b + c, &tValue)) { SkChopQuadAt(src, dst, tValue); flatten_double_quad_extrema(&dst[0].fY); return 1; } // if we get here, we need to force dst to be monotonic, even though // we couldn't compute a unit_divide value (probably underflow). b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c; } dst[0].set(src[0].fX, a); dst[1].set(src[1].fX, b); dst[2].set(src[2].fX, c); return 0; }
/** Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1 */ int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) { /* At + B == 0 t = -B / A */ return valid_unit_divide(a - b, a - b - b + c, tValue); }
/** Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1 */ int SkDQuad::FindExtrema(double a, double b, double c, double tValue[1]) { /* At + B == 0 t = -B / A */ return valid_unit_divide(a - b, a - b - b + c, tValue); }