// this does not discard real roots <= 0 or >= 1 int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) { const double p = B / (2 * A); const double q = C / A; if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) { if (approximately_zero(B)) { s[0] = 0; return C == 0; } s[0] = -C / B; return 1; } /* normal form: x^2 + px + q = 0 */ const double p2 = p * p; if (!AlmostDequalUlps(p2, q) && p2 < q) { return 0; } double sqrt_D = 0; if (p2 > q) { sqrt_D = sqrt(p2 - q); } s[0] = sqrt_D - p; s[1] = -sqrt_D - p; return 1 + !AlmostDequalUlps(s[0], s[1]); }
/* Given a pair of quadratics, determine their parametric coefficients. * If the scaled coefficients are nearly equal, then the part of the quadratics * may be coincident. * OPTIMIZATION -- since comparison short-circuits on no match, * lazily compute the coefficients, comparing the easiest to compute first. * xx and yy first; then xy; and so on. */ bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const { int first = 0; for (int index = 0; index <= kC_Coeff; ++index) { if (approximately_zero(fP[index]) && approximately_zero(p2.fP[index])) { first += first == index; continue; } if (first == index) { continue; } if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) { return false; } } return true; }
int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { #ifdef SK_DEBUG // create a string mathematica understands // GDB set print repe 15 # if repeated digits is a bother // set print elements 400 # if line doesn't fit char str[1024]; sk_bzero(str, sizeof(str)); SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D); SkPathOpsDebug::MathematicaIze(str, sizeof(str)); #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA SkDebugf("%s\n", str); #endif #endif if (approximately_zero(A) && approximately_zero_when_compared_to(A, B) && approximately_zero_when_compared_to(A, C) && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic return SkDQuad::RootsReal(B, C, D, s); } if (approximately_zero_when_compared_to(D, A) && approximately_zero_when_compared_to(D, B) && approximately_zero_when_compared_to(D, C)) { // 0 is one root int num = SkDQuad::RootsReal(A, B, C, s); for (int i = 0; i < num; ++i) { if (approximately_zero(s[i])) { return num; } } s[num++] = 0; return num; } if (approximately_zero(A + B + C + D)) { // 1 is one root int num = SkDQuad::RootsReal(A, A + B, -D, s); for (int i = 0; i < num; ++i) { if (AlmostDequalUlps(s[i], 1)) { return num; } } s[num++] = 1; return num; } double a, b, c; { double invA = 1 / A; a = B * invA; b = C * invA; c = D * invA; } double a2 = a * a; double Q = (a2 - b * 3) / 9; double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; double R2 = R * R; double Q3 = Q * Q * Q; double R2MinusQ3 = R2 - Q3; double adiv3 = a / 3; double r; double* roots = s; if (R2MinusQ3 < 0) { // we have 3 real roots double theta = acos(R / sqrt(Q3)); double neg2RootQ = -2 * sqrt(Q); r = neg2RootQ * cos(theta / 3) - adiv3; *roots++ = r; r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; if (!AlmostDequalUlps(s[0], r)) { *roots++ = r; } r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) { *roots++ = r; } } else { // we have 1 real root double sqrtR2MinusQ3 = sqrt(R2MinusQ3); double A = fabs(R) + sqrtR2MinusQ3; A = SkDCubeRoot(A); if (R > 0) { A = -A; } if (A != 0) { A += Q / A; } r = A - adiv3; *roots++ = r; if (AlmostDequalUlps(R2, Q3)) { r = -A / 2 - adiv3; if (!AlmostDequalUlps(s[0], r)) { *roots++ = r; } } } return static_cast<int>(roots - s); }
int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C, const double D, const double E, double s[4]) { double u, v; /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */ const double invA = 1 / A; const double a = B * invA; const double b = C * invA; const double c = D * invA; const double d = E * invA; /* substitute x = y - a/4 to eliminate cubic term: x^4 + px^2 + qx + r = 0 */ const double a2 = a * a; const double p = -3 * a2 / 8 + b; const double q = a2 * a / 8 - a * b / 2 + c; const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d; int num; if (approximately_zero(r)) { /* no absolute term: y(y^3 + py + q) = 0 */ num = SkDCubic::RootsReal(1, 0, p, q, s); s[num++] = 0; } else { /* solve the resolvent cubic ... */ double cubicRoots[3]; int roots = SkDCubic::RootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots); int index; /* ... and take one real solution ... */ double z; num = 0; int num2 = 0; for (index = firstCubicRoot; index < roots; ++index) { z = cubicRoots[index]; /* ... to build two quadric equations */ u = z * z - r; v = 2 * z - p; if (approximately_zero_squared(u)) { u = 0; } else if (u > 0) { u = sqrt(u); } else { continue; } if (approximately_zero_squared(v)) { v = 0; } else if (v > 0) { v = sqrt(v); } else { continue; } num = SkDQuad::RootsReal(1, q < 0 ? -v : v, z - u, s); num2 = SkDQuad::RootsReal(1, q < 0 ? v : -v, z + u, s + num); if (!((num | num2) & 1)) { break; // prefer solutions without single quad roots } } num += num2; if (!num) { return 0; // no valid cubic root } } /* resubstitute */ const double sub = a / 4; for (int i = 0; i < num; ++i) { s[i] -= sub; } // eliminate duplicates for (int i = 0; i < num - 1; ++i) { for (int j = i + 1; j < num; ) { if (AlmostDequalUlps(s[i], s[j])) { if (j < --num) { s[j] = s[num]; } } else { ++j; } } } return num; }
bool AlmostDequalUlps(double a, double b) { if (SkScalarIsFinite(a) || SkScalarIsFinite(b)) { return AlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } return fabs(a - b) / SkTMax(fabs(a), fabs(b)) < FLT_EPSILON * 16; }