diff --git a/experimental/Intersection/CubicIntersection.cpp b/experimental/Intersection/CubicIntersection.cpp index 8de6398622..9316f342a4 100644 --- a/experimental/Intersection/CubicIntersection.cpp +++ b/experimental/Intersection/CubicIntersection.cpp @@ -206,7 +206,10 @@ void computeDelta(const Cubic& c1, double t1, double scale1, const Cubic& c2, do delta2 = cubicDelta(dxy2, tangent2, scale2 / precisionUnit); } +#if SK_DEBUG int debugDepth; +#endif + // this flavor approximates the cubics with quads to find the intersecting ts // OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used // to create the approximations, could be stored in the cubic segment @@ -217,14 +220,14 @@ int debugDepth; // t range ala is linear inner. The range can be figured by taking the dx/dy and determining // the fraction that matches the precision. That fraction is the change in t for the smaller cubic. static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2, - double t2s, double t2e, Intersections& i) { + double t2s, double t2e, double precisionScale, Intersections& i) { Cubic c1, c2; sub_divide(cubic1, t1s, t1e, c1); sub_divide(cubic2, t2s, t2e, c2); SkTDArray ts1; - cubic_to_quadratics(c1, calcPrecision(c1), ts1); + cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1); SkTDArray ts2; - cubic_to_quadratics(c2, calcPrecision(c2), ts2); + cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2); double t1Start = t1s; int ts1Count = ts1.count(); for (int i1 = 0; i1 <= ts1Count; ++i1) { @@ -277,14 +280,22 @@ static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& } else { double dt1, dt2; computeDelta(cubic1, to1, (t1e - t1s), cubic2, to2, (t2e - t2s), dt1, dt2); + double scale = precisionScale; + if (dt1 > 0.125 || dt2 > 0.125) { + scale /= 2; + SkDebugf("%s scale=%1.9g\n", __FUNCTION__, scale); + } +#if SK_DEBUG ++debugDepth; assert(debugDepth < 10); +#endif i.swap(); intersect2(cubic2, SkTMax(to2 - dt2, 0.), SkTMin(to2 + dt2, 1.), - cubic1, SkTMax(to1 - dt1, 0.), SkTMin(to1 + dt1, 1.), i); + cubic1, SkTMax(to1 - dt1, 0.), SkTMin(to1 + dt1, 1.), scale, i); i.swap(); +#if SK_DEBUG --debugDepth; - +#endif } } t2Start = t2; @@ -336,9 +347,11 @@ static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, c tMin = std::min(tMin, local2.fT[0][index]); tMax = std::max(tMax, local2.fT[0][index]); } +#if SK_DEBUG debugDepth = 0; +#endif return intersect2(cubic1, start ? 0 : 1, start ? 1.0 / precisionUnit : 1 - 1.0 / precisionUnit, - cubic2, tMin, tMax, i); + cubic2, tMin, tMax, 1, i); } // FIXME: add intersection of convex null on cubics' ends with the opposite cubic. The hull line @@ -346,16 +359,20 @@ static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, c // line segments intersect the cubic, then use the intersections to construct a subdivision for // quadratic curve fitting. bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) { +#if SK_DEBUG debugDepth = 0; - bool result = intersect2(c1, 0, 1, c2, 0, 1, i); +#endif + bool result = intersect2(c1, 0, 1, c2, 0, 1, 1, i); // FIXME: pass in cached bounds from caller _Rect c1Bounds, c2Bounds; c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? c2Bounds.setBounds(c2); result |= intersectEnd(c1, false, c2, c2Bounds, i); result |= intersectEnd(c1, true, c2, c2Bounds, i); + i.swap(); result |= intersectEnd(c2, false, c1, c1Bounds, i); result |= intersectEnd(c2, true, c1, c1Bounds, i); + i.swap(); return result; } diff --git a/experimental/Intersection/CubicIntersection_Test.cpp b/experimental/Intersection/CubicIntersection_Test.cpp index 2e738fc0e4..94d86ea760 100644 --- a/experimental/Intersection/CubicIntersection_Test.cpp +++ b/experimental/Intersection/CubicIntersection_Test.cpp @@ -98,6 +98,15 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) { } static const Cubic testSet[] = { +{{0, 0}, {0, 1}, {1, 1}, {1, 0}}, +{{1, 0}, {0, 0}, {0, 1}, {1, 1}}, + +{{95.837747722788592, 45.025976907939643}, {16.564570095652982, 0.72959763963222402}, {63.209855865319199, 68.047528419665767}, {57.640240647662544, 59.524565264361243}}, +{{51.593891741518817, 38.53849970667553}, {62.34752929878772, 74.924924725166022}, {74.810149322641152, 34.17966562983564}, {29.368398119401373, 94.66719277886078}}, + +{{39.765160968417838, 33.060396198677083}, {5.1922921581157908, 66.854301452103215}, {31.619281802149157, 25.269248720849514}, {81.541621071073038, 70.025341524754353}}, +{{46.078911165743556, 48.259962651999651}, {20.24450549867214, 49.403916182650214}, {0.26325131778756683, 24.46489805563581}, {15.915006546264051, 83.515023059917155}}, + {{65.454505973241524, 93.881892270353575}, {45.867360264932437, 92.723972719499827}, {2.1464054482739447, 74.636369140183717}, {33.774068594804994, 40.770872887582925}}, {{72.963387832494163, 95.659300729473728}, {11.809496633619768, 82.209921247423594}, {13.456139067865974, 57.329313623406605}, {36.060621606214262, 70.867335643091849}}, @@ -295,7 +304,7 @@ void CubicIntersection_RandTestOld() { void CubicIntersection_RandTest() { srand(0); - const int tests = 1000000; // 10000000; + const int tests = 10000000; for (int test = 0; test < tests; ++test) { Cubic cubic1, cubic2; for (int i = 0; i < 4; ++i) { diff --git a/experimental/Intersection/CubicReduceOrder.cpp b/experimental/Intersection/CubicReduceOrder.cpp index fdc7265923..87d9fd876a 100644 --- a/experimental/Intersection/CubicReduceOrder.cpp +++ b/experimental/Intersection/CubicReduceOrder.cpp @@ -151,14 +151,11 @@ bool isLinear(const Cubic& cubic, int startIndex, int endIndex) { lineParameters.cubicEndPoints(cubic, startIndex, endIndex); // FIXME: maybe it's possible to avoid this and compare non-normalized lineParameters.normalize(); - int mask = other_two(startIndex, endIndex); - int inner1 = startIndex ^ mask; - int inner2 = endIndex ^ mask; - double distance = lineParameters.controlPtDistance(cubic, inner1); + double distance = lineParameters.controlPtDistance(cubic, 1); if (!approximately_zero(distance)) { return false; } - distance = lineParameters.controlPtDistance(cubic, inner2); + distance = lineParameters.controlPtDistance(cubic, 2); return approximately_zero(distance); } diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h index a6516fa0fc..ef9bcef65e 100644 --- a/experimental/Intersection/DataTypes.h +++ b/experimental/Intersection/DataTypes.h @@ -28,6 +28,7 @@ int UlpsDiff(float A, float B); const double FLT_EPSILON_CUBED = FLT_EPSILON * FLT_EPSILON * FLT_EPSILON; const double FLT_EPSILON_SQUARED = FLT_EPSILON * FLT_EPSILON; const double FLT_EPSILON_SQRT = sqrt(FLT_EPSILON); +const double FLT_EPSILON_INVERSE = 1 / FLT_EPSILON; inline bool approximately_zero(double x) { @@ -56,6 +57,10 @@ inline bool approximately_zero_sqrt(double x) { return fabs(x) < FLT_EPSILON_SQRT; } +inline bool approximately_zero_inverse(double x) { + return fabs(x) > FLT_EPSILON_INVERSE; +} + // Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use // AlmostEqualUlps instead. inline bool approximately_equal(double x, double y) { @@ -247,11 +252,11 @@ struct _Rect { } bool intersects(_Rect& r) const { - assert(left < right); - assert(top < bottom); - assert(r.left < r.right); - assert(r.top < r.bottom); - return r.left < right && left < r.right && r.top < bottom && top < r.bottom; + assert(left <= right); + assert(top <= bottom); + assert(r.left <= r.right); + assert(r.top <= r.bottom); + return r.left <= right && left <= r.right && r.top <= bottom && top <= r.bottom; } void set(const _Point& pt) { diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp index bf86c07c79..8b4052f91b 100644 --- a/experimental/Intersection/Intersection_Tests.cpp +++ b/experimental/Intersection/Intersection_Tests.cpp @@ -15,13 +15,13 @@ void cubecode_test(int test); void Intersection_Tests() { int testsRun = 0; - QuadraticIntersection_Test(); + SimplifyNew_Test(); + CubicToQuadratics_Test(); CubicIntersection_OneOffTest(); + QuadraticIntersection_Test(); QuarticRoot_Test(); CubicIntersection_RandTest(); - SimplifyNew_Test(); CubicsToQuadratics_RandTest(); - CubicToQuadratics_Test(); Simplify4x4RectsThreaded_Test(testsRun); Simplify4x4QuadraticsThreaded_Test(testsRun); QuadLineIntersectThreaded_Test(testsRun); diff --git a/experimental/Intersection/QuadraticImplicit.cpp b/experimental/Intersection/QuadraticImplicit.cpp index 9cd24e9f53..054be64e2c 100644 --- a/experimental/Intersection/QuadraticImplicit.cpp +++ b/experimental/Intersection/QuadraticImplicit.cpp @@ -13,6 +13,10 @@ #include "QuadraticUtilities.h" #include "TSearch.h" +#if SK_DEBUG +#include "LineUtilities.h" +#endif + /* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F * and given x = at^2 + bt + c (the parameterized form) * y = dt^2 + et + f @@ -141,8 +145,11 @@ static bool pointInTriangle(const _Point& pt, const _Line* testLines[]) { return (u >= 0) && (v >= 0) && (u + v < 1); } +// returns false if there's more than one intercept or the intercept doesn't match the point +// returns true if the intercept was successfully added or if the +// original quads need to be subdivided static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin, double tMax, - Intersections& i) { + Intersections& i, bool* subDivide) { double tMid = (tMin + tMax) / 2; _Point mid; xy_at_t(q2, tMid, mid.x, mid.y); @@ -156,10 +163,15 @@ static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin, line[1].y += dxdy.y; Intersections rootTs; int roots = intersect(q1, line, rootTs); + if (roots == 0) { + if (subDivide) { + *subDivide = true; + } + return true; + } if (roots == 2) { return false; } - SkASSERT(roots == 1); _Point pt2; xy_at_t(q1, rootTs.fT[0][0], pt2.x, pt2.y); if (!pt2.approximatelyEqual(mid)) { @@ -170,7 +182,7 @@ static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin, } static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Quadratic& q2, - double t2s, double t2e, Intersections& i) { + double t2s, double t2e, Intersections& i, bool* subDivide) { Quadratic hull; sub_divide(q1, t1s, t1e, hull); _Line line = {hull[2], hull[0]}; @@ -182,6 +194,12 @@ static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Qua int roots = intersect(q2, *testLines[index], rootTs); for (int idx2 = 0; idx2 < roots; ++idx2) { double t = rootTs.fT[0][idx2]; +#if SK_DEBUG + _Point qPt, lPt; + xy_at_t(q2, t, qPt.x, qPt.y); + xy_at_t(*testLines[index], rootTs.fT[1][idx2], lPt.x, lPt.y); + SkASSERT(qPt.approximatelyEqual(lPt)); +#endif if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) { continue; } @@ -227,25 +245,25 @@ static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Qua } if (split == 0) { // there's one point - if (addIntercept(q1, q2, tMin, tMax, i)) { + if (addIntercept(q1, q2, tMin, tMax, i, subDivide)) { return true; } i.swap(); - return isLinearInner(q2, tMin, tMax, q1, t1s, t1e, i); + return isLinearInner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide); } // At this point, we have two ranges of t values -- treat each separately at the split bool result; - if (addIntercept(q1, q2, tMin, tsFound[split - 1], i)) { + if (addIntercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) { result = true; } else { i.swap(); - result = isLinearInner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i); + result = isLinearInner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide); } - if (addIntercept(q1, q2, tsFound[split], tMax, i)) { + if (addIntercept(q1, q2, tsFound[split], tMax, i, subDivide)) { result = true; } else { i.swap(); - result |= isLinearInner(q2, tsFound[split], tMax, q1, t1s, t1e, i); + result |= isLinearInner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide); } return result; } @@ -266,11 +284,11 @@ static bool isLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) if (!approximately_zero_sqrt(measure)) { return false; } - return isLinearInner(q1, 0, 1, q2, 0, 1, i); + return isLinearInner(q1, 0, 1, q2, 0, 1, i, NULL); } // FIXME: if flat measure is sufficiently large, then probably the quartic solution failed -static bool relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) { +static void relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) { double m1 = flatMeasure(q1); double m2 = flatMeasure(q2); #if SK_DEBUG @@ -280,12 +298,25 @@ static bool relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersecti } #endif i.reset(); - if (m1 < m2) { - isLinearInner(q1, 0, 1, q2, 0, 1, i); - return false; - } else { - isLinearInner(q2, 0, 1, q1, 0, 1, i); - return true; + const Quadratic& rounder = m2 < m1 ? q1 : q2; + const Quadratic& flatter = m2 < m1 ? q2 : q1; + bool subDivide = false; + isLinearInner(flatter, 0, 1, rounder, 0, 1, i, &subDivide); + if (subDivide) { + QuadraticPair pair; + chop_at(flatter, pair, 0.5); + Intersections firstI, secondI; + relaxedIsLinear(pair.first(), rounder, firstI); + for (int index = 0; index < firstI.used(); ++index) { + i.insert(firstI.fT[0][index] * 0.5, firstI.fT[1][index]); + } + relaxedIsLinear(pair.second(), rounder, secondI); + for (int index = 0; index < secondI.used(); ++index) { + i.insert(0.5 + secondI.fT[0][index] * 0.5, secondI.fT[1][index]); + } + } + if (m2 < m1) { + i.swapPts(); } } @@ -428,9 +459,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) { } } if (i.fUsed && i.fUsed2 && !foundSomething) { - if (relaxedIsLinear(q1, q2, i)) { - i.swapPts(); - } + relaxedIsLinear(q1, q2, i); return i.intersected(); } double roots1Copy[4], roots2Copy[4]; diff --git a/experimental/Intersection/QuadraticUtilities.cpp b/experimental/Intersection/QuadraticUtilities.cpp index c1fab05822..d8755a3bb4 100644 --- a/experimental/Intersection/QuadraticUtilities.cpp +++ b/experimental/Intersection/QuadraticUtilities.cpp @@ -97,7 +97,9 @@ int quadraticRootsValidT(double A, double B, double C, double t[2]) { // unlike quadratic roots, this does not discard real roots <= 0 or >= 1 int quadraticRootsReal(const double A, const double B, const double C, double s[2]) { - if (approximately_zero(A)) { + 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; @@ -106,8 +108,6 @@ int quadraticRootsReal(const double A, const double B, const double C, double s[ return 1; } /* normal form: x^2 + px + q = 0 */ - const double p = B / (2 * A); - const double q = C / A; const double p2 = p * p; #if 0 double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q; diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp index 618dc30a26..1094496742 100644 --- a/experimental/Intersection/Simplify.cpp +++ b/experimental/Intersection/Simplify.cpp @@ -1017,12 +1017,14 @@ public: void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) { lineTo(); moveTo(); + fDefer[1] = pt3; + nudge(); + fDefer[0] = fDefer[1]; #if DEBUG_PATH_CONSTRUCTION SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n", - pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY); + pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY); #endif - fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY); - fDefer[0] = fDefer[1] = pt3; + fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY); fEmpty = false; } @@ -1070,6 +1072,7 @@ public: return; } moveTo(); + nudge(); fEmpty = false; #if DEBUG_PATH_CONSTRUCTION SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY); @@ -1081,16 +1084,26 @@ public: const SkPath* nativePath() const { return fPathPtr; } + + void nudge() { + if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX) + || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) { + return; + } + fDefer[1] = fFirstPt; + } void quadTo(const SkPoint& pt1, const SkPoint& pt2) { lineTo(); moveTo(); + fDefer[1] = pt2; + nudge(); + fDefer[0] = fDefer[1]; #if DEBUG_PATH_CONSTRUCTION SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n", - pt1.fX, pt1.fY, pt2.fX, pt2.fY); + pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY); #endif - fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY); - fDefer[0] = fDefer[1] = pt2; + fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY); fEmpty = false; } @@ -5097,7 +5110,6 @@ static void debugShowCubicLineIntersection(int pts, const Work& wt, SkDebugf("\n"); } -#if APPROXIMATE_CUBICS // FIXME: show more than two intersection points static void debugShowCubicQuadIntersection(int pts, const Work& wt, const Work& wn, const double wtTs[2], const double wnTs[2]) { @@ -5112,7 +5124,7 @@ static void debugShowCubicQuadIntersection(int pts, const Work& wt, } SkPoint wtOutPt, wnOutPt; CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt); - CubicXYAtT(wn.pts(), wnTs[0], &wnOutPt); + QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt); SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)", __FUNCTION__, wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY, @@ -5163,7 +5175,7 @@ static void debugShowCubicIntersection(int pts, const Work& wt, } SkDebugf("\n"); } -#endif + #else static void debugShowLineIntersection(int , const Work& , const Work& , const double [2], const double [2]) { @@ -5181,7 +5193,6 @@ static void debugShowCubicLineIntersection(int , const Work& , const Work& , const double [2], const double [2]) { } -#if APPROXIMATE_CUBICS static void debugShowCubicQuadIntersection(int , const Work& , const Work& , const double [2], const double [2]) { } @@ -5190,7 +5201,6 @@ static void debugShowCubicIntersection(int , const Work& , const Work& , const double [2], const double [2]) { } #endif -#endif static bool addIntersectTs(Contour* test, Contour* next) { @@ -5340,6 +5350,7 @@ static bool addIntersectTs(Contour* test, Contour* next) { #else wt.promoteToCubic(); pts = CubicIntersect(wt.cubic(), wn.pts(), ts); + debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]); #endif break; } @@ -5370,16 +5381,13 @@ static bool addIntersectTs(Contour* test, Contour* next) { #else wn.promoteToCubic(); pts = CubicIntersect(wt.pts(), wn.cubic(), ts); + debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]); #endif break; } case Work::kCubic_Segment: { - #if APPROXIMATE_CUBICS pts = CubicIntersect(wt.pts(), wn.pts(), ts); debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]); - #else - pts = CubicIntersect(wt.pts(), wn.pts(), ts); - #endif break; } default: diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp index 823693c7e3..4cad7e106c 100644 --- a/experimental/Intersection/SimplifyNew_Test.cpp +++ b/experimental/Intersection/SimplifyNew_Test.cpp @@ -3536,12 +3536,26 @@ static void testCubic1() { testSimplifyx(path); } +static void testQuadratic93() { + SkPath path; + path.moveTo(3, 0); + path.quadTo(0, 1, 3, 2); + path.lineTo(0, 3); + path.close(); + path.moveTo(1, 0); + path.lineTo(2, 0); + path.quadTo(1, 1, 2, 2); + path.close(); + testSimplifyx(path); +} + static void (*firstTest)() = 0; static struct { void (*fun)(); const char* str; } tests[] = { + TEST(testQuadratic93), TEST(testCubic1), TEST(testQuadralateral1), TEST(testLine85), @@ -3870,6 +3884,8 @@ static struct { TEST(testLine1), }; +static const size_t testCount = sizeof(tests) / sizeof(tests[0]); + static void testIntersect1() { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); @@ -4034,8 +4050,6 @@ static void testOp8d() { testShapeOp(path, pathB, kDifference_Op); } -static const size_t testCount = sizeof(tests) / sizeof(tests[0]); - static struct { void (*fun)(); const char* str; @@ -4065,7 +4079,7 @@ static const size_t subTestCount = sizeof(subTests) / sizeof(subTests[0]); static void (*firstBinaryTest)() = testOp8d; static bool skipAll = false; -static bool runBinaryTestsFirst = true; +static bool runBinaryTestsFirst = false; static bool runReverse = false; static void (*stopTest)() = 0; diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index 8c8a9919ea..e2db44bdcb 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -3317,11 +3317,23 @@ path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction); path.close(); +
+ path.moveTo(3, 0); + path.quadTo(0, 1, 3, 2); + path.lineTo(0, 3); + path.close(); + path.moveTo(1, 0); + path.lineTo(2, 0); + path.quadTo(1, 1, 2, 2); + path.close(); +
+