diff options
author | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-02-28 16:12:39 +0000 |
---|---|---|
committer | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-02-28 16:12:39 +0000 |
commit | 4aaaaeace7e617ddc473645756fb7c20790bc270 (patch) | |
tree | a8bb7e7d654d04fbc6a1dfba273cbf37ee5383f2 /experimental | |
parent | 826b41525b5d4e67c7777b41da1435f921a47cde (diff) |
shape ops work in progress
git-svn-id: http://skia.googlecode.com/svn/trunk@7898 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'experimental')
-rw-r--r-- | experimental/Intersection/CubicIntersection.cpp | 292 | ||||
-rw-r--r-- | experimental/Intersection/CubicIntersection_Test.cpp | 34 | ||||
-rw-r--r-- | experimental/Intersection/CubicUtilities.cpp | 2 | ||||
-rw-r--r-- | experimental/Intersection/CurveIntersection.h | 2 | ||||
-rw-r--r-- | experimental/Intersection/DataTypes.h | 1 | ||||
-rw-r--r-- | experimental/Intersection/EdgeWalker_TestUtility.cpp | 3 | ||||
-rw-r--r-- | experimental/Intersection/Intersection_Tests.cpp | 2 | ||||
-rw-r--r-- | experimental/Intersection/QuadraticIntersection_Test.cpp | 3 | ||||
-rw-r--r-- | experimental/Intersection/QuarticRoot.cpp | 2 | ||||
-rw-r--r-- | experimental/Intersection/ShapeOps.cpp | 23 | ||||
-rw-r--r-- | experimental/Intersection/Simplify.cpp | 52 | ||||
-rw-r--r-- | experimental/Intersection/SimplifyAngle_Test.cpp | 52 | ||||
-rw-r--r-- | experimental/Intersection/SimplifyNew_Test.cpp | 76 | ||||
-rw-r--r-- | experimental/Intersection/as.htm | 363 | ||||
-rw-r--r-- | experimental/Intersection/op.htm | 52 | ||||
-rw-r--r-- | experimental/Intersection/qc.htm | 37 |
16 files changed, 677 insertions, 319 deletions
diff --git a/experimental/Intersection/CubicIntersection.cpp b/experimental/Intersection/CubicIntersection.cpp index 0a82866d45..f7b66d540e 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.772784538, 0.77278492}, {0.999111748, 0.999112129}}; +static const double tLimits[2][2] = {{0.134, 0.145}, {0.134, 0.136}}; #endif #define DEBUG_QUAD_PART 0 @@ -66,217 +66,6 @@ static void intersectWithOrder(const Quadratic& simple1, int order1, const Quadr } } -static double distanceFromEnd(double t) { - return t > 0.5 ? 1 - t : t; -} - -// OPTIMIZATION: this used to try to guess the value for delta, and that may still be worthwhile -static void bumpForRetry(double t1, double t2, double& s1, double& e1, double& s2, double& e2) { - double dt1 = distanceFromEnd(t1); - double dt2 = distanceFromEnd(t2); - double delta = 1.0 / gPrecisionUnit; - if (dt1 < dt2) { - if (t1 == dt1) { - s1 = SkTMax(s1 - delta, 0.); - } else { - e1 = SkTMin(e1 + delta, 1.); - } - } else { - if (t2 == dt2) { - s2 = SkTMax(s2 - delta, 0.); - } else { - e2 = SkTMin(e2 + delta, 1.); - } - } -} - -static bool doIntersect(const Cubic& cubic1, double t1s, double t1m, double t1e, - const Cubic& cubic2, double t2s, double t2m, double t2e, Intersections& i) { - bool result = false; - i.upDepth(); - // divide the quadratics at the new t value and try again - double p1s = t1s; - double p1e = t1m; - for (int p1 = 0; p1 < 2; ++p1) { - Quadratic s1a; - int o1a = quadPart(cubic1, p1s, p1e, s1a); - double p2s = t2s; - double p2e = t2m; - for (int p2 = 0; p2 < 2; ++p2) { - Quadratic s2a; - int o2a = quadPart(cubic2, p2s, p2e, s2a); - Intersections locals; - #if ONE_OFF_DEBUG - if (tLimits[0][0] >= p1s && tLimits[0][1] <= p1e - && tLimits[1][0] >= p2s && tLimits[1][1] <= p2e) { - SkDebugf("t1=(%1.9g,%1.9g) o1=%d t2=(%1.9g,%1.9g) o2=%d\n", - p1s, p1e, o1a, p2s, p2e, o2a); - if (o1a == 2) { - SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", - s1a[0].x, s1a[0].y, s1a[1].x, s1a[1].y); - } else { - SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", - s1a[0].x, s1a[0].y, s1a[1].x, s1a[1].y, s1a[2].x, s1a[2].y); - } - if (o2a == 2) { - SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", - s2a[0].x, s2a[0].y, s2a[1].x, s2a[1].y); - } else { - SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", - s2a[0].x, s2a[0].y, s2a[1].x, s2a[1].y, s2a[2].x, s2a[2].y); - } - Intersections xlocals; - intersectWithOrder(s1a, o1a, s2a, o2a, xlocals); - SkDebugf("xlocals.fUsed=%d depth=%d\n", xlocals.used(), i.depth()); - } - #endif - intersectWithOrder(s1a, o1a, s2a, o2a, locals); - for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { - double to1 = p1s + (p1e - p1s) * locals.fT[0][tIdx]; - double to2 = p2s + (p2e - p2s) * locals.fT[1][tIdx]; - // if the computed t is not sufficiently precise, iterate - _Point p1, p2; - xy_at_t(cubic1, to1, p1.x, p1.y); - xy_at_t(cubic2, to2, p2.x, p2.y); - #if ONE_OFF_DEBUG - SkDebugf("to1=%1.9g p1=(%1.9g,%1.9g) to2=%1.9g p2=(%1.9g,%1.9g) d=%1.9g\n", - to1, p1.x, p1.y, to2, p2.x, p2.y, p1.distance(p2)); - - #endif - if (p1.approximatelyEqualHalf(p2)) { - i.insertSwap(to1, to2, p1); - result = true; - } else { - result = doIntersect(cubic1, p1s, to1, p1e, cubic2, p2s, to2, p2e, i); - if (!result && p1.approximatelyEqual(p2)) { - i.insertSwap(to1, to2, p1); - #if SWAP_TOP_DEBUG - SkDebugf("!!!\n"); - #endif - result = true; - } else - // if both cubics curve in the same direction, the quadratic intersection - // may mark a range that does not contain the cubic intersection. If no - // intersection is found, look again including the t distance of the - // of the quadratic intersection nearest a quadratic end (which in turn is - // nearest the actual cubic) - if (!result) { - double b1s = p1s; - double b1e = p1e; - double b2s = p2s; - double b2e = p2e; - bumpForRetry(locals.fT[0][tIdx], locals.fT[1][tIdx], b1s, b1e, b2s, b2e); - result = doIntersect(cubic1, b1s, to1, b1e, cubic2, b2s, to2, b2e, i); - } - } - } - p2s = p2e; - p2e = t2e; - } - p1s = p1e; - p1e = t1e; - } - i.downDepth(); - return result; -} - -// 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 -// FIXME: this strategy needs to intersect the convex hull on either end with the opposite to -// account for inset quadratics that cause the endpoint intersection to avoid detection -// the segments can be very short -- the length of the maximum quadratic error (precision) -static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2, - double t2s, double t2e, double precisionScale, Intersections& i) { - Cubic c1, c2; - sub_divide(cubic1, t1s, t1e, c1); - sub_divide(cubic2, t2s, t2e, c2); - SkTDArray<double> ts1; - cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1); - SkTDArray<double> ts2; - cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2); - double t1Start = t1s; - int ts1Count = ts1.count(); - for (int i1 = 0; i1 <= ts1Count; ++i1) { - const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1; - const double t1 = t1s + (t1e - t1s) * tEnd1; - Quadratic s1; - int o1 = quadPart(cubic1, t1Start, t1, s1); - double t2Start = t2s; - int ts2Count = ts2.count(); - for (int i2 = 0; i2 <= ts2Count; ++i2) { - const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1; - 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); - - for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { - double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx]; - double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx]; - // if the computed t is not sufficiently precise, iterate - _Point p1, p2; - xy_at_t(cubic1, to1, p1.x, p1.y); - xy_at_t(cubic2, to2, p2.x, p2.y); - if (p1.approximatelyEqual(p2)) { - i.insert(to1, to2, p1); - } else { - #if ONE_OFF_DEBUG - if (tLimits[0][0] >= t1Start && tLimits[0][1] <= t1 - && tLimits[1][0] >= t2Start && tLimits[1][1] <= t2) { - SkDebugf("t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)\n", - t1Start, t1, t2Start, t2); - } - #endif - bool found = doIntersect(cubic1, t1Start, to1, t1, cubic2, t2Start, to2, t2, i); - if (!found) { - double b1s = t1Start; - double b1e = t1; - double b2s = t2Start; - double b2e = t2; - bumpForRetry(locals.fT[0][tIdx], locals.fT[1][tIdx], b1s, b1e, b2s, b2e); - doIntersect(cubic1, b1s, to1, b1e, cubic2, b2s, to2, b2e, i); - } - } - } - int coincidentCount = locals.coincidentUsed(); - if (coincidentCount) { - // FIXME: one day, we'll probably need to allow coincident + non-coincident pts - SkASSERT(coincidentCount == locals.used()); - SkASSERT(coincidentCount == 2); - double coTs[2][2]; - for (int tIdx = 0; tIdx < coincidentCount; ++tIdx) { - if (locals.fIsCoincident[0] & (1 << tIdx)) { - coTs[0][tIdx] = t1Start + (t1 - t1Start) * locals.fT[0][tIdx]; - } - if (locals.fIsCoincident[1] & (1 << tIdx)) { - coTs[1][tIdx] = t2Start + (t2 - t2Start) * locals.fT[1][tIdx]; - } - } - i.insertCoincidentPair(coTs[0][0], coTs[0][1], coTs[1][0], coTs[1][1], - locals.fPt[0], locals.fPt[1]); - } - t2Start = t2; - } - t1Start = t1; - } - return i.intersected(); -} - // this flavor centers potential intersections recursively. In contrast, '2' may inadvertently // chase intersections near quadratic ends, requiring odd hacks to find them. static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2, @@ -325,7 +114,8 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& intersectWithOrder(s1, o1, s2, o2, locals); double coStart[2] = { -1 }; _Point coPoint; - for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { + int tCount = locals.used(); + for (int tIdx = 0; tIdx < tCount; ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx]; // if the computed t is not sufficiently precise, iterate @@ -353,39 +143,32 @@ static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& } } else { double offset = precisionScale / 16; // FIME: const is arbitrary -- test & refine - double c1Min = SkTMax(0., to1 - offset); - double c1Max = SkTMin(1., to1 + offset); - double c2Min = SkTMax(0., to2 - offset); - double c2Max = SkTMin(1., to2 + offset); - bool found = intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); - if (false && !found) { - // either offset was overagressive or cubics didn't really intersect - // if they didn't intersect, then quad tangents ought to be nearly parallel - offset = precisionScale / 2; // try much less agressive offset - c1Min = SkTMax(0., to1 - offset); - c1Max = SkTMin(1., to1 + offset); - c2Min = SkTMax(0., to2 - offset); - c2Max = SkTMin(1., to2 + offset); - found = intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); - if (found) { - SkDebugf("%s *** over-aggressive? offset=%1.9g depth=%d\n", __FUNCTION__, - offset, i.depth()); - } - // try parallel measure - _Vector d1 = dxdy_at_t(cubic1, to1); - _Vector d2 = dxdy_at_t(cubic2, to2); - double shallow = d1.cross(d2); - #if 1 || ONE_OFF_DEBUG // not sure this is worth debugging - if (!approximately_zero(shallow)) { - SkDebugf("%s *** near-miss? shallow=%1.9g depth=%d\n", __FUNCTION__, - offset, i.depth()); - } - #endif - if (i.depth() == 1 && shallow < 0.6) { - SkDebugf("%s !!! near-miss? shallow=%1.9g depth=%d\n", __FUNCTION__, - offset, i.depth()); - } + double c1Bottom = tIdx == 0 ? 0 : + (t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2; + double c1Min = SkTMax(c1Bottom, to1 - offset); + double c1Top = tIdx == tCount - 1 ? 1 : + (t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2; + double c1Max = SkTMin(c1Top, to1 + offset); + double c2Bottom = tIdx == 0 ? to2 : + (t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2; + double c2Top = tIdx == tCount - 1 ? to2 : + (t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2; + if (c2Bottom > c2Top) { + SkTSwap(c2Bottom, c2Top); + } + if (c2Bottom == to2) { + c2Bottom = 0; } + if (c2Top == to2) { + c2Top = 1; + } + double c2Min = SkTMax(c2Bottom, to2 - offset); + double c2Max = SkTMin(c2Top, to2 + offset); + intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); + // TODO: if no intersection is found, either quadratics intersected where + // cubics did not, or the intersection was missed. In the former case, expect + // the quadratics to be nearly parallel at the point of intersection, and check + // for that. } } SkASSERT(coStart[0] == -1); @@ -440,25 +223,6 @@ static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, c return intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); } -// FIXME: add intersection of convex hull on cubics' ends with the opposite cubic. The hull line -// segments can be constructed to be only as long as the calculated precision suggests. If the hull -// 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) { - 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; -} - const double CLOSE_ENOUGH = 0.001; static bool closeStart(const Cubic& cubic, int cubicIndex, Intersections& i, _Point& pt) { diff --git a/experimental/Intersection/CubicIntersection_Test.cpp b/experimental/Intersection/CubicIntersection_Test.cpp index 358afcf605..20c7671242 100644 --- a/experimental/Intersection/CubicIntersection_Test.cpp +++ b/experimental/Intersection/CubicIntersection_Test.cpp @@ -134,7 +134,10 @@ static const Cubic testSet[] = { const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]); static const Cubic newTestSet[] = { -{{0,1}, {2,5}, {6,0}, {5,3}}, +{{0,1}, {3,5}, {2,1}, {3,1}}, +{{1,2}, {1,3}, {1,0}, {5,3}}, + +{{0,1}, {2,5}, {6,0}, {5,3}}, {{0,6}, {3,5}, {1,0}, {5,2}}, {{0,1}, {3,6}, {1,0}, {5,2}}, @@ -152,6 +155,7 @@ static const Cubic newTestSet[] = { const size_t newTestSetCount = sizeof(newTestSet) / sizeof(newTestSet[0]); +#if 0 static void oneOff(const Cubic& cubic1, const Cubic& cubic2) { SkTDArray<Quadratic> quads1; cubic_to_quadratics(cubic1, calcPrecision(cubic1), quads1); @@ -224,7 +228,7 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) { tt2 = intersections3.fT[1][pt2]; xy_at_t(cubic2, tt2, xy2.x, xy2.y); #if ONE_OFF_DEBUG - SkDebugf("%s t3=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", + SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.x, xy1.y, intersections3.fPt[pt1].x, intersections3.fPt[pt1].y, xy2.x, xy2.y, tt2); #endif @@ -233,6 +237,7 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) { } } } +#endif static void oneOff3(const Cubic& cubic1, const Cubic& cubic2) { SkTDArray<Quadratic> quads1; @@ -270,7 +275,7 @@ static void oneOff3(const Cubic& cubic1, const Cubic& cubic2) { tt2 = intersections3.fT[1][pt2]; xy_at_t(cubic2, tt2, xy2.x, xy2.y); #if ONE_OFF_DEBUG - SkDebugf("%s t3=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", + SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.x, xy1.y, intersections3.fPt[pt3].x, intersections3.fPt[pt3].y, xy2.x, xy2.y, tt2); #endif @@ -278,6 +283,7 @@ static void oneOff3(const Cubic& cubic1, const Cubic& cubic2) { } } +#if 0 static int fails[][2] = { {0, 23}, // fails in intersect2 recursing {2, 7}, // answers differ, but neither is correct ('3' is closer) {3, 26}, // fails in intersect2 recursing @@ -293,10 +299,12 @@ static int fails[][2] = { {0, 23}, // fails in intersect2 recursing }; static int failCount = sizeof(fails) / sizeof(fails[0]); +#endif static void oneOff(int outer, int inner) { const Cubic& cubic1 = testSet[outer]; const Cubic& cubic2 = testSet[inner]; +#if 0 bool failing = false; for (int i = 0; i < failCount; ++i) { if ((fails[i][0] == outer && fails[i][1] == inner) @@ -308,8 +316,9 @@ static void oneOff(int outer, int inner) { if (!failing) { oneOff(cubic1, cubic2); } else { +#endif oneOff3(cubic1, cubic2); - } +// } } void CubicIntersection_OneOffTest() { @@ -324,6 +333,7 @@ static void newOneOff(int outer, int inner) { void CubicIntersection_NewOneOffTest() { newOneOff(0, 1); + newOneOff(1, 0); } static void oneOffTests() { @@ -356,7 +366,7 @@ bool intersect(double minT1, double maxT1, double minT2, double maxT2) { sub_divide(cubic1, minT1, maxT1, sub1); sub_divide(cubic2, minT2, maxT2, sub2); Intersections i; - intersect2(sub1, sub2, i); + intersect3(sub1, sub2, i); if (i.used() == 0) { return false; } @@ -433,7 +443,7 @@ void CubicIntersection_RandTestOld() { if (test == -1) { SkDebugf("ready...\n"); } - bool newIntersects = intersect2(cubic1, cubic2, i2); + bool newIntersects = intersect3(cubic1, cubic2, i2); if (!boundsIntersect && (oldIntersects || newIntersects)) { #if DEBUG_CRASH SkDebugf("%s %d unexpected intersection boundsIntersect=%d oldIntersects=%d" @@ -484,7 +494,7 @@ void CubicIntersection_RandTestOld() { #if DEBUG_CRASH SkDebugf("%s\n", str); #endif - oneOff(cubic1, cubic2); + oneOff3(cubic1, cubic2); largestFactor = factor1; } if (factor2 < largestFactor) { @@ -492,7 +502,7 @@ void CubicIntersection_RandTestOld() { #if DEBUG_CRASH SkDebugf("%s\n", str); #endif - oneOff(cubic1, cubic2); + oneOff3(cubic1, cubic2); largestFactor = factor2; } } @@ -527,7 +537,7 @@ void CubicIntersection_RandTest() { SkDebugf("ready...\n"); } Intersections intersections2; - bool newIntersects = intersect2(cubic1, cubic2, intersections2); + bool newIntersects = intersect3(cubic1, cubic2, intersections2); if (!boundsIntersect && newIntersects) { #if DEBUG_CRASH SkDebugf("%s %d unexpected intersection boundsIntersect=%d " @@ -556,10 +566,10 @@ void CubicIntersection_IntersectionFinder() { const Cubic& cubic1 = newTestSet[0]; const Cubic& cubic2 = newTestSet[1]; - double t1Seed = 0.77; - double t2Seed = 0.99; + double t1Seed = 0.134792061; + double t2Seed = 0.134792094; double t1Step = 0.1; - double t2Step = 0.01; + 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 1de2ef75e9..56f1e4548d 100644 --- a/experimental/Intersection/CubicUtilities.cpp +++ b/experimental/Intersection/CubicUtilities.cpp @@ -113,7 +113,7 @@ int cubicRootsReal(double A, double B, double C, double D, double s[3]) { 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 +#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA SkDebugf("%s\n", str); #endif #endif diff --git a/experimental/Intersection/CurveIntersection.h b/experimental/Intersection/CurveIntersection.h index d01bb7ddb8..cbcda76452 100644 --- a/experimental/Intersection/CurveIntersection.h +++ b/experimental/Intersection/CurveIntersection.h @@ -51,7 +51,7 @@ int horizontalIntersect(const Quadratic& quad, double left, double right, double y, bool flipped, Intersections& ); bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& ); // the following flavor uses quadratic approximation instead of convex hulls -bool intersect2(const Cubic& cubic1, const Cubic& cubic2, Intersections& ); +//bool intersect2(const Cubic& cubic1, const Cubic& cubic2, Intersections& ); // like '2', but iterates on centers instead of possible edges bool intersect3(const Cubic& cubic1, const Cubic& cubic2, Intersections& ); int intersect(const Cubic& cubic, Intersections& i); // return true if cubic self-intersects diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h index 4b682fff63..166ba813c7 100644 --- a/experimental/Intersection/DataTypes.h +++ b/experimental/Intersection/DataTypes.h @@ -13,6 +13,7 @@ #include "SkPoint.h" #define ONE_OFF_DEBUG 0 +#define ONE_OFF_DEBUG_MATHEMATICA 1 // FIXME: move these into SkTypes.h template <typename T> inline T SkTMax(T a, T b) { diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp index 8afc09f3cc..9f4aafc14e 100644 --- a/experimental/Intersection/EdgeWalker_TestUtility.cpp +++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp @@ -265,7 +265,8 @@ static void showShapeOpPath(const SkPath& one, const SkPath& two, const SkPath& showPath(a, "minuend:"); SkDebugf("op: %s\n", opStrs[shapeOp]); showPath(b, "subtrahend:"); - showPath(scaledOne, "region:", scale); + // the region often isn't very helpful since it approximates curves with a lot of line-tos + if (0) showPath(scaledOne, "region:", scale); showPath(two, "op result:"); drawAsciiPaths(scaledOne, scaledTwo, true); } diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp index 244f92f187..9e5237cdc3 100644 --- a/experimental/Intersection/Intersection_Tests.cpp +++ b/experimental/Intersection/Intersection_Tests.cpp @@ -14,11 +14,11 @@ void parseSVG(); void Intersection_Tests() { int testsRun = 0; + CubicIntersection_SelfTest(); QuadraticIntersection_IntersectionFinder(); QuadraticIntersection_OneOffTest(); CubicIntersection_IntersectionFinder(); CubicIntersection_NewOneOffTest(); - CubicIntersection_SelfTest(); #if 0 #endif SimplifyNew_Test(); diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp index 46cf20ab51..35fda3aae5 100644 --- a/experimental/Intersection/QuadraticIntersection_Test.cpp +++ b/experimental/Intersection/QuadraticIntersection_Test.cpp @@ -54,6 +54,9 @@ static void standardTestCases() { } static const Quadratic testSet[] = { + {{1,2}, {0.984375,2.3359375}, {1.0625,2.15625}}, + {{0,1}, {0.983539095,2.30041152}, {1.47325103,2.61316872}}, + {{4.09011926,2.20971038}, {4.74608133,1.9335932}, {5.02469918,2.00694987}}, {{2.79472921,1.73568666}, {3.36246373,1.21251209}, {5,2}}, diff --git a/experimental/Intersection/QuarticRoot.cpp b/experimental/Intersection/QuarticRoot.cpp index 061098a598..039a2c797f 100644 --- a/experimental/Intersection/QuarticRoot.cpp +++ b/experimental/Intersection/QuarticRoot.cpp @@ -41,7 +41,7 @@ int reducedQuarticRoots(const double t4, const double t3, const double t2, const 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 +#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA SkDebugf("%s\n", str); #endif #endif diff --git a/experimental/Intersection/ShapeOps.cpp b/experimental/Intersection/ShapeOps.cpp index f1792a87eb..7c7a205174 100644 --- a/experimental/Intersection/ShapeOps.cpp +++ b/experimental/Intersection/ShapeOps.cpp @@ -134,8 +134,6 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, bool firstContour = true; bool unsortable = false; bool topUnsortable = false; - bool firstRetry = false; - bool closable = true; SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; do { int index, endIndex; @@ -145,8 +143,7 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, if (!current) { if (topUnsortable || !done) { topUnsortable = false; - SkASSERT(!firstRetry); - firstRetry = true; + SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin); topLeft.fX = topLeft.fY = SK_ScalarMin; continue; } @@ -155,7 +152,6 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, SkTDArray<Span*> chaseArray; do { if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) { - bool active = true; do { #if DEBUG_ACTIVE_SPANS if (!unsortable && current->done()) { @@ -168,34 +164,37 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, Segment* next = current->findNextOp(chaseArray, nextStart, nextEnd, unsortable, op, xorMask, xorOpMask); if (!next) { - // SkASSERT(!unsortable); if (!unsortable && simple.hasMove() && current->verb() != SkPath::kLine_Verb && !simple.isClosed()) { current->addCurveTo(index, endIndex, simple, true); SkASSERT(simple.isClosed()); } - active = false; break; } + #if DEBUG_FLOW + SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, + current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY, + current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY); + #endif current->addCurveTo(index, endIndex, simple, true); current = next; index = nextStart; endIndex = nextEnd; - } while (!simple.isClosed() && ((!unsortable) || !current->done())); - if (active && !simple.isClosed()) { + } while (!simple.isClosed() && ((!unsortable) + || !current->done(SkMin32(index, endIndex)))); + if (current->activeWinding(index, endIndex) && !simple.isClosed()) { SkASSERT(unsortable); int min = SkMin32(index, endIndex); if (!current->done(min)) { current->addCurveTo(index, endIndex, simple, true); current->markDoneBinary(min); } - closable = false; } simple.close(); } else { Span* last = current->markAndChaseDoneBinary(index, endIndex); - if (last) { + if (last && !last->fLoop) { *chaseArray.append() = last; } } @@ -208,7 +207,7 @@ static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, } } while (true); } while (true); - return closable; + return simple.someAssemblyRequired(); } } // end of Op namespace diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp index e269ffd782..47ce8f49e9 100644 --- a/experimental/Intersection/Simplify.cpp +++ b/experimental/Intersection/Simplify.cpp @@ -69,7 +69,7 @@ const bool gRunTestsInOneThread = true; #define DEBUG_ADD_INTERSECTING_TS 1 #define DEBUG_ADD_T_PAIR 1 #define DEBUG_ANGLE 1 -#define DEBUG_AS_C_CODE 0 +#define DEBUG_AS_C_CODE 1 #define DEBUG_ASSEMBLE 1 #define DEBUG_CONCIDENT 1 #define DEBUG_CROSS 0 @@ -97,7 +97,7 @@ static int gSegmentID; #endif #if DEBUG_SORT -static int gDebugSortCountDefault = 3; // SK_MaxS32; +static int gDebugSortCountDefault = SK_MaxS32; static int gDebugSortCount; #endif @@ -649,6 +649,7 @@ struct Span { bool fUnsortableStart; // set when start is part of an unsortable pair bool fUnsortableEnd; // set when end is part of an unsortable pair bool fTiny; // if set, span may still be considered once for edge following + bool fLoop; // set when a cubic loops back to this point }; // sorting angles @@ -862,12 +863,10 @@ public: Quadratic& quad = (Quadratic&)fCurvePart; QuadSubDivideHD(fPts, startT, endT, quad); fTangent1.quadEndPoints(quad, 0, 1); - #if 1 // FIXME: try enabling this and see if a) it's called and b) does it break anything if (dx() == 0 && dy() == 0) { // SkDebugf("*** %s quad is line\n", __FUNCTION__); fTangent1.quadEndPoints(quad); } - #endif fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only } break; case SkPath::kCubic_Verb: { @@ -877,12 +876,10 @@ public: if (dx() == 0 && dy() == 0) { fTangent1.cubicEndPoints(fCurvePart, 0, 2); nextC = 3; - #if 1 // FIXME: try enabling this and see if a) it's called and b) does it break anything if (dx() == 0 && dy() == 0) { - SkDebugf("*** %s cubic is line\n", __FUNCTION__); + // SkDebugf("*** %s cubic is line\n", __FUNCTION__); fTangent1.cubicEndPoints(fCurvePart, 0, 3); } - #endif } fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only if (nextC == 2 && approximately_zero(fSide)) { @@ -1686,6 +1683,7 @@ public: span->fWindValue = 1; span->fOppValue = 0; span->fTiny = false; + span->fLoop = false; if ((span->fDone = newT == 1)) { ++fDoneSpans; } @@ -1881,6 +1879,13 @@ public: other.addCancelOutsides(tStart, oStart, *this, endT); } } + + int addSelfT(Segment* other, const SkPoint& pt, double& newT) { + int result = addT(other, pt, newT); + Span* span = &fTs[result]; + span->fLoop = true; + return result; + } int addUnsortableT(Segment* other, bool start, const SkPoint& pt, double& newT) { int result = addT(other, pt, newT); @@ -2435,6 +2440,7 @@ public: bool foundDone = false; // iterate through the angle, and compute everyone's winding Segment* nextSegment; + int activeCount = 0; do { SkASSERT(nextIndex != firstIndex); if (nextIndex == angleCount) { @@ -2446,9 +2452,16 @@ public: bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(), nextAngle->end(), op, sumMiWinding, sumSuWinding, maxWinding, sumWinding, oppMaxWinding, oppSumWinding); - if (activeAngle && (!foundAngle || foundDone)) { - foundAngle = nextAngle; - foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle); + if (activeAngle) { + ++activeCount; + if (!foundAngle || (foundDone && activeCount & 1)) { + if (nextSegment->tiny(nextAngle)) { + unsortable = true; + return NULL; + } + foundAngle = nextAngle; + foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle); + } } if (nextSegment->done()) { continue; @@ -2909,6 +2922,10 @@ public: SkVector dxyS = leftSegment->dxdy(tIndex); double cross = dxyE.cross(dxyS); bool bumpCheck = bumpsUp && xyE.fY < xyS.fY && dxyE.fX < 0; + if (xyE == xyS){ + SkDebugf("%s ignore loops\n", __FUNCTION__); + cross = 0; + } #if DEBUG_SWAP_TOP SkDebugf("%s xyE=(%1.9g,%1.9g) xyS=(%1.9g,%1.9g)\n", __FUNCTION__, xyE.fX, xyE.fY, xyS.fX, xyS.fY); @@ -3195,7 +3212,7 @@ the same winding is shared by both. Segment* other = this; while ((other = other->nextChase(index, step, min, last))) { if (other->fTs[min].fWindSum != SK_MinS32) { - SkASSERT(other->fTs[min].fWindSum == winding); + SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop); return NULL; } other->markWinding(min, winding, oppWinding); @@ -4442,6 +4459,11 @@ public: return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT); } + int addSelfT(int segIndex, Contour* other, int otherIndex, const SkPoint& pt, double& newT) { + setContainsIntercepts(); + return fSegments[segIndex].addSelfT(&other->fSegments[otherIndex], pt, newT); + } + int addUnsortableT(int segIndex, Contour* other, int otherIndex, bool start, const SkPoint& pt, double& newT) { return fSegments[segIndex].addUnsortableT(&other->fSegments[otherIndex], start, pt, newT); @@ -5133,6 +5155,10 @@ public: int addT(const Work& other, const SkPoint& pt, double& newT) { return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT); } + + int addSelfT(const Work& other, const SkPoint& pt, double& newT) { + return fContour->addSelfT(fIndex, other.fContour, other.fIndex, pt, newT); + } int addUnsortableT(const Work& other, bool start, const SkPoint& pt, double& newT) { return fContour->addUnsortableT(fIndex, other.fContour, other.fIndex, start, pt, newT); @@ -5694,7 +5720,7 @@ static void addSelfIntersectTs(Contour* test) { SkASSERT(ts.fT[0][0] >= 0 && ts.fT[0][0] <= 1); SkASSERT(ts.fT[1][0] >= 0 && ts.fT[1][0] <= 1); SkPoint point = ts.fPt[0].asSkPoint(); - int testTAt = wt.addT(wt, point, ts.fT[0][0]); + int testTAt = wt.addSelfT(wt, point, ts.fT[0][0]); int nextTAt = wt.addT(wt, point, ts.fT[1][0]); wt.addOtherT(testTAt, ts.fT[1][0], nextTAt); wt.addOtherT(nextTAt, ts.fT[0][0], testTAt); @@ -6157,7 +6183,7 @@ static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) simple.close(); } else { Span* last = current->markAndChaseDoneUnary(index, endIndex); - if (last) { + if (last && !last->fLoop) { *chaseArray.append() = last; } } diff --git a/experimental/Intersection/SimplifyAngle_Test.cpp b/experimental/Intersection/SimplifyAngle_Test.cpp index 9b25851eee..e83a52f650 100644 --- a/experimental/Intersection/SimplifyAngle_Test.cpp +++ b/experimental/Intersection/SimplifyAngle_Test.cpp @@ -230,10 +230,19 @@ static const segmentWithT oneOffTest3[] = { {SkPath::kMove_Verb } }; +static const segmentWithT oneOffTest4[] = { + {SkPath::kCubic_Verb, {{1,2}, {1,3}, {1,0}, {5,3}}, {0.134792, 0}}, + {SkPath::kCubic_Verb, {{0,1}, {3,5}, {2,1}, {3,1}}, {0.134792094, 0}}, + {SkPath::kCubic_Verb, {{0,1}, {3,5}, {2,1}, {3,1}}, {0.134792094, 0.551812363}}, + {SkPath::kCubic_Verb, {{1,2}, {1,3}, {1,0}, {5,3}}, {0.134792, 0.333333333}}, + {SkPath::kMove_Verb } +}; + static const segmentWithT* oneOffTests[] = { oneOffTest1, oneOffTest2, oneOffTest3, + oneOffTest4, }; static const size_t oneOffTestCount = sizeof(oneOffTests) / sizeof(oneOffTests[0]); @@ -259,6 +268,49 @@ static void oneOffTest(bool testFlat) { SkDebugf("%s finished\n", __FUNCTION__); } +#if 0 // seems too complicated for this to be useful (didn't finish writing/debugging this) +// this (trys to) take a pair of curves, construct segments/spans, and verify that they sort correctly +static void oneOffTestNew() { + const segmentWithT* segPtr = oneOffTest4; + SimplifyAngleTest::Segment segOne, segTwo; + segOne.init(segPtr[0].pts, segPtr[0].verb, false, false); + segTwo.init(segPtr[1].pts, segPtr[1].verb, false, false); + int index = 0; + do { + int next = index + 1; + if (segPtr[index].verb == SkPath::kMove_Verb) { + break; + } + SkPoint sub[4]; + (*SegmentSubDivide[segPtr[index].verb])(segPtr[index].pts, segPtr[index].ts[0], + segPtr[index].ts[1], sub); + if (memcmp(segPtr[index].pts, segPtr[0].pts, sizeof(SkPoint) * (segPtr[index].verb + 1) == 0) { + segOne.addT(&segTwo, sub[0], segPtr[index].ts[0]); + segOne.addT(&segTwo, sub[segPtr[index].verb], segPtr[index].ts[1]); + } else { + segTwo.addT(&segOne, sub[0], segPtr[index].ts[0]); + segTwo.addT(&v, sub[segPtr[index].verb], segPtr[index].ts[1]); + } + } while (true); + SimplifyAngleTest::Angle lesser, greater; + do { + int next = index + 1; + if (segPtr[next].verb == SkPath::kMove_Verb) { + break; + } + SkPoint one[4], two[4]; + bool use1 = memcmp(segPtr[index].pts, segPtr[0].pts, sizeof(SkPoint) * (segPtr[index].verb + 1) == 0; + lesser.set(segPtr[index].pts, segPtr[index].verb, use1 ? &segOne : &segTwo, index, next, dummy); + use1 = memcmp(segPtr[next].pts, segPtr[0].pts, sizeof(SkPoint) * (segPtr[next].verb + 1) == 0; + greater.set(segPtr[next].pts, segPtr[next].verb, use1 ? &segOne : &segTwo, index, next, dummy); + bool result = lesser < greater; + SkASSERT(result); + index = next; + } while (true); + SkDebugf("%s finished\n", __FUNCTION__); +} +#endif + static void (*tests[])(bool) = { oneOffTest, testSegments, diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp index ffba8dc6e4..145b856d8e 100644 --- a/experimental/Intersection/SimplifyNew_Test.cpp +++ b/experimental/Intersection/SimplifyNew_Test.cpp @@ -4164,6 +4164,19 @@ static void cubicOp31u() { testShapeOp(path, pathB, kUnion_Op); } +static void cubicOp31x() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,2); + path.cubicTo(0,3, 2,1, 4,0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(0,4, 2,0, 3,0); + pathB.close(); + testShapeOp(path, pathB, kXor_Op); +} + static void testCubic2() { SkPath path; path.moveTo(0,2); @@ -4175,15 +4188,72 @@ static void testCubic2() { testSimplifyx(path); } -static void (*firstTest)() = 0; +static void cubicOp32d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 6,0, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,6); + pathB.cubicTo(1,3, 1,0, 2,1); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void cubicOp33i() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 6,0, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,6); + pathB.cubicTo(1,3, 1,0, 2,1); + pathB.close(); + testShapeOp(path, pathB, kIntersect_Op); +} + +static void cubicOp34d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(3,5, 2,1, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(1,3, 1,0, 5,3); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void cubicOp35d() { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,5, 2,1, 4,0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(0,4, 1,0, 5,1); + pathB.close(); + testShapeOp(path, pathB, kDifference_Op); +} + +static void (*firstTest)() = cubicOp35d; static struct { void (*fun)(); const char* str; } tests[] = { + TEST(cubicOp35d), + TEST(cubicOp34d), + TEST(cubicOp33i), + TEST(cubicOp32d), + TEST(cubicOp31d), TEST(testCubic2), + TEST(cubicOp31x), TEST(cubicOp31u), - TEST(cubicOp31d), TEST(cubicOp30d), TEST(cubicOp29d), TEST(cubicOp28u), @@ -4591,7 +4661,7 @@ static void (*firstSubTest)() = 0; static bool skipAll = false; static bool runSubTestsFirst = false; -static bool runReverse = false; +static bool runReverse = true; static void (*stopTest)() = 0; void SimplifyNew_Test() { diff --git a/experimental/Intersection/as.htm b/experimental/Intersection/as.htm index 93845cbbc7..c8a50bb3e5 100644 --- a/experimental/Intersection/as.htm +++ b/experimental/Intersection/as.htm @@ -60,11 +60,177 @@ debugShowActiveSpans id=4 (3,0 1,2) t=0.5 (2,1) tEnd=0.875 other=2 otherT=0.5 ot debugShowActiveSpans id=4 (3,0 1,2) t=0.875 (1.25,1.75) tEnd=1 other=1 otherT=0.5 otherIndex=2 windSum=? windValue=1 oppValue=0 </div> +<div id="cubicOp35d"> + SimplifyNew_Test [cubicOp35d] +debugShowCubicIntersection no self intersect {{0,1}, {1,5}, {2,1}, {4,0}} +debugShowCubicLineIntersection wtTs[0]=0 {{0,1}, {1,5}, {2,1}, {4,0}} {{0,1}} wtTs[1]=1 {{4,0}} wnTs[0]=1 {{4,0}, {0,1}} wnTs[1]=0 +debugShowCubicIntersection wtTs[0]=0.210357794 {{0,1}, {1,5}, {2,1}, {4,0}} {{0.64038179843233178,2.5646764769093426}} wtTs[1]=0.788675135 {{2.8565880156561105,0.93208711896642604}} wnTs[0]=0.223476 {{1,2}, {0,4}, {1,0}, {5,1}} wnTs[1]=0.788675135 +debugShowCubicLineIntersection wtTs[0]=0.646900486 {{0,1}, {1,5}, {2,1}, {4,0}} {{2.2114165293341985,1.6971458676664504}} wnTs[0]=0.697146 {{5,1}, {1,2}} +debugShowCubicLineIntersection no intersect {{1,2}, {0,4}, {1,0}, {5,1}} {{4,0}, {0,1}} +debugShowLineIntersection no intersect {{4,0}, {0,1}} {{5,1}, {1,2}} +debugShowCubicIntersection wtTs[0]=0.002763735 {{1,2}, {0,4}, {1,0}, {5,1}} {{0.9917546455060533,2.0164451540317003}} wtTs[1]=0.461521979 +debugShowCubicLineIntersection wtTs[0]=0 {{1,2}, {0,4}, {1,0}, {5,1}} {{1,2}} wtTs[1]=0.466666667 {{1.0082962962962965,1.9979259259259259}} wtTs[2]=1 {{5,1}} wnTs[0]=1 {{5,1}, {1,2}} wnTs[1]=0.997925926 wnTs[2]=0 +debugShowActiveSpans id=1 (0,1 1,5 2,1 4,0) t=0 (0,1) tEnd=0.210357794 other=2 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=1 (0,1 1,5 2,1 4,0) t=0.210357794 (0.640381813,2.56467652) tEnd=0.646900486 other=3 otherT=0.223476406 otherIndex=2 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=1 (0,1 1,5 2,1 4,0) t=0.646900486 (2.21141648,1.69714582) tEnd=0.788675135 other=4 otherT=0.697145868 otherIndex=1 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=1 (0,1 1,5 2,1 4,0) t=0.788675135 (2.85658813,0.932087123) tEnd=1 other=3 otherT=0.788675135 otherIndex=5 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=2 (4,0 0,1) t=0 (4,0) tEnd=1 other=1 otherT=1 otherIndex=4 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0 (1,2) tEnd=0.002763735 other=4 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.002763735 (0.991754651,2.01644516) tEnd=0.223476406 other=3 otherT=0.461521979 otherIndex=3 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.223476406 (0.640381813,2.56467652) tEnd=0.461521979 other=1 otherT=0.210357794 otherIndex=1 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.461521979 (0.991754651,2.01644516) tEnd=0.466666667 other=3 otherT=0.002763735 otherIndex=1 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.466666667 (1.00829625,1.99792588) tEnd=0.788675135 other=4 otherT=0.997925926 otherIndex=2 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.788675135 (2.85658813,0.932087123) tEnd=1 other=1 otherT=0.788675135 otherIndex=3 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=4 (5,1 1,2) t=0 (5,1) tEnd=0.697145868 other=3 otherT=1 otherIndex=6 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=4 (5,1 1,2) t=0.697145868 (2.21141648,1.69714582) tEnd=0.997925926 other=1 otherT=0.646900486 otherIndex=2 windSum=? windValue=1 oppValue=0 +debugShowActiveSpans id=4 (5,1 1,2) t=0.997925926 (1.00829625,1.99792588) tEnd=1 other=3 otherT=0.466666667 otherIndex=4 windSum=? windValue=1 oppValue=0 +findTop debugShowSort contourWinding=0 oppContourWinding=0 sign=-1 +debugShowSort [0] id=2 line start=0 (4,0) end=1 (0,1) sign=-1 windValue=1 windSum=? 0->1 (max=1) done=0 tiny=0 opp=0 +debugShowSort [1] id=1 cubic start=4 (4,0) end=3 (2.85658813,0.932087123) sign=1 windValue=1 windSum=? 1->0 (max=1) done=0 tiny=0 opp=0 +markWinding id=2 (4,0 0,1) t=0 [0] (4,0) newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 +markWinding id=1 (0,1 1,5 2,1 4,0) t=0.788675135 [3] (2.85658813,0.932087123) newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 +markWinding id=2 (4,0 0,1) t=0 [0] (4,0) newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 +markWinding id=1 (0,1 1,5 2,1 4,0) t=0 [0] (0,1) newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 +activeOp op=diff miFrom=1 miTo=0 suFrom=0 suTo=0 result=1 +findNextOp simple +markDoneBinary id=2 (4,0 0,1) t=0 [0] (4,0) newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 +bridgeOp current id=2 from=(0,1) to=(4,0) +findNextOp debugShowSort contourWinding=0 oppContourWinding=0 sign=-1 +debugShowSort [1] id=1 cubic start=3 (2.85658813,0.932087123) end=4 (4,0) sign=-1 windValue=1 windSum=1 0->1 (max=1) done=0 tiny=0 opp=0 +debugShowSort [2] id=3 cubic start=5 (2.85658813,0.932087123) end=4 (1.00829625,1.99792588) sign=1 windValue=1 windSum=? 0->-1 (max=-1) done=0 tiny=0 opp=1 +debugShowSort [3] id=1 cubic start=3 (2.85658813,0.932087123) end=2 (2.21141648,1.69714582) sign=1 windValue=1 windSum=? 1->0 (max=1) done=0 tiny=0 opp=0 +debugShowSort [0] id=3 cubic start=5 (2.85658813,0.932087123) end=6 (5,1) sign=-1 windValue=1 windSum=? -1->0 (max=-1) done=0 tiny=0 opp=1 +findNextOp firstIndex=[1] sign=-1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +markWinding id=3 (1,2 0,4 1,0 5,1) t=0.466666667 [4] (1.00829625,1.99792588) newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=3 +activeOp op=diff miFrom=1 miTo=0 suFrom=1 suTo=1 result=0 +markDoneBinary id=1 (0,1 1,5 2,1 4,0) t=0.646900486 [2] (2.21141648,1.69714582) newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=1 +activeOp op=diff miFrom=0 miTo=0 suFrom=1 suTo=0 result=0 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0.788675135 [5] (2.85658813,0.932087123) newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 +markDoneBinary id=4 (5,1 1,2) t=0 [0] (5,1) newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=4 +markDoneBinary id=1 (0,1 1,5 2,1 4,0) t=0.788675135 [3] (2.85658813,0.932087123) newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 +findNextOp from:[1] to:[3] start=5 end=4 +bridgeOp current id=1 from=(4,0) to=(2.85658813,0.932087123) +path.moveTo(0,1); +path.lineTo(4,0); +path.cubicTo(3.57735038,0.211324871, 3.1993587,0.556624353, 2.85658813,0.932087123); +findNextOp debugShowSort contourWinding=-1 oppContourWinding=1 sign=-1 +debugShowSort [1] id=3 cubic start=4 (1.00829625,1.99792588) end=5 (2.85658813,0.932087123) sign=-1 windValue=1 windSum=-1 -1->0 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [2] id=4 line start=2 (1.00829625,1.99792588) end=3 (1,2) sign=-1 windValue=1 windSum=? 0->1 (max=1) done=0 tiny=0 opp=0 +debugShowSort [3] id=3 cubic start=4 (1.00829625,1.99792588) end=3 (0.991754651,2.01644516) sign=1 windValue=1 windSum=? 1->0 (max=1) done=0 tiny=0 opp=0 +debugShowSort [0] id=4 line start=2 (1.00829625,1.99792588) end=1 (2.21141648,1.69714582) sign=1 windValue=1 windSum=? 0->-1 (max=-1) done=0 tiny=0 opp=0 +findNextOp firstIndex=[1] sign=-1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +markWinding id=4 (5,1 1,2) t=0.997925926 [2] (1.00829625,1.99792588) newWindSum=1 newOppSum=1 oppSum=? windSum=? windValue=1 +markWinding id=3 (1,2 0,4 1,0 5,1) t=0 [0] (1,2) newWindSum=1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=3 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +markWinding id=3 (1,2 0,4 1,0 5,1) t=0.461521979 [3] (0.991754651,2.01644516) newWindSum=1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=3 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +markWinding id=4 (5,1 1,2) t=0.697145868 [1] (2.21141648,1.69714582) newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=4 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0.466666667 [4] (1.00829625,1.99792588) newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 +findNextOp from:[3] to:[4] start=2 end=3 +bridgeOp current id=3 from=(2.85658813,0.932087123) to=(1.00829625,1.99792588) +path.cubicTo(1.96246409,1.13237906, 1.35749662,1.61008465, 1.00829625,1.99792588); +findNextOp simple +markDoneBinary id=4 (5,1 1,2) t=0.997925926 [2] (1.00829625,1.99792588) newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 +bridgeOp current id=4 from=(1.00829625,1.99792588) to=(1,2) +Program received signal: “EXC_BAD_ACCESS”. +findNextOp debugShowSort contourWinding=1 oppContourWinding=1 sign=1 +debugShowSort [1] id=3 cubic start=1 (0.991754651,2.01644516) end=0 (1,2) sign=1 windValue=1 windSum=1 1->0 (max=1) done=0 tiny=0 opp=0 +debugShowSort [2] id=3 cubic start=3 (0.991754651,2.01644516) end=2 (0.640381813,2.56467652) sign=1 windValue=1 windSum=? 0->-1 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [3] id=3 cubic start=1 (0.991754651,2.01644516) end=2 (0.640381813,2.56467652) sign=-1 windValue=1 windSum=? -1->0 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [0] id=3 cubic start=3 (0.991754651,2.01644516) end=4 (1.00829625,1.99792588) sign=-1 windValue=1 windSum=1 0->1 (max=1) done=0 tiny=0 opp=0 +findNextOp firstIndex=[1] sign=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +markWinding id=3 (1,2 0,4 1,0 5,1) t=0.223476406 [2] (0.640381813,2.56467652) newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=3 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +markWinding id=3 (1,2 0,4 1,0 5,1) t=0.002763735 [1] (0.991754651,2.01644516) newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=3 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0 [0] (1,2) newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 +findNextOp from:[3] to:[3] start=3 end=2 +bridgeOp current id=3 from=(1,2) to=(0.991754651,2.01644516) +path.lineTo(1,2); +path.cubicTo(0.997236252,2.0055275, 0.994487822,2.01100922, 0.991754651,2.01644516); +findNextOp debugShowSort contourWinding=-1 oppContourWinding=1 sign=-1 +debugShowSort [0] id=3 cubic start=2 (0.640381813,2.56467652) end=3 (0.991754651,2.01644516) sign=-1 windValue=1 windSum=-1 -1->0 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [1] id=1 cubic start=1 (0.640381813,2.56467652) end=0 (0,1) sign=1 windValue=1 windSum=1 1->0 (max=1) done=0 tiny=0 opp=1 +debugShowSort [2] id=3 cubic start=2 (0.640381813,2.56467652) end=1 (0.991754651,2.01644516) sign=1 windValue=1 windSum=-1 0->-1 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [3] id=1 cubic start=1 (0.640381813,2.56467652) end=2 (2.21141648,1.69714582) sign=-1 windValue=1 windSum=? 0->1 (max=1) done=0 tiny=0 opp=1 +findNextOp firstIndex=[0] sign=-1 +activeOp op=diff miFrom=1 miTo=0 suFrom=0 suTo=0 result=1 +activeOp op=diff miFrom=0 miTo=0 suFrom=0 suTo=1 result=0 +activeOp op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0 +markDoneBinary id=1 (0,1 1,5 2,1 4,0) t=0.210357794 [1] (0.640381813,2.56467652) newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 +findNextOp chase.append id=1 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0.223476406 [2] (0.640381813,2.56467652) newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 +findNextOp from:[3] to:[1] start=1 end=0 +bridgeOp current id=3 from=(0.991754651,2.01644516) to=(0.640381813,2.56467652) +path.cubicTo(0.739642859,2.30096555, 0.627014875,2.53316927, 0.640381813,2.56467652); +findNextOp simple +markDoneBinary id=1 (0,1 1,5 2,1 4,0) t=0 [0] (0,1) newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 +bridgeOp current id=1 from=(0.640381813,2.56467652) to=(0,1) +path.cubicTo(0.42071557,2.32885909, 0.2103578,1.84143114, 0,1); +path.close(); +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.002763735 (0.991754651,2.01644516) tEnd=0.223476406 other=3 otherT=0.461521979 otherIndex=3 windSum=-1 windValue=1 oppValue=0 +debugShowActiveSpans id=3 (1,2 0,4 1,0 5,1) t=0.461521979 (0.991754651,2.01644516) tEnd=0.466666667 other=3 otherT=0.002763735 otherIndex=1 windSum=1 windValue=1 oppValue=0 +debugShowActiveSpans id=4 (5,1 1,2) t=0.697145868 (2.21141648,1.69714582) tEnd=0.997925926 other=1 otherT=0.646900486 otherIndex=2 windSum=-1 windValue=1 oppValue=0 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +findNextOp debugShowSort contourWinding=0 oppContourWinding=1 sign=1 +debugShowSort [0] id=4 line start=2 (1.00829625,1.99792588) end=1 (2.21141648,1.69714582) sign=1 windValue=1 windSum=-1 0->-1 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [1] id=3 cubic start=4 (1.00829625,1.99792588) end=5 (2.85658813,0.932087123) sign=-1 windValue=1 windSum=-1 -1->0 (max=-1) done=1 tiny=0 opp=0 +debugShowSort [2] id=4 line start=2 (1.00829625,1.99792588) end=3 (1,2) sign=-1 windValue=1 windSum=1 0->1 (max=1) done=1 tiny=0 opp=0 +debugShowSort [3] id=3 cubic start=4 (1.00829625,1.99792588) end=3 (0.991754651,2.01644516) sign=1 windValue=1 windSum=1 1->0 (max=1) done=0 tiny=0 opp=0 +findNextOp firstIndex=[0] sign=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +markDoneBinary id=4 (5,1 1,2) t=0.697145868 [1] (2.21141648,1.69714582) newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 +findNextOp from:[4] to:[3] start=4 end=3 +bridgeOp current id=4 from=(2.21141648,1.69714582) to=(1.00829625,1.99792588) +findNextOp debugShowSort contourWinding=0 oppContourWinding=1 sign=-1 +debugShowSort [0] id=3 cubic start=3 (0.991754651,2.01644516) end=4 (1.00829625,1.99792588) sign=-1 windValue=1 windSum=1 0->1 (max=1) done=0 tiny=0 opp=0 +debugShowSort [1] id=3 cubic start=1 (0.991754651,2.01644516) end=0 (1,2) sign=1 windValue=1 windSum=1 1->0 (max=1) done=1 tiny=0 opp=0 +debugShowSort [2] id=3 cubic start=3 (0.991754651,2.01644516) end=2 (0.640381813,2.56467652) sign=1 windValue=1 windSum=-1 0->-1 (max=-1) done=1 tiny=0 opp=0 +debugShowSort [3] id=3 cubic start=1 (0.991754651,2.01644516) end=2 (0.640381813,2.56467652) sign=-1 windValue=1 windSum=-1 -1->0 (max=-1) done=0 tiny=0 opp=0 +findNextOp firstIndex=[0] sign=-1 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0.461521979 [3] (0.991754651,2.01644516) newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 +findNextOp from:[3] to:[3] start=1 end=2 +bridgeOp current id=3 from=(1.00829625,1.99792588) to=(0.991754651,2.01644516) +path.moveTo(2.21141648,1.69714582); +path.lineTo(1.00829625,1.99792588); +path.cubicTo(1.00271726,2.0041225, 0.99720329,2.01029587, 0.991754651,2.01644516); +findNextOp debugShowSort contourWinding=0 oppContourWinding=1 sign=1 +debugShowSort [2] id=3 cubic start=2 (0.640381813,2.56467652) end=1 (0.991754651,2.01644516) sign=1 windValue=1 windSum=-1 0->-1 (max=-1) done=0 tiny=0 opp=0 +debugShowSort [3] id=1 cubic start=1 (0.640381813,2.56467652) end=2 (2.21141648,1.69714582) sign=-1 windValue=1 windSum=1 1->2 (max=2) done=1 tiny=0 opp=1 +debugShowSort [0] id=3 cubic start=2 (0.640381813,2.56467652) end=3 (0.991754651,2.01644516) sign=-1 windValue=1 windSum=-1 -1->0 (max=-1) done=1 tiny=0 opp=0 +debugShowSort [1] id=1 cubic start=1 (0.640381813,2.56467652) end=0 (0,1) sign=1 windValue=1 windSum=1 2->1 (max=2) done=1 tiny=0 opp=1 +findNextOp firstIndex=[2] sign=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=1 result=0 +activeOp op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1 +activeOp op=diff miFrom=1 miTo=1 suFrom=0 suTo=0 result=0 +markDoneBinary id=3 (1,2 0,4 1,0 5,1) t=0.002763735 [1] (0.991754651,2.01644516) newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 +findNextOp from:[3] to:[3] start=2 end=3 +bridgeOp current id=3 from=(0.991754651,2.01644516) to=(0.640381813,2.56467652) +path.cubicTo(0.773483634,2.45056915, 0.652775407,2.59388947, 0.640381813,2.56467652); +</div> + </div> <script type="text/javascript"> var testDivs = [ + cubicOp35d, cubicOp31da, cubicOp31d, cubicOp28u, @@ -140,14 +306,48 @@ var SPAN_C_VAL = 18; var SPAN_C_OPP = 19; var ACTIVE_LINE_SPAN = 1; -var ACTIVE_QUAD_SPAN = 2; -var ACTIVE_CUBIC_SPAN = 3; -var INTERSECT_QUAD_LINE = 4; -var INTERSECT_QUAD_LINE_2 = 5; -var INTERSECT_QUAD_LINE_NO = 6; -var INTERSECT_QUAD = 7; -var INTERSECT_QUAD_2 = 8; -var INTERSECT_QUAD_NO = 9; +var ACTIVE_QUAD_SPAN = ACTIVE_LINE_SPAN + 1; +var ACTIVE_CUBIC_SPAN = ACTIVE_QUAD_SPAN + 1; + +var PATH_MOVETO = ACTIVE_CUBIC_SPAN + 1; +var PATH_LINETO = PATH_MOVETO + 1; +var PATH_QUADTO = PATH_LINETO + 1; +var PATH_CUBICTO = PATH_QUADTO + 1; +var PATH_CLOSE = PATH_CUBICTO + 1; + +var INTERSECT_LINE = PATH_CLOSE + 1; +var INTERSECT_LINE_2 = INTERSECT_LINE + 1; +var INTERSECT_LINE_NO = INTERSECT_LINE_2 + 1; +var INTERSECT_QUAD_LINE = INTERSECT_LINE_NO + 1; +var INTERSECT_QUAD_LINE_2 = INTERSECT_QUAD_LINE + 1; +var INTERSECT_QUAD_LINE_NO = INTERSECT_QUAD_LINE_2 + 1; +var INTERSECT_QUAD = INTERSECT_QUAD_LINE_NO + 1; +var INTERSECT_QUAD_2 = INTERSECT_QUAD + 1; +var INTERSECT_QUAD_NO = INTERSECT_QUAD_2 + 1; +var INTERSECT_SELF_CUBIC = INTERSECT_QUAD_NO + 1; +var INTERSECT_SELF_CUBIC_NO = INTERSECT_SELF_CUBIC + 1; +var INTERSECT_CUBIC_LINE = INTERSECT_SELF_CUBIC_NO + 1; +var INTERSECT_CUBIC_LINE_2 = INTERSECT_CUBIC_LINE + 1; +var INTERSECT_CUBIC_LINE_3 = INTERSECT_CUBIC_LINE_2 + 1; +var INTERSECT_CUBIC_LINE_NO = INTERSECT_CUBIC_LINE_3 + 1; +// FIXME: add cubic/quad +var INTERSECT_CUBIC = INTERSECT_CUBIC_LINE_NO + 1; +var INTERSECT_CUBIC_2 = INTERSECT_CUBIC + 1; +var INTERSECT_CUBIC_3 = INTERSECT_CUBIC_2 + 1; +var INTERSECT_CUBIC_4 = INTERSECT_CUBIC_3 + 1; +// FIXME: add cubic 5- 9 +var INTERSECT_CUBIC_NO = INTERSECT_CUBIC_4 + 1; + +var SORT_LINE = INTERSECT_CUBIC_NO + 1; +var SORT_QUAD = SORT_LINE + 1; +var SORT_CUBIC = SORT_QUAD + 1; + +var REC_TYPE_UNKNOWN = -1; +var REC_TYPE_PATH = 0; +var REC_TYPE_SECT = 1; +var REC_TYPE_ACTIVE = 2; +var REC_TYPE_ADD = 3; +var REC_TYPE_SORT = 4; function strs_to_nums(strs) { var result = []; @@ -215,7 +415,9 @@ function filter_str_by(id, str, regex, array) { var result = strs_to_nums(strs); array.push(id); array.push(result); + return true; } + return false; } // FIXME: add cubic support @@ -249,6 +451,150 @@ function parse_intersections(test, title) { } } +function construct_regexp2(pattern) { + var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); + escape = escape.replace(/CUBIC_VAL/g, "\\(P_VAL P_VAL P_VAL P_VAL\\)"); + escape = escape.replace(/QUAD_VAL/g, "\\(P_VAL P_VAL P_VAL\\)"); + escape = escape.replace(/LINE_VAL/g, "\\(P_VAL P_VAL\\)"); + escape = escape.replace(/PT_VAL/g, "\\(P_VAL\\)"); + escape = escape.replace(/P_VAL/g, "(\\d+\\.?\\d*),(\\d+\\.?\\d*)"); + escape = escape.replace(/T_VAL/g, "(\\d+\\.?\\d*e?-?\\d*)"); + escape = escape.replace(/IDX/g, "(\\d+)"); + escape = escape.replace(/NUM/g, "(-?\\d+)"); + escape = escape.replace(/OPT/g, "(\\?|-?\\d+)"); + return new RegExp(escape, 'i'); +} + +function construct_regexp2c(pattern) { + var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); + escape = escape.replace(/CUBIC_VAL/g, "\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}"); + escape = escape.replace(/QUAD_VAL/g, "\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}"); + escape = escape.replace(/LINE_VAL/g, "\\{\\{P_VAL\\}, \\{P_VAL\\}\\}"); + escape = escape.replace(/PT_VAL/g, "\\{\\{P_VAL\\}\\}"); + escape = escape.replace(/P_VAL/g, "(\\d+\\.?\\d*),(\\d+\\.?\\d*)"); + escape = escape.replace(/T_VAL/g, "(\\d+\\.?\\d*e?-?\\d*)"); + escape = escape.replace(/IDX/g, "(\\d+)"); + escape = escape.replace(/NUM/g, "(-?\\d+)"); + escape = escape.replace(/OPT/g, "(\\?|-?\\d+)"); + return new RegExp(escape, 'i'); +} + +function match_regexp(str, lineNo, array, id, pattern) { + var regex = construct_regexp2(pattern); + if (filter_str_by(id, str, regex, array)) { + return true; + } + regex = construct_regexp2c(pattern); + return filter_str_by(id, str, regex, array); +} + +function parse_all(test, title) { + var lines = test.match(/[^\r\n]+/g); + var records = []; // a rec can be the original paths, a set of intersections, a set of active spans, a sort, or a path add + var record = []; + var recType = REC_TYPE_UNKNOWN; + for (var lineNo in lines) { + var line = lines[lineNo]; + var type = line.lastIndexOf("debugShowSort", 0) === 0 ? REC_TYPE_SORT + : line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE + : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT + : line.lastIndexOf("path.", 0) === 0 ? REC_TYPE_ADD + : line.lastIndexOf("{{", 0) === 0 ? REC_TYPE_PATH + : REC_TYPE_UNKNOWN; + if (recType != type) { + if (recType != REC_TYPE_UNKNOWN) { + records.push(recType); + records.push(lineNo); + records.push(record); + } + record = []; + recType = type; + } + var found = false; + switch (recType) { + case REC_TYPE_ACTIVE: + found = match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" + +" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=IDX" + ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" + +" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=IDX" + ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" + +" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=IDX" + ); + break; + case REC_TYPE_ADD: + found = match_regexp(line, lineNo, record, PATH_MOVETO, "path.moveToPT_VAL;" + ) || match_regexp(line, lineNo, record, PATH_LINETO, "path.lineTo(P_VAL);" + ) || match_regexp(line, lineNo, record, PATH_QUADTO, "path.quadTo(P_VAL, P_VAL);" + ) || match_regexp(line, lineNo, record, PATH_CUBICTO, "path.cubicTo(P_VAL, P_VAL, P_VAL);" + ) || match_regexp(line, lineNo, record, PATH_CLOSE, "path.close();" + ); + break; + case REC_TYPE_SECT: + found = match_regexp(line, lineNo, record, INTERSECT_LINE, "debugShowLineIntersection" + +" wtTs[0]=T_VAL LINE_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_LINE_2, "debugShowLineIntersection" + +" wtTs[0]=T_VAL LINE_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_LINE_NO, "debugShowLineIntersection" + +" no intersect LINE_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE, "debugShowQuadLineIntersection" + +" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_2, "debugShowQuadLineIntersection" + +" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_LINE_NO, "debugShowQuadLineIntersection" + +" no intersect QUAD_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD, "debugShowQuadIntersection" + +" wtTs[0]=T_VAL QUAD_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_2, "debugShowQuadIntersection" + +" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_NO, "debugShowQuadIntersection" + +" no intersect QUAD_VAL QUAD_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC, "debugShowCubicIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_SELF_CUBIC_NO, "debugShowCubicIntersection" + +" no self intersect CUBIC_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE, "debugShowCubicLineIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_2, "debugShowCubicLineIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_3, "debugShowCubicLineIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_NO, "debugShowCubicLineIntersection" + +" no intersect CUBIC_VAL LINE_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC, "debugShowCubicIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_2, "debugShowCubicIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_3, "debugShowCubicIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_4, "debugShowCubicIntersection" + +" wtTs[0]=T_VAL CUBIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wtTs[2]=T_VAL PT_VAL wtTs[3]=T_VAL PT_VAL wnTs[0]=T_VAL CUBIC_VAL wnTs[1]=T_VAL wnTs[2]=T_VAL wnTs[3]=T_VAL" + ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_NO, "debugShowCubicIntersection" + +" no intersect CUBIC_VAL CUBIC_VAL" + ); + break; + case REC_TYPE_SORT: + found = match_regexp(line, lineNo, record, SORT_LINE, "debugShowSort" + +" [IDX] id=IDX line start=IDX PT_VAL end=IDX PT_VAL sign=NUM windValue=IDX windSum=OPT NUM->NUM (max=NUM) done=IDX tiny=IDX opp=IDX" + ) || match_regexp(line, lineNo, record, SORT_QUAD, "debugShowSort" + +" [IDX] id=IDX quad start=IDX PT_VAL end=IDX PT_VAL sign=NUM windValue=IDX windSum=OPT NUM->NUM (max=NUM) done=IDX tiny=IDX opp=IDX" + ) || match_regexp(line, lineNo, record, SORT_CUBIC, "debugShowSort" + +" [IDX] id=IDX cubic start=IDX PT_VAL end=IDX PT_VAL sign=NUM windValue=IDX windSum=OPT NUM->NUM (max=NUM) done=IDX tiny=IDX opp=IDX" + ); + break; + case REC_TYPE_UNKNOWN: + found = true; + break; + } + if (!found) { + console.log(line + " [" + lineNo + "] of type " + type + " not found"); + } + } + if (records.length >= 1) { + tests.push(records); + testTitles.push(title); + } +} + function init(test) { var canvas = document.getElementById('canvas'); if (!canvas.getContext) return; @@ -902,6 +1248,7 @@ function start() { for (i = 0; i < testDivs.length; ++i) { var title = testDivs[i].id.toString(); var str = testDivs[i].firstChild.data; + parse_all(str, title); parse_debugShowActiveSpans(str, title); parse_intersections(str, title); } diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index 199a9df076..267fbec338 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -3721,11 +3721,59 @@ path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction); pathB.close(); </div> +<div id="cubicOp32d"> + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 6,0, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,6); + pathB.cubicTo(1,3, 1,0, 2,1); + pathB.close(); +</div> + +<div id="cubicOp33i"> + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,2, 6,0, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,6); + pathB.cubicTo(1,3, 1,0, 2,1); + pathB.close(); +</div> + +<div id="cubicOp34d"> + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(3,5, 2,1, 3,1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(1,3, 1,0, 5,3); + pathB.close(); +</div> + +<div id="cubicOp35d"> + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(1,5, 2,1, 4,0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(0,4, 1,0, 5,1); + pathB.close(); +</div> + </div> <script type="text/javascript"> var testDivs = [ + cubicOp35d, + cubicOp34d, + cubicOp33i, + cubicOp32d, cubicOp31d, cubicOp30d, cubicOp29d, @@ -4267,11 +4315,11 @@ function handleMouseOver() { calcXY(); var num = mouseX.toFixed(3) + ", " + mouseY.toFixed(3); ctx.beginPath(); - ctx.rect(300,100,200,10); + ctx.rect(300,0,72,9); ctx.fillStyle="white"; ctx.fill(); ctx.fillStyle="black"; - ctx.fillText(num, 300, 108); + ctx.fillText(num, 300, 7); } function handleMouseClick() { diff --git a/experimental/Intersection/qc.htm b/experimental/Intersection/qc.htm index db77fa4d70..e3075d1e79 100644 --- a/experimental/Intersection/qc.htm +++ b/experimental/Intersection/qc.htm @@ -2086,11 +2086,48 @@ quad=(1.20573276,0.813456177 1.2679289,0.830085346 1.33333333,0.851851852) {{2.79472921,1.73568666}, {3.36246373,1.21251209}, {5,2}}, </div> +<div id="cubicOp34d"> +{{x = 1.0097960937999808, y = 2.2108396209439607}, {x = 1, y = 2.1969085233712229}, {x = 1, y = 2.1347920612232207}, {x = 1, y = 2}} +{{x = 1.0097960937999808, y = 2.2108396209439607}, {x = 1.0242251996917398, y = 2.2313593593386498}, {x = 1.0599075827658746, y = 2.1473377437648784}, {x = 1.1481481481481481, y = 2.0370370370370372}} +{{x = 1.0097960958786989, y = 2.2108396260650962}, {x = 0.73607693096853644, y = 1.9329854848088734}, {x = 0.40437628284615079, y = 1.5391683771282005}, {x = 0, y = 1}} +{{x = 1.0097960958786989, y = 2.2108396260650962}, {x = 1.8566294376993437, y = 3.0704657726520206}, {x = 2.1484860084122528, y = 2.8201383770234716}, {x = 2.320499529631658, y = 2.3301248824079162}} +</div> + +<div id="cubicOp34da"> +{{x = 1.0097960937999808, y = 2.2108396209439607}, {x = 1, y = 2.1969085233712229}, {x = 1, y = 2.1347920612232207}, {x = 1, y = 2}} +{{x = 1.0097960937999808, y = 2.2108396209439607}, {x = 1.0242251996917398, y = 2.2313593593386498}, {x = 1.0599075827658746, y = 2.1473377437648784}, {x = 1.1481481481481481, y = 2.0370370370370372}} +</div> + +<div id="cubicOp34db"> +{{1,2}, {1,3}, {1,0}, {5,3}}, +{{0,1}, {3,5}, {2,1}, {3,1}}, + + {{1,2}, {0.984375,2.3359375}, {1.0625,2.15625}}, + {{1.0625,2.15625}, {1.140625,1.9765625}, {1.5,1.75}}, + {{1.5,1.75}, {1.859375,1.5234375}, {2.6875,1.71875}}, + {{2.6875,1.71875}, {3.515625,1.9140625}, {5,3}}, + + {{0,1}, {0.983539095,2.30041152}, {1.47325103,2.61316872}}, + {{1.47325103,2.61316872}, {1.96296296,2.92592593}, {2.1563786,2.64609053}}, + {{2.1563786,2.64609053}, {2.34979424,2.36625514}, {2.44444444,1.88888889}}, + {{2.44444444,1.88888889}, {2.52083333,1.54166667}, {2.63888889,1.27777778}}, + {{2.63888889,1.27777778}, {2.75694444,1.01388889}, {3,1}}, +</div> + +<div id="quadOp34d"> + {{1,2}, {0.984375,2.3359375}, {1.0625,2.15625}}, + {{0,1}, {0.983539095,2.30041152}, {1.47325103,2.61316872}}, +</div> + </div> <script type="text/javascript"> var testDivs = [ + quadOp34d, + cubicOp34db, + cubicOp34d, + cubicOp34da, quadOp30d, cubicOp30d, quadOp27d, |