diff --git a/experimental/Intersection/ConvexHull.cpp b/experimental/Intersection/ConvexHull.cpp index e9a08c299..86cb2dc6f 100644 --- a/experimental/Intersection/ConvexHull.cpp +++ b/experimental/Intersection/ConvexHull.cpp @@ -113,10 +113,10 @@ bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) { int lower0Index = 1; int upper0Index = 1; for (index = 2; index < 4; ++index) { - if (approximately_greater(projectedY[lower0Index], projectedY[index])) { + if (approximately_greater_or_equal(projectedY[lower0Index], projectedY[index])) { lower0Index = index; } - if (approximately_lesser(projectedY[upper0Index], projectedY[index])) { + if (approximately_lesser_or_equal(projectedY[upper0Index], projectedY[index])) { upper0Index = index; } } @@ -129,10 +129,10 @@ bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) { int lower3Index = 2; int upper3Index = 2; for (index = 1; index > -1; --index) { - if (approximately_greater(projectedY[lower3Index], projectedY[index])) { + if (approximately_greater_or_equal(projectedY[lower3Index], projectedY[index])) { lower3Index = index; } - if (approximately_lesser(projectedY[upper3Index], projectedY[index])) { + if (approximately_lesser_or_equal(projectedY[upper3Index], projectedY[index])) { upper3Index = index; } } diff --git a/experimental/Intersection/CubicBezierClip.cpp b/experimental/Intersection/CubicBezierClip.cpp index 378e45f22..d4c50fb41 100644 --- a/experimental/Intersection/CubicBezierClip.cpp +++ b/experimental/Intersection/CubicBezierClip.cpp @@ -54,17 +54,17 @@ bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& endLine.cubicDistanceY(cubic2, distance2y); int flags = 0; - if (approximately_lesser(distance2y[0].y, top)) { + if (approximately_lesser_or_equal(distance2y[0].y, top)) { flags |= kFindTopMin; - } else if (approximately_greater(distance2y[0].y, bottom)) { + } else if (approximately_greater_or_equal(distance2y[0].y, bottom)) { flags |= kFindBottomMin; } else { minT = 0; } - if (approximately_lesser(distance2y[3].y, top)) { + if (approximately_lesser_or_equal(distance2y[3].y, top)) { flags |= kFindTopMax; - } else if (approximately_greater(distance2y[3].y, bottom)) { + } else if (approximately_greater_or_equal(distance2y[3].y, bottom)) { flags |= kFindBottomMax; } else { maxT = 1; diff --git a/experimental/Intersection/CubicIntersection.cpp b/experimental/Intersection/CubicIntersection.cpp index f4a7ef785..fe146714a 100644 --- a/experimental/Intersection/CubicIntersection.cpp +++ b/experimental/Intersection/CubicIntersection.cpp @@ -14,7 +14,7 @@ #include "QuadraticUtilities.h" #if ONE_OFF_DEBUG -static const double tLimits[2][2] = {{0.516980827, 0.516981209}, {0.647714088, 0.64771447}}; +static const double tLimits[2][2] = {{0.599274754, 0.599275135}, {0.599274754, 0.599275135}}; #endif #define DEBUG_QUAD_PART 0 @@ -358,6 +358,19 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& const double t2 = t2s + (t2e - t2s) * tEnd2; Quadratic s2; int o2 = quadPart(cubic2, t2Start, t2, s2); + #if ONE_OFF_DEBUG + if (tLimits[0][0] >= t1Start && tLimits[0][1] <= t1 + && tLimits[1][0] >= t2Start && tLimits[1][1] <= t2) { + Cubic cSub1, cSub2; + sub_divide(cubic1, t1Start, tEnd1, cSub1); + sub_divide(cubic2, t2Start, tEnd2, cSub2); + SkDebugf("t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)\n", + t1Start, t1, t2Start, t2); + Intersections xlocals; + intersectWithOrder(s1, o1, s2, o2, xlocals); + SkDebugf("xlocals.fUsed=%d\n", xlocals.used()); + } + #endif Intersections locals; intersectWithOrder(s1, o1, s2, o2, locals); double coStart[2] = { -1 }; diff --git a/experimental/Intersection/CubicIntersection_Test.cpp b/experimental/Intersection/CubicIntersection_Test.cpp index 9dea33244..08bffd61b 100644 --- a/experimental/Intersection/CubicIntersection_Test.cpp +++ b/experimental/Intersection/CubicIntersection_Test.cpp @@ -134,6 +134,12 @@ static const Cubic testSet[] = { const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]); static const Cubic newTestSet[] = { +{{1,2},{5,6},{1,0},{1,0}}, +{{0,1},{0,1},{2,1},{6,5}}, + +{{0,6},{1,2},{1,0},{1,0}}, +{{0,1},{0,1},{6,0},{2,1}}, + {{0,2},{0,1},{3,0},{1,0}}, {{0,3},{0,1},{2,0},{1,0}}, }; @@ -544,10 +550,10 @@ void CubicIntersection_IntersectionFinder() { const Cubic& cubic1 = newTestSet[0]; const Cubic& cubic2 = newTestSet[1]; - double t1Seed = 0.99; - double t2Seed = 0.99; - double t1Step = 0.01; - double t2Step = 0.01; + double t1Seed = 0.599; + double t2Seed = 0.599; + double t1Step = 0.1; + double t2Step = 0.1; _Point t1[3], t2[3]; bool toggle = true; do { diff --git a/experimental/Intersection/CubicUtilities.cpp b/experimental/Intersection/CubicUtilities.cpp index 1c9b9e486..11d4e6cdb 100644 --- a/experimental/Intersection/CubicUtilities.cpp +++ b/experimental/Intersection/CubicUtilities.cpp @@ -112,6 +112,10 @@ int cubicRootsReal(double A, double B, double C, double D, double s[3]) { char str[1024]; bzero(str, sizeof(str)); sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D); + mathematica_ize(str, sizeof(str)); +#if ONE_OFF_DEBUG + SkDebugf("%s\n", str); +#endif #endif if (approximately_zero(A) && approximately_zero_when_compared_to(A, B) diff --git a/experimental/Intersection/DataTypes.cpp b/experimental/Intersection/DataTypes.cpp index dc4fea1b8..698f49743 100644 --- a/experimental/Intersection/DataTypes.cpp +++ b/experimental/Intersection/DataTypes.cpp @@ -65,3 +65,22 @@ int UlpsDiff(float A, float B) return abs(uA.i - uB.i); } #endif + +#if SK_DEBUG +void mathematica_ize(char* str, size_t bufferLen) { + size_t len = strlen(str); + bool num = false; + for (size_t idx = 0; idx < len; ++idx) { + if (num && str[idx] == 'e') { + if (len + 2 >= bufferLen) { + return; + } + memmove(&str[idx + 2], &str[idx + 1], len - idx); + str[idx] = '*'; + str[idx + 1] = '^'; + ++len; + } + num = str[idx] >= '0' && str[idx] <= '9'; + } +} +#endif diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h index ed3ec218f..ae8d44d6e 100644 --- a/experimental/Intersection/DataTypes.h +++ b/experimental/Intersection/DataTypes.h @@ -120,11 +120,19 @@ inline bool approximately_equal_squared(double x, double y) { } inline bool approximately_greater(double x, double y) { - return approximately_equal(x, y) ? false : x > y; + return x - FLT_EPSILON >= y; +} + +inline bool approximately_greater_or_equal(double x, double y) { + return x + FLT_EPSILON > y; } inline bool approximately_lesser(double x, double y) { - return approximately_equal(x, y) ? false : x < y; + return x + FLT_EPSILON <= y; +} + +inline bool approximately_lesser_or_equal(double x, double y) { + return x - FLT_EPSILON < y; } inline double approximately_pin(double x) { @@ -285,7 +293,10 @@ struct _Point { double lengthSquared() const { return x * x + y * y; } - + + double roughlyEqual(const _Point& a) const { + return roughly_equal(a.y, y) && roughly_equal(a.x, x); + } }; typedef _Point _Line[2]; @@ -361,4 +372,8 @@ struct QuadraticPair { #define sk_double_isnan(a) sk_float_isnan(a) +#if SK_DEBUG +void mathematica_ize(char* str, size_t bufferSize); +#endif + #endif // __DataTypes_h__ diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp index f55bab776..2db5d316f 100644 --- a/experimental/Intersection/Intersection_Tests.cpp +++ b/experimental/Intersection/Intersection_Tests.cpp @@ -15,15 +15,15 @@ void parseSVG(); void Intersection_Tests() { int testsRun = 0; #if 0 +#endif QuadraticIntersection_IntersectionFinder(); QuadraticIntersection_OneOffTest(); CubicIntersection_IntersectionFinder(); CubicIntersection_NewOneOffTest(); -#endif SimplifyNew_Test(); CubicsToQuadratics_OneOffTest(); CubicIntersection_OneOffTest(); - CubicIntersection_OneOffTests(); +// CubicIntersection_OneOffTests(); #if 0 parseSVG(); #endif diff --git a/experimental/Intersection/QuadraticBezierClip.cpp b/experimental/Intersection/QuadraticBezierClip.cpp index 0e948a093..5b15fe51c 100644 --- a/experimental/Intersection/QuadraticBezierClip.cpp +++ b/experimental/Intersection/QuadraticBezierClip.cpp @@ -41,17 +41,17 @@ bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& endLine.quadDistanceY(q2, distance2y); int flags = 0; - if (approximately_lesser(distance2y[0].y, top)) { + if (approximately_lesser_or_equal(distance2y[0].y, top)) { flags |= kFindTopMin; - } else if (approximately_greater(distance2y[0].y, bottom)) { + } else if (approximately_greater_or_equal(distance2y[0].y, bottom)) { flags |= kFindBottomMin; } else { minT = 0; } - if (approximately_lesser(distance2y[2].y, top)) { + if (approximately_lesser_or_equal(distance2y[2].y, top)) { flags |= kFindTopMax; - } else if (approximately_greater(distance2y[2].y, bottom)) { + } else if (approximately_greater_or_equal(distance2y[2].y, bottom)) { flags |= kFindBottomMax; } else { maxT = 1; diff --git a/experimental/Intersection/QuadraticImplicit.cpp b/experimental/Intersection/QuadraticImplicit.cpp index 937bb6ad9..2aed06f8f 100644 --- a/experimental/Intersection/QuadraticImplicit.cpp +++ b/experimental/Intersection/QuadraticImplicit.cpp @@ -25,7 +25,7 @@ */ static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4], - bool oneHint) { + bool oneHint, int firstCubicRoot) { double a, b, c; set_abc(&q2[0].x, a, b, c); double d, e, f; @@ -56,7 +56,7 @@ static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double root if (rootCount >= 0) { return rootCount; } - return quarticRootsReal(t4, t3, t2, t1, t0, roots); + return quarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots); } static int addValidRoots(const double roots[4], const int count, double valid[4]) { @@ -334,6 +334,87 @@ static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersec } #endif +// each time through the loop, this computes values it had from the last loop +// if i == j == 1, the center values are still good +// otherwise, for i != 1 or j != 1, four of the values are still good +// and if i == 1 ^ j == 1, an additional value is good +static bool binarySearch(const Quadratic& quad1, const Quadratic& quad2, double& t1Seed, + double& t2Seed, _Point& pt) { + double tStep = ROUGH_EPSILON; + _Point t1[3], t2[3]; + int calcMask = ~0; + do { + if (calcMask & (1 << 1)) t1[1] = xy_at_t(quad1, t1Seed); + if (calcMask & (1 << 4)) t2[1] = xy_at_t(quad2, t2Seed); + if (t1[1].approximatelyEqual(t2[1])) { + pt = t1[1]; + #if ONE_OFF_DEBUG + SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__, + t1Seed, t2Seed, t1[1].x, t1[1].y, t1[2].x, t1[2].y); + #endif + return true; + } + if (calcMask & (1 << 0)) t1[0] = xy_at_t(quad1, t1Seed - tStep); + if (calcMask & (1 << 2)) t1[2] = xy_at_t(quad1, t1Seed + tStep); + if (calcMask & (1 << 3)) t2[0] = xy_at_t(quad2, t2Seed - tStep); + if (calcMask & (1 << 5)) t2[2] = xy_at_t(quad2, t2Seed + tStep); + double dist[3][3]; + // OPTIMIZE: using calcMask value permits skipping some distance calcuations + // if prior loop's results are moved to correct slot for reuse + dist[1][1] = t1[1].distanceSquared(t2[1]); + int best_i = 1, best_j = 1; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (i == 1 && j == 1) { + continue; + } + dist[i][j] = t1[i].distanceSquared(t2[j]); + if (dist[best_i][best_j] > dist[i][j]) { + best_i = i; + best_j = j; + } + } + } + if (best_i == 1 && best_j == 1) { + tStep /= 2; + if (tStep < FLT_EPSILON_HALF) { + break; + } + calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5); + continue; + } + if (best_i == 0) { + t1Seed -= tStep; + t1[2] = t1[1]; + t1[1] = t1[0]; + calcMask = 1 << 0; + } else if (best_i == 2) { + t1Seed += tStep; + t1[0] = t1[1]; + t1[1] = t1[2]; + calcMask = 1 << 2; + } else { + calcMask = 0; + } + if (best_j == 0) { + t2Seed -= tStep; + t2[2] = t2[1]; + t2[1] = t2[0]; + calcMask |= 1 << 3; + } else if (best_j == 2) { + t2Seed += tStep; + t2[0] = t2[1]; + t2[1] = t2[2]; + calcMask |= 1 << 5; + } + } while (true); +#if ONE_OFF_DEBUG + SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__, + t1Seed, t2Seed, t1[1].x, t1[1].y, t1[2].x, t1[2].y); +#endif + return false; +} + bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { // if the quads share an end point, check to see if they overlap @@ -378,7 +459,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { int index; bool useCubic = q1[0] == q2[0] || q1[0] == q2[2] || q1[2] == q2[0]; double roots1[4]; - int rootCount = findRoots(i2, q1, roots1, useCubic); + int rootCount = findRoots(i2, q1, roots1, useCubic, 0); // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1 double roots1Copy[4]; int r1Count = addValidRoots(roots1, rootCount, roots1Copy); @@ -387,7 +468,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { xy_at_t(q1, roots1Copy[index], pts1[index].x, pts1[index].y); } double roots2[4]; - int rootCount2 = findRoots(i1, q2, roots2, useCubic); + int rootCount2 = findRoots(i1, q2, roots2, useCubic, 0); double roots2Copy[4]; int r2Count = addValidRoots(roots2, rootCount2, roots2Copy); _Point pts2[4]; @@ -398,6 +479,39 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { if (r1Count == 1) { if (pts1[0].approximatelyEqualHalf(pts2[0])) { i.insert(roots1Copy[0], roots2Copy[0], pts1[0]); + } else if (pts1[0].roughlyEqual(pts2[0])) { + // experiment: see if a different cubic solution provides the correct quartic answer + #if 0 + for (int cu1 = 0; cu1 < 3; ++cu1) { + rootCount = findRoots(i2, q1, roots1, useCubic, cu1); + r1Count = addValidRoots(roots1, rootCount, roots1Copy); + if (r1Count == 0) { + continue; + } + for (int cu2 = 0; cu2 < 3; ++cu2) { + if (cu1 == 0 && cu2 == 0) { + continue; + } + rootCount2 = findRoots(i1, q2, roots2, useCubic, cu2); + r2Count = addValidRoots(roots2, rootCount2, roots2Copy); + if (r2Count == 0) { + continue; + } + SkASSERT(r1Count == 1 && r2Count == 1); + SkDebugf("*** [%d,%d] (%1.9g,%1.9g) %s (%1.9g,%1.9g)\n", cu1, cu2, + pts1[0].x, pts1[0].y, pts1[0].approximatelyEqualHalf(pts2[0]) + ? "==" : "!=", pts2[0].x, pts2[0].y); + } + } + #endif + // experiment: try to find intersection by chasing t + rootCount = findRoots(i2, q1, roots1, useCubic, 0); + r1Count = addValidRoots(roots1, rootCount, roots1Copy); + rootCount2 = findRoots(i1, q2, roots2, useCubic, 0); + r2Count = addValidRoots(roots2, rootCount2, roots2Copy); + if (binarySearch(q1, q2, roots1Copy[0], roots2Copy[0], pts1[0])) { + i.insert(roots1Copy[0], roots2Copy[0], pts1[0]); + } } } return i.intersected(); diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp index a9d506034..d2be49274 100644 --- a/experimental/Intersection/QuadraticIntersection_Test.cpp +++ b/experimental/Intersection/QuadraticIntersection_Test.cpp @@ -54,6 +54,15 @@ static void standardTestCases() { } static const Quadratic testSet[] = { +{{2.7279999999999998, 3.024}, {2.5600000000000005, 2.5600000000000005}, {2.1520000000000001, 1.8560000000000001}}, +{{0.66666666666666652, 1.1481481481481481}, {1.3333333333333326, 1.3333333333333335}, {2.6666666666666665, 2.1851851851851851}}, + + {{2.728,3.024}, {2.56,2.56}, {2.152,1.856}}, + {{0.666666667,1.14814815}, {1.33333333,1.33333333}, {2.66666667,2.18518519}}, + + {{0.875,1.5}, {1.03125,1.11022302e-16}, {1,0}}, + {{0.875,0.859375}, {1.6875,0.73046875}, {2.5,0.625}}, + {{1.64451042,0.0942001592}, {1.53635465,0.00152863961}, {1,0}}, {{1.27672209,0.15}, {1.32143477,9.25185854e-17}, {1,0}}, @@ -196,6 +205,7 @@ static void oneOffTest1(size_t outer, size_t inner) { void QuadraticIntersection_OneOffTest() { oneOffTest1(0, 1); + oneOffTest1(2, 3); } static void oneOffTests() { @@ -297,14 +307,14 @@ void QuadraticIntersection_PointFinder() { hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]); } -void QuadraticIntersection_IntersectionFinder() { - const Quadratic& quad1 = testSet[0]; - const Quadratic& quad2 = testSet[1]; +static void intersectionFinder(int test1, int test2) { + const Quadratic& quad1 = testSet[test1]; + const Quadratic& quad2 = testSet[test2]; - double t1Seed = 0.98; - double t2Seed = 0.97; - double t1Step = 0.05; - double t2Step = 0.05; + double t1Seed = 0.989; + double t2Seed = 0.800; + double t1Step = 0.01; + double t2Step = 0.01; _Point t1[3], t2[3]; bool toggle = true; do { @@ -388,3 +398,8 @@ void QuadraticIntersection_IntersectionFinder() { SkDebugf("%s p2=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__, p20.x, p20.y, p2Seed.x, p2Seed.y, p22.x, p22.y); } + +void QuadraticIntersection_IntersectionFinder() { + intersectionFinder(0, 1); + intersectionFinder(2, 3); +} diff --git a/experimental/Intersection/QuarticRoot.cpp b/experimental/Intersection/QuarticRoot.cpp index 50f85b59e..ea786e440 100644 --- a/experimental/Intersection/QuarticRoot.cpp +++ b/experimental/Intersection/QuarticRoot.cpp @@ -40,17 +40,22 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const bzero(str, sizeof(str)); sprintf(str, "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", t4, t3, t2, t1, t0); + mathematica_ize(str, sizeof(str)); +#if ONE_OFF_DEBUG + SkDebugf("%s\n", str); +#endif #endif if (approximately_zero_when_compared_to(t4, t0) // 0 is one root && approximately_zero_when_compared_to(t4, t1) - && approximately_zero_when_compared_to(t4, t2) - && approximately_zero_when_compared_to(t4, t3)) { + && approximately_zero_when_compared_to(t4, t2)) { if (approximately_zero_when_compared_to(t3, t0) && approximately_zero_when_compared_to(t3, t1) && approximately_zero_when_compared_to(t3, t2)) { return quadraticRootsReal(t2, t1, t0, roots); } - return cubicRootsReal(t3, t2, t1, t0, roots); + if (approximately_zero_when_compared_to(t4, t3)) { + return cubicRootsReal(t3, t2, t1, t0, roots); + } } if (approximately_zero_when_compared_to(t0, t1) // 0 is one root && approximately_zero_when_compared_to(t0, t2) @@ -79,8 +84,8 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const return -1; } -int quarticRootsReal(const double A, const double B, const double C, const double D, - const double E, double s[4]) { +int quarticRootsReal(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; @@ -149,7 +154,7 @@ int quarticRootsReal(const double A, const double B, const double C, const doubl double z; num = 0; int num2 = 0; - for (index = 0; index < roots; ++index) { + for (index = firstCubicRoot; index < roots; ++index) { z = cubicRoots[index]; /* ... to build two quadric equations */ u = z * z - r; diff --git a/experimental/Intersection/QuarticRoot.h b/experimental/Intersection/QuarticRoot.h index 3692caa1b..3089d59c8 100644 --- a/experimental/Intersection/QuarticRoot.h +++ b/experimental/Intersection/QuarticRoot.h @@ -1,5 +1,5 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1, const double t0, const bool oneHint, double s[4]); -int quarticRootsReal(const double A, const double B, const double C, const double D, - const double E, double s[4]); +int quarticRootsReal(int firstCubicRoot, const double A, const double B, const double C, + const double D, const double E, double s[4]); diff --git a/experimental/Intersection/QuarticRoot_Test.cpp b/experimental/Intersection/QuarticRoot_Test.cpp index 18a3a8d5a..48431c8f0 100644 --- a/experimental/Intersection/QuarticRoot_Test.cpp +++ b/experimental/Intersection/QuarticRoot_Test.cpp @@ -131,7 +131,7 @@ static void testOneQuartic(size_t aIndex, size_t bIndex, size_t cIndex, size_t d bool oneHint = approximately_zero(A + b + c + d + e); int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots); if (rootCount < 0) { - rootCount = quarticRootsReal(A, b, c, d, e, roots); + rootCount = quarticRootsReal(0, A, b, c, d, e, roots); } const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E); SkASSERT(rootCount == expected); diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp index 2c7103950..70038e640 100644 --- a/experimental/Intersection/Simplify.cpp +++ b/experimental/Intersection/Simplify.cpp @@ -32,7 +32,7 @@ int gDebugMaxWindValue = SK_MaxS32; #define DEBUG_UNUSED 0 // set to expose unused functions -#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging +#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging #if FORCE_RELEASE || defined SK_RELEASE @@ -51,8 +51,8 @@ const bool gRunTestsInOneThread = false; #define DEBUG_MARK_DONE 0 #define DEBUG_PATH_CONSTRUCTION 0 #define DEBUG_SHOW_WINDING 0 -#define DEBUG_SORT 0 -#define DEBUG_SWAP_TOP 1 +#define DEBUG_SORT 1 +#define DEBUG_SWAP_TOP 0 #define DEBUG_UNSORTABLE 0 #define DEBUG_WIND_BUMP 0 #define DEBUG_WINDING 0 @@ -88,13 +88,16 @@ const bool gRunTestsInOneThread = true; DEBUG_PATH_CONSTRUCTION) #if DEBUG_DUMP -static const char* kShapeOpStr[] = {"diff", "sect", "union", "xor"}; static const char* kLVerbStr[] = {"", "line", "quad", "cubic"}; // static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"}; static int gContourID; static int gSegmentID; #endif +#if DEBUG_ACTIVE_OP +static const char* kShapeOpStr[] = {"diff", "sect", "union", "xor"}; +#endif + #ifndef DEBUG_TEST #define DEBUG_TEST 0 #endif @@ -1041,7 +1044,7 @@ static bool useInnerWinding(int outerWinding, int innerWinding) { int absIn = abs(innerWinding); bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn; if (outerWinding * innerWinding < 0) { -#if DEBUG_WINDING +#if 0 && DEBUG_WINDING SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__, outerWinding, innerWinding, result ? "true" : "false"); #endif @@ -2822,14 +2825,24 @@ public: endIndex = angle->start(); } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone); if (leftSegment->verb() >= SkPath::kQuad_Verb) { + bool bumpsUp = leftSegment->bumpsUp(tIndex, endIndex); + SkPoint xyE = leftSegment->xyAtT(endIndex); + SkPoint xyS = leftSegment->xyAtT(tIndex); SkPoint dxyE = leftSegment->dxdy(endIndex); SkPoint dxyS = leftSegment->dxdy(tIndex); double cross = dxyE.cross(dxyS); + bool bumpCheck = bumpsUp && xyE.fY < xyS.fY; #if DEBUG_SWAP_TOP - SkDebugf("%s dxyE=(%1.9g,%1.9g) dxyS=(%1.9g,%1.9g) cross=%1.9g\n", __FUNCTION__, - dxyE.fX, dxyE.fY, dxyS.fX, dxyS.fY, cross); + SkDebugf("%s xyE=(%1.9g,%1.9g) xyS=(%1.9g,%1.9g)\n", __FUNCTION__, + xyE.fX, xyE.fY, xyS.fX, xyS.fY); + SkDebugf("%s dxyE=(%1.9g,%1.9g) dxyS=(%1.9g,%1.9g) cross=%1.9g bump=%s\n", __FUNCTION__, + dxyE.fX, dxyE.fY, dxyS.fX, dxyS.fY, cross, bumpCheck ? "true" : "false"); #endif - if (cross >= 1) { + if ((cross > 0) ^ bumpCheck) { + leftSegment->bumpsUp(tIndex, endIndex); + SkDebugf("%s cross bump disagree\n", __FUNCTION__); + } + if (bumpCheck) { #if DEBUG_SWAP_TOP SkDebugf("%s swap\n", __FUNCTION__); #endif @@ -3298,6 +3311,27 @@ the same winding is shared by both. return &span; } + bool bumpsUp(int tStart, int tEnd) const { + SkPoint edge[4]; + (*SegmentSubDivide[fVerb])(fPts, fTs[tStart].fT, fTs[tEnd].fT, edge); + switch (fVerb) { + case SkPath::kLine_Verb: + SkASSERT(0); // shouldn't call in for lines + return true; + case SkPath::kQuad_Verb: + return approximately_greater(edge[0].fY, edge[1].fY) + && approximately_lesser(edge[1].fY, edge[2].fY); + case SkPath::kCubic_Verb: + return (approximately_greater(edge[0].fY, edge[1].fY) + && approximately_lesser(edge[1].fY, edge[3].fY)) + || (approximately_greater(edge[0].fY, edge[2].fY) + && approximately_lesser(edge[2].fY, edge[3].fY)); + default: + SkASSERT(0); + return false; + } + } + Span* verifyOneWinding(const char* funName, int tIndex) { Span& span = fTs[tIndex]; if (span.fDone) { @@ -3685,7 +3719,8 @@ the same winding is shared by both. int lesser = SkMin32(index, endIndex); int oppWinding = oppSum(lesser); int oppSpanWinding = oppSign(index, endIndex); - if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) { + if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding) + && oppWinding != SK_MaxS32) { oppWinding -= oppSpanWinding; } return oppWinding; @@ -3707,7 +3742,7 @@ the same winding is shared by both. int lesser = SkMin32(index, endIndex); int winding = windSum(lesser); int spanWinding = spanSign(index, endIndex); - if (winding && useInnerWinding(winding - spanWinding, winding)) { + if (winding && useInnerWinding(winding - spanWinding, winding) && winding != SK_MaxS32) { winding -= spanWinding; } return winding; @@ -4130,6 +4165,8 @@ the same winding is shared by both. start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end, segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue); + start here; + // create an inline to replace this conditional if (mSpan.fWindSum == SK_MinS32) { SkDebugf("?"); } else { diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp index 78b815ac8..39cf5a8da 100644 --- a/experimental/Intersection/SimplifyNew_Test.cpp +++ b/experimental/Intersection/SimplifyNew_Test.cpp @@ -3845,12 +3845,68 @@ static void cubicOp20d() { testShapeOp(path, pathB, kDifference_Op); } -static void (*firstTest)() = cubicOp20d; +static void cubicOp21d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(0,1, 2,1, 6,5); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(5,6, 1,0, 1,0); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void cubicOp22d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(2,3, 3,0, 2,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,3); + pathB.cubicTo(1,2, 1,0, 3,2); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void cubicOp23d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 4,0, 2,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,4); + pathB.cubicTo(1,2, 1,0, 2,1); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void cubicOp24d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 2,0, 3,2); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,2); + pathB.cubicTo(2,3, 1,0, 2,1); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void (*firstTest)() = 0; static struct { void (*fun)(); const char* str; } tests[] = { + TEST(cubicOp24d), + TEST(cubicOp23d), + TEST(cubicOp22d), + TEST(cubicOp21d), TEST(cubicOp20d), TEST(cubicOp19i), TEST(cubicOp18d), diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index 9c2ce8e47..21c315de6 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -3600,11 +3600,59 @@ path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction); pathB.close(); +