/** Trim A/B/C down so that they are all <= 32bits and then call SkFindUnitQuadRoots() */ static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2]) { int na = A.shiftToMake32(); int nb = B.shiftToMake32(); int nc = C.shiftToMake32(); int shift = SkMax32(na, SkMax32(nb, nc)); SkASSERT(shift >= 0); return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots); }
/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html Inflection means that curvature is zero. Curvature is [F' x F''] / [F'^3] So we solve F'x X F''y - F'y X F''y == 0 After some canceling of the cubic term, we get A = b - a B = c - 2b + a C = d - 3c + 3b - a (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0 */ int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[]) { SkScalar Ax = src[1].fX - src[0].fX; SkScalar Ay = src[1].fY - src[0].fY; SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX; SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY; SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX; SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY; return SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues); }
/* Find t value for quadratic [a, b, c] = d. Return 0 if there is no solution within [0, 1) */ static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d) { // At^2 + Bt + C = d SkScalar A = a - 2 * b + c; SkScalar B = 2 * (b - a); SkScalar C = a - d; SkScalar roots[2]; int count = SkFindUnitQuadRoots(A, B, C, roots); SkASSERT(count <= 1); return count == 1 ? roots[0] : 0; }
/** Cubic'(t) = At^2 + Bt + C, where A = 3(-a + 3(b - c) + d) B = 6(a - 2b + c) C = 3(b - a) Solve for t, keeping only those that fit betwee 0 < t < 1 */ int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]) { #ifdef SK_SCALAR_IS_FIXED if (!is_not_monotonic(a, b, c, d)) return 0; #endif // we divide A,B,C by 3 to simplify SkScalar A = d - a + 3*(b - c); SkScalar B = 2*(a - b - b + c); SkScalar C = b - a; return SkFindUnitQuadRoots(A, B, C, tValues); }
static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) { /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2 * We solve for t, using quadratic equation, hence we have to rearrange * our cooefficents to look like At^2 + Bt + C */ SkScalar A = pts[0].fY - pts[1].fY - pts[1].fY + pts[2].fY; SkScalar B = 2*(pts[1].fY - pts[0].fY); SkScalar C = pts[0].fY - y; SkScalar roots[2]; // we only expect one, but make room for 2 for safety int count = SkFindUnitQuadRoots(A, B, C, roots); if (count) { *t = roots[0]; return true; } return false; }
static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar target, SkScalar* t) { /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2 * We solve for t, using quadratic equation, hence we have to rearrange * our cooefficents to look like At^2 + Bt + C */ SkScalar A = c0 - c1 - c1 + c2; SkScalar B = 2*(c1 - c0); SkScalar C = c0 - target; SkScalar roots[2]; // we only expect one, but make room for 2 for safety int count = SkFindUnitQuadRoots(A, B, C, roots); if (count) { *t = roots[0]; return true; } return false; }
/* Solve coeff(t) == 0, returning the number of roots that lie withing 0 < t < 1. coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] Eliminates repeated roots (so that all tValues are distinct, and are always in increasing order. */ static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) { if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); } SkScalar a, b, c, Q, R; { SkASSERT(coeff[0] != 0); SkScalar inva = SkScalarInvert(coeff[0]); a = coeff[1] * inva; b = coeff[2] * inva; c = coeff[3] * inva; } Q = (a*a - b*3) / 9; R = (2*a*a*a - 9*a*b + 27*c) / 54; SkScalar Q3 = Q * Q * Q; SkScalar R2MinusQ3 = R * R - Q3; SkScalar adiv3 = a / 3; SkScalar* roots = tValues; SkScalar r; if (R2MinusQ3 < 0) { // we have 3 real roots SkScalar theta = SkScalarACos(R / SkScalarSqrt(Q3)); SkScalar neg2RootQ = -2 * SkScalarSqrt(Q); r = neg2RootQ * SkScalarCos(theta/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } r = neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } r = neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) { *roots++ = r; } SkDEBUGCODE(test_collaps_duplicates();)
static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { SkScalar y0 = pts[0].fY; SkScalar y2 = pts[2].fY; int dir = 1; if (y0 > y2) { SkTSwap(y0, y2); dir = -1; } if (y < y0 || y >= y2) { return 0; } // bounds check on X (not required, but maybe faster) #if 0 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { return 0; } #endif SkScalar roots[2]; int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2 * (pts[1].fY - pts[0].fY), pts[0].fY - y, roots); SkASSERT(n <= 1); SkScalar xt; if (0 == n) { SkScalar mid = SkScalarAve(y0, y2); // Need [0] and [2] if dir == 1 // and [2] and [0] if dir == -1 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; } else { SkScalar t = roots[0]; SkScalar C = pts[0].fX; SkScalar A = pts[2].fX - 2 * pts[1].fX + C; SkScalar B = 2 * (pts[1].fX - C); xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); } return xt < x ? dir : 0; }
/* Solve coeff(t) == 0, returning the number of roots that lie withing 0 < t < 1. coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] */ static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3]) { #ifndef SK_SCALAR_IS_FLOAT return 0; // this is not yet implemented for software float #endif if (SkScalarNearlyZero(coeff[0])) // we're just a quadratic { return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); } SkFP a, b, c, Q, R; { SkASSERT(coeff[0] != 0); SkFP inva = SkFPInvert(coeff[0]); a = SkFPMul(coeff[1], inva); b = SkFPMul(coeff[2], inva); c = SkFPMul(coeff[3], inva); } Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9); // R = (2*a*a*a - 9*a*b + 27*c) / 54; R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2); R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9)); R = SkFPAdd(R, SkFPMulInt(c, 27)); R = SkFPDivInt(R, 54); SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q); SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3); SkFP adiv3 = SkFPDivInt(a, 3); SkScalar* roots = tValues; SkScalar r; if (SkFPLT(R2MinusQ3, 0)) // we have 3 real roots { #ifdef SK_SCALAR_IS_FLOAT float theta = sk_float_acos(R / sk_float_sqrt(Q3)); float neg2RootQ = -2 * sk_float_sqrt(Q); r = neg2RootQ * sk_float_cos(theta/3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3; if (is_unit_interval(r)) *roots++ = r; // now sort the roots bubble_sort(tValues, (int)(roots - tValues)); #endif } else // we have 1 real root { SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3)); A = SkFPCubeRoot(A); if (SkFPGT(R, 0)) A = SkFPNeg(A); if (A != 0) A = SkFPAdd(A, SkFPDiv(Q, A)); r = SkFPToScalar(SkFPSub(A, adiv3)); if (is_unit_interval(r)) *roots++ = r; } return (int)(roots - tValues); }