Revert of pathops version two (patchset #16 id:150001 of https://codereview.chromium...
authorreed <reed@google.com>
Tue, 24 Mar 2015 20:55:33 +0000 (13:55 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 24 Mar 2015 20:55:33 +0000 (13:55 -0700)
Reason for revert:
ASAN investigation

Original issue's description:
> pathops version two
>
> R=reed@google.com
>
> marked 'no commit' to attempt to get trybots to run
>
> TBR=reed@google.com
>
> Committed: https://skia.googlesource.com/skia/+/ccec0f958ffc71a9986d236bc2eb335cb2111119

TBR=caryclark@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review URL: https://codereview.chromium.org/1029993002

95 files changed:
gyp/core.gypi
gyp/pathops.gypi [new file with mode: 0644]
gyp/pathops_unittest.gyp
gyp/pathops_unittest.gypi
include/pathops/SkPathOps.h
src/pathops/SkAddIntersections.cpp
src/pathops/SkAddIntersections.h
src/pathops/SkDCubicIntersection.cpp [new file with mode: 0644]
src/pathops/SkDCubicLineIntersection.cpp
src/pathops/SkDCubicToQuads.cpp
src/pathops/SkDLineIntersection.cpp
src/pathops/SkDQuadImplicit.cpp [new file with mode: 0644]
src/pathops/SkDQuadImplicit.h [new file with mode: 0644]
src/pathops/SkDQuadIntersection.cpp [new file with mode: 0644]
src/pathops/SkDQuadLineIntersection.cpp
src/pathops/SkIntersectionHelper.h
src/pathops/SkIntersections.cpp
src/pathops/SkIntersections.h
src/pathops/SkOpAngle.cpp
src/pathops/SkOpAngle.h
src/pathops/SkOpCoincidence.cpp [deleted file]
src/pathops/SkOpCoincidence.h
src/pathops/SkOpContour.cpp
src/pathops/SkOpContour.h
src/pathops/SkOpEdgeBuilder.cpp
src/pathops/SkOpEdgeBuilder.h
src/pathops/SkOpSegment.cpp
src/pathops/SkOpSegment.h
src/pathops/SkOpSpan.cpp [deleted file]
src/pathops/SkOpSpan.h
src/pathops/SkOpTAllocator.h
src/pathops/SkPathOpsCommon.cpp
src/pathops/SkPathOpsCommon.h
src/pathops/SkPathOpsCubic.cpp
src/pathops/SkPathOpsCubic.h
src/pathops/SkPathOpsCubicSect.h [new file with mode: 0644]
src/pathops/SkPathOpsDebug.cpp
src/pathops/SkPathOpsDebug.h
src/pathops/SkPathOpsLine.cpp
src/pathops/SkPathOpsLine.h
src/pathops/SkPathOpsOp.cpp
src/pathops/SkPathOpsPoint.h
src/pathops/SkPathOpsPostSect.cpp [changed mode: 0755->0644]
src/pathops/SkPathOpsQuad.cpp
src/pathops/SkPathOpsQuad.h
src/pathops/SkPathOpsQuadSect.h [new file with mode: 0644]
src/pathops/SkPathOpsRect.cpp
src/pathops/SkPathOpsRect.h
src/pathops/SkPathOpsSimplify.cpp
src/pathops/SkPathOpsTCubicSect.cpp
src/pathops/SkPathOpsTQuadSect.cpp
src/pathops/SkPathOpsTSect.h
src/pathops/SkPathOpsTightBounds.cpp
src/pathops/SkPathOpsTriangle.cpp [new file with mode: 0644]
src/pathops/SkPathOpsTriangle.h [new file with mode: 0644]
src/pathops/SkPathOpsTypes.h
src/pathops/SkQuarticRoot.cpp [new file with mode: 0644]
src/pathops/SkQuarticRoot.h [new file with mode: 0644]
src/pathops/SkReduceOrder.cpp
src/pathops/SkReduceOrder.h
tests/PathOpsAngleIdeas.cpp
tests/PathOpsAngleTest.cpp
tests/PathOpsBattles.cpp
tests/PathOpsBuilderTest.cpp
tests/PathOpsCubicIntersectionTest.cpp
tests/PathOpsCubicIntersectionTestData.cpp
tests/PathOpsCubicLineIntersectionTest.cpp
tests/PathOpsCubicQuadIntersectionTest.cpp
tests/PathOpsCubicToQuadsTest.cpp [new file with mode: 0644]
tests/PathOpsDLineTest.cpp
tests/PathOpsDPointTest.cpp
tests/PathOpsDQuadTest.cpp [new file with mode: 0644]
tests/PathOpsDRectTest.cpp
tests/PathOpsDTriangleTest.cpp [new file with mode: 0644]
tests/PathOpsDebug.cpp
tests/PathOpsExtendedTest.cpp
tests/PathOpsExtendedTest.h
tests/PathOpsFuzz763Test.cpp
tests/PathOpsLineIntersectionTest.cpp
tests/PathOpsOpCubicThreadedTest.cpp
tests/PathOpsOpLoopThreadedTest.cpp
tests/PathOpsOpTest.cpp
tests/PathOpsQuadIntersectionTest.cpp
tests/PathOpsQuadIntersectionTestData.cpp
tests/PathOpsQuadParameterizationTest.cpp [new file with mode: 0644]
tests/PathOpsSimplifyFailTest.cpp
tests/PathOpsSimplifyTest.cpp
tests/PathOpsSkpTest.cpp
tests/PathOpsTSectDebug.h
tests/PathOpsTestCommon.cpp
tests/PathOpsTestCommon.h
tests/PathOpsThreeWayTest.cpp
tests/PathOpsTightBoundsTest.cpp
tools/pathops_sorter.htm
tools/pathops_visualizer.htm

index 4d8938844c7a7efa9deaf39deeebb701b5c9dfaa..95e5eb939c6a16ad2032a9c29b48acfc59a1f0b0 100644 (file)
         '<(skia_include_path)/pathops/SkPathOps.h',
 
         '<(skia_src_path)/pathops/SkAddIntersections.cpp',
+        '<(skia_src_path)/pathops/SkDCubicIntersection.cpp',
         '<(skia_src_path)/pathops/SkDCubicLineIntersection.cpp',
         '<(skia_src_path)/pathops/SkDCubicToQuads.cpp',
         '<(skia_src_path)/pathops/SkDLineIntersection.cpp',
+        '<(skia_src_path)/pathops/SkDQuadImplicit.cpp',
+        '<(skia_src_path)/pathops/SkDQuadIntersection.cpp',
         '<(skia_src_path)/pathops/SkDQuadLineIntersection.cpp',
         '<(skia_src_path)/pathops/SkIntersections.cpp',
         '<(skia_src_path)/pathops/SkOpAngle.cpp',
-        '<(skia_src_path)/pathops/SkOpBuilder.cpp',
-        '<(skia_src_path)/pathops/SkOpCoincidence.cpp',
         '<(skia_src_path)/pathops/SkOpContour.cpp',
-        '<(skia_src_path)/pathops/SkOpCubicHull.cpp',
         '<(skia_src_path)/pathops/SkOpEdgeBuilder.cpp',
         '<(skia_src_path)/pathops/SkOpSegment.cpp',
-        '<(skia_src_path)/pathops/SkOpSpan.cpp',
         '<(skia_src_path)/pathops/SkPathOpsBounds.cpp',
         '<(skia_src_path)/pathops/SkPathOpsCommon.cpp',
         '<(skia_src_path)/pathops/SkPathOpsCubic.cpp',
         '<(skia_src_path)/pathops/SkPathOpsQuad.cpp',
         '<(skia_src_path)/pathops/SkPathOpsRect.cpp',
         '<(skia_src_path)/pathops/SkPathOpsSimplify.cpp',
-        '<(skia_src_path)/pathops/SkPathOpsTCubicSect.cpp',
         '<(skia_src_path)/pathops/SkPathOpsTightBounds.cpp',
-        '<(skia_src_path)/pathops/SkPathOpsTQuadSect.cpp',
+        '<(skia_src_path)/pathops/SkPathOpsTriangle.cpp',
         '<(skia_src_path)/pathops/SkPathOpsTypes.cpp',
         '<(skia_src_path)/pathops/SkPathWriter.cpp',
+        '<(skia_src_path)/pathops/SkQuarticRoot.cpp',
         '<(skia_src_path)/pathops/SkReduceOrder.cpp',
-
         '<(skia_src_path)/pathops/SkAddIntersections.h',
+        '<(skia_src_path)/pathops/SkDQuadImplicit.h',
         '<(skia_src_path)/pathops/SkIntersectionHelper.h',
         '<(skia_src_path)/pathops/SkIntersections.h',
         '<(skia_src_path)/pathops/SkLineParameters.h',
         '<(skia_src_path)/pathops/SkOpAngle.h',
-        '<(skia_src_path)/pathops/SkOpCoincidence.h',
         '<(skia_src_path)/pathops/SkOpContour.h',
         '<(skia_src_path)/pathops/SkOpEdgeBuilder.h',
         '<(skia_src_path)/pathops/SkOpSegment.h',
         '<(skia_src_path)/pathops/SkOpSpan.h',
-        '<(skia_src_path)/pathops/SkOpTAllocator.h',
         '<(skia_src_path)/pathops/SkPathOpsBounds.h',
         '<(skia_src_path)/pathops/SkPathOpsCommon.h',
         '<(skia_src_path)/pathops/SkPathOpsCubic.h',
         '<(skia_src_path)/pathops/SkPathOpsPoint.h',
         '<(skia_src_path)/pathops/SkPathOpsQuad.h',
         '<(skia_src_path)/pathops/SkPathOpsRect.h',
-        '<(skia_src_path)/pathops/SkPathOpsTSect.h',
+        '<(skia_src_path)/pathops/SkPathOpsTriangle.h',
         '<(skia_src_path)/pathops/SkPathOpsTypes.h',
         '<(skia_src_path)/pathops/SkPathWriter.h',
+        '<(skia_src_path)/pathops/SkQuarticRoot.h',
         '<(skia_src_path)/pathops/SkReduceOrder.h',
     ],
 }
diff --git a/gyp/pathops.gypi b/gyp/pathops.gypi
new file mode 100644 (file)
index 0000000..3bb163e
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  'include_dirs' : [
+    '../include/pathops',
+    '../src/pathops',
+  ],
+  'sources': [
+    '../include/pathops/SkPathOps.h',
+    '../src/pathops/SkAddIntersections.cpp',
+    '../src/pathops/SkDCubicIntersection.cpp',
+    '../src/pathops/SkDCubicLineIntersection.cpp',
+    '../src/pathops/SkDCubicToQuads.cpp',
+    '../src/pathops/SkDLineIntersection.cpp',
+    '../src/pathops/SkDQuadImplicit.cpp',
+    '../src/pathops/SkDQuadIntersection.cpp',
+    '../src/pathops/SkDQuadLineIntersection.cpp',
+    '../src/pathops/SkIntersections.cpp',
+    '../src/pathops/SkOpAngle.cpp',
+    '../src/pathops/SkOpContour.cpp',
+    '../src/pathops/SkOpEdgeBuilder.cpp',
+    '../src/pathops/SkOpSegment.cpp',
+    '../src/pathops/SkPathOpsBounds.cpp',
+    '../src/pathops/SkPathOpsCommon.cpp',
+    '../src/pathops/SkPathOpsCubic.cpp',
+    '../src/pathops/SkPathOpsDebug.cpp',
+    '../src/pathops/SkPathOpsLine.cpp',
+    '../src/pathops/SkPathOpsOp.cpp',
+    '../src/pathops/SkPathOpsPoint.cpp',
+    '../src/pathops/SkPathOpsQuad.cpp',
+    '../src/pathops/SkPathOpsRect.cpp',
+    '../src/pathops/SkPathOpsSimplify.cpp',
+    '../src/pathops/SkPathOpsTriangle.cpp',
+    '../src/pathops/SkPathOpsTypes.cpp',
+    '../src/pathops/SkPathWriter.cpp',
+    '../src/pathops/SkQuarticRoot.cpp',
+    '../src/pathops/SkReduceOrder.cpp',
+    '../src/pathops/SkAddIntersections.h',
+    '../src/pathops/SkDQuadImplicit.h',
+    '../src/pathops/SkIntersectionHelper.h',
+    '../src/pathops/SkIntersections.h',
+    '../src/pathops/SkLineParameters.h',
+    '../src/pathops/SkOpAngle.h',
+    '../src/pathops/SkOpContour.h',
+    '../src/pathops/SkOpEdgeBuilder.h',
+    '../src/pathops/SkOpSegment.h',
+    '../src/pathops/SkOpSpan.h',
+    '../src/pathops/SkPathOpsBounds.h',
+    '../src/pathops/SkPathOpsCommon.h',
+    '../src/pathops/SkPathOpsCubic.h',
+    '../src/pathops/SkPathOpsCurve.h',
+    '../src/pathops/SkPathOpsDebug.h',
+    '../src/pathops/SkPathOpsLine.h',
+    '../src/pathops/SkPathOpsPoint.h',
+    '../src/pathops/SkPathOpsQuad.h',
+    '../src/pathops/SkPathOpsRect.h',
+    '../src/pathops/SkPathOpsTriangle.h',
+    '../src/pathops/SkPathOpsTypes.h',
+    '../src/pathops/SkPathWriter.h',
+    '../src/pathops/SkQuarticRoot.h',
+    '../src/pathops/SkReduceOrder.h',
+  ],
+}
index b0da432c6708a3512bbd8fb26035f780c10b511a..c8732545f27398ec7352cfcca70229ba38554c29 100644 (file)
@@ -20,7 +20,6 @@
         '../tests/PathOpsCubicLineIntersectionIdeas.cpp',
         '../tests/PathOpsDebug.cpp',
         '../tests/PathOpsOpLoopThreadedTest.cpp',
-        '../tests/PathOpsTSectDebug.h',
         '../tests/skia_test.cpp',
       ],
       'conditions': [
index e3a6174a5a5a2dd398ea5a9782b1f13a995f3f1a..5117b63caacf7b78b53a9a6cbd2e352559975e40 100644 (file)
 
     '../tests/PathOpsAngleTest.cpp',
     '../tests/PathOpsBoundsTest.cpp',
-    '../tests/PathOpsBuilderTest.cpp',
-    '../tests/PathOpsBuildUseTest.cpp',
     '../tests/PathOpsCubicIntersectionTest.cpp',
     '../tests/PathOpsCubicIntersectionTestData.cpp',
     '../tests/PathOpsCubicLineIntersectionTest.cpp',
     '../tests/PathOpsCubicQuadIntersectionTest.cpp',
     '../tests/PathOpsCubicReduceOrderTest.cpp',
+    '../tests/PathOpsCubicToQuadsTest.cpp',
     '../tests/PathOpsDCubicTest.cpp',
     '../tests/PathOpsDLineTest.cpp',
     '../tests/PathOpsDPointTest.cpp',
+    '../tests/PathOpsDQuadTest.cpp',
     '../tests/PathOpsDRectTest.cpp',
+    '../tests/PathOpsDTriangleTest.cpp',
     '../tests/PathOpsDVectorTest.cpp',
     '../tests/PathOpsExtendedTest.cpp',
     '../tests/PathOpsFuzz763Test.cpp',
@@ -43,6 +44,7 @@
     '../tests/PathOpsQuadIntersectionTestData.cpp',
     '../tests/PathOpsQuadLineIntersectionTest.cpp',
     '../tests/PathOpsQuadLineIntersectionThreadedTest.cpp',
+    '../tests/PathOpsQuadParameterizationTest.cpp',
     '../tests/PathOpsQuadReduceOrderTest.cpp',
     '../tests/PathOpsSimplifyDegenerateThreadedTest.cpp',
     '../tests/PathOpsSimplifyFailTest.cpp',
     '../tests/PathOpsSkpTest.cpp',
     '../tests/PathOpsTestCommon.cpp',
     '../tests/PathOpsThreadedCommon.cpp',
-    '../tests/PathOpsThreeWayTest.cpp',
-    '../tests/PathOpsTightBoundsTest.cpp', 
-    '../tests/PathOpsTypesTest.cpp', 
-
+    '../tests/PathOpsTightBoundsTest.cpp',
     '../tests/PathOpsCubicIntersectionTestData.h',
     '../tests/PathOpsExtendedTest.h',
     '../tests/PathOpsQuadIntersectionTestData.h',
     '../tests/PathOpsTestCommon.h',
     '../tests/PathOpsThreadedCommon.h',
-    '../tests/PathOpsTSectDebug.h',
   ],
 }
index 3ec935160632116c99ad5a268342ff6f05886ece..ba18f4ba72f50738e4d7f09618db2db3e6c5ba80 100644 (file)
@@ -8,8 +8,6 @@
 #define SkPathOps_DEFINED
 
 #include "SkPreConfig.h"
-#include "SkTArray.h"
-#include "SkTDArray.h"
 
 class SkPath;
 struct SkRect;
@@ -37,10 +35,9 @@ enum SkPathOp {
 
     @param one The first operand (for difference, the minuend)
     @param two The second operand (for difference, the subtrahend)
-    @param op The operator to apply.
     @param result The product of the operands. The result may be one of the
                   inputs.
-    @return True if the operation succeeded.
+    @return True if operation succeeded.
   */
 bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result);
 
@@ -66,31 +63,4 @@ bool SK_API Simplify(const SkPath& path, SkPath* result);
   */
 bool SK_API TightBounds(const SkPath& path, SkRect* result);
 
-/** Perform a series of path operations, optimized for unioning many paths together.
-  */
-class SK_API SkOpBuilder {
-public:
-    /** Add one or more paths and their operand. The builder is empty before the first
-        path is added, so the result of a single add is (emptyPath OP path).
-
-        @param path The second operand.
-        @param op The operator to apply to the existing and supplied paths.
-     */ 
-    void add(const SkPath& path, SkPathOp op);
-
-    /** Computes the sum of all paths and operands, and resets the builder to its
-        initial state.
-        @param result The product of the operands.
-        @return True if the operation succeeded.
-      */
-    bool resolve(SkPath* result);
-
-private:
-    SkTArray<SkPath> fPathRefs;
-    SkTDArray<SkPathOp> fOps;
-
-    void reset();
-};
-
 #endif
index b507eb7560989665109fd358e7b5fc9b359c4f21..c27434f9f778cb1a4df19570b6ecfdbeb8d90e38 100644 (file)
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
 #include "SkPathOpsBounds.h"
 
 #if DEBUG_ADD_INTERSECTING_TS
@@ -131,6 +130,20 @@ static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
     SkDebugf("\n");
 }
 
+static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
+        const SkIntersections& i) {
+    SkASSERT(i.used() == pts);
+    if (!pts) {
+        SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__,
+                CUBIC_DEBUG_DATA(wt.pts()));
+        return;
+    }
+    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+            i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0));
+    SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]);
+    SkDebugf("\n");
+}
+
 #else
 static void debugShowLineIntersection(int , const SkIntersectionHelper& ,
         const SkIntersectionHelper& , const SkIntersections& ) {
@@ -155,10 +168,13 @@ static void debugShowCubicQuadIntersection(int , const SkIntersectionHelper& ,
 static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
         const SkIntersectionHelper& , const SkIntersections& ) {
 }
+
+static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
+        const SkIntersections& ) {
+}
 #endif
 
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
-        SkChunkAlloc* allocator) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
     if (test != next) {
         if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
             return false;
@@ -170,11 +186,10 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
     }
     SkIntersectionHelper wt;
     wt.init(test);
+    bool foundCommonContour = test == next;
     do {
         SkIntersectionHelper wn;
         wn.init(next);
-        test->debugValidate();
-        next->debugValidate();
         if (test == next && !wn.startAfter(wt)) {
             continue;
         }
@@ -291,22 +306,14 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
                             break;
                         }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            SkDQuad quad1;
-                            quad1.set(wt.pts());
-                            SkDQuad quad2;
-                            quad2.set(wn.pts());
-                            pts = ts.intersect(quad1, quad2);
+                            pts = ts.quadQuad(wt.pts(), wn.pts());
+                            ts.alignQuadPts(wt.pts(), wn.pts());
                             debugShowQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
                         case SkIntersectionHelper::kCubic_Segment: {
                             swap = true;
-                            SkDQuad quad1;
-                            quad1.set(wt.pts());
-                            SkDCubic cubic1 = quad1.toCubic();
-                            SkDCubic cubic2;
-                            cubic2.set(wn.pts());
-                            pts = ts.intersect(cubic2, cubic1);
+                            pts = ts.cubicQuad(wn.pts(), wt.pts());
                             debugShowCubicQuadIntersection(pts, wn, wt, ts);
                             break;
                         }
@@ -332,21 +339,12 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
                             break;
                         }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            SkDCubic cubic1;
-                            cubic1.set(wt.pts());
-                            SkDQuad quad2;
-                            quad2.set(wn.pts());
-                            SkDCubic cubic2 = quad2.toCubic();
-                            pts = ts.intersect(cubic1, cubic2);
+                            pts = ts.cubicQuad(wt.pts(), wn.pts());
                             debugShowCubicQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
                         case SkIntersectionHelper::kCubic_Segment: {
-                            SkDCubic cubic1;
-                            cubic1.set(wt.pts());
-                            SkDCubic cubic2;
-                            cubic2.set(wn.pts());
-                            pts = ts.intersect(cubic1, cubic2);
+                            pts = ts.cubicCubic(wt.pts(), wn.pts());
                             debugShowCubicIntersection(pts, wt, wn, ts);
                             break;
                         }
@@ -357,53 +355,102 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
                 default:
                     SkASSERT(0);
             }
-            int coinIndex = -1;
-            SkOpPtT* coinPtT[2];
+            if (!foundCommonContour && pts > 0) {
+                test->addCross(next);
+                next->addCross(test);
+                foundCommonContour = true;
+            }
+            // in addition to recording T values, record matching segment
+            if (pts == 2) {
+                if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment
+                        && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) {
+                    if (wt.addCoincident(wn, ts, swap)) {
+                        continue;
+                    }
+                    pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
+                        && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
+                        && ts.isCoincident(0)) {
+                    SkASSERT(ts.coincidentUsed() == 2);
+                    if (wt.addCoincident(wn, ts, swap)) {
+                        continue;
+                    }
+                    pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                }
+            }
+            if (pts >= 2) {
+                for (int pt = 0; pt < pts - 1; ++pt) {
+                    const SkDPoint& point = ts.pt(pt);
+                    const SkDPoint& next = ts.pt(pt + 1);
+                    if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next)
+                            && wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) {
+                        if (!wt.addPartialCoincident(wn, ts, pt, swap)) {
+                            // remove extra point if two map to same float values
+                            pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                        }
+                    }
+                }
+            }
             for (int pt = 0; pt < pts; ++pt) {
                 SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
                 SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
-                wt.segment()->debugValidate();
-                SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
-                        allocator);
-                wn.segment()->debugValidate();
-                SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
-                        allocator);
-                testTAt->addOpp(nextTAt);
-                if (testTAt->fPt != nextTAt->fPt) {
-                    testTAt->span()->unaligned();
-                    nextTAt->span()->unaligned();
-                }
-                wt.segment()->debugValidate();
-                wn.segment()->debugValidate();
-                if (!ts.isCoincident(pt)) {
-                    continue;
-                }
-                if (coinIndex < 0) {
-                    coinPtT[0] = testTAt;
-                    coinPtT[1] = nextTAt;
-                    coinIndex = pt;
-                    continue;
-                }
-                if (coinPtT[0]->span() == testTAt->span()) {
-                    coinIndex = -1;
-                    continue;
-                }
-                if (coinPtT[1]->span() == nextTAt->span()) {
-                    coinIndex = -1;  // coincidence span collapsed
-                    continue;
-                }
-                if (swap) {
-                    SkTSwap(coinPtT[0], coinPtT[1]);
-                    SkTSwap(testTAt, nextTAt);
-                }
-                SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
-                coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
-                wt.segment()->debugValidate();
-                wn.segment()->debugValidate();
-                coinIndex = -1;
+                SkPoint point = ts.pt(pt).asSkPoint();
+                wt.alignTPt(wn, swap, pt, &ts, &point);
+                int testTAt = wt.addT(wn, point, ts[swap][pt]);
+                int nextTAt = wn.addT(wt, point, ts[!swap][pt]);
+                wt.addOtherT(testTAt, ts[!swap][pt], nextTAt);
+                wn.addOtherT(nextTAt, ts[swap][pt], testTAt);
             }
-            SkASSERT(coinIndex < 0);  // expect coincidence to be paired
         } while (wn.advance());
     } while (wt.advance());
     return true;
 }
+
+void AddSelfIntersectTs(SkOpContour* test) {
+    SkIntersectionHelper wt;
+    wt.init(test);
+    do {
+        if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) {
+            continue;
+        }
+        SkIntersections ts;
+        int pts = ts.cubic(wt.pts());
+        debugShowCubicIntersection(pts, wt, ts);
+        if (!pts) {
+            continue;
+        }
+        SkASSERT(pts == 1);
+        SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1);
+        SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1);
+        SkPoint point = ts.pt(0).asSkPoint();
+        int testTAt = wt.addSelfT(point, ts[0][0]);
+        int nextTAt = wt.addSelfT(point, ts[1][0]);
+        wt.addOtherT(testTAt, ts[1][0], nextTAt);
+        wt.addOtherT(nextTAt, ts[0][0], testTAt);
+    } while (wt.advance());
+}
+
+// resolve any coincident pairs found while intersecting, and
+// see if coincidence is formed by clipping non-concident segments
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
+    int contourCount = (*contourList).count();
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        SkOpContour* contour = (*contourList)[cIndex];
+        contour->resolveNearCoincidence();
+    }
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        SkOpContour* contour = (*contourList)[cIndex];
+        contour->addCoincidentPoints();
+    }
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        SkOpContour* contour = (*contourList)[cIndex];
+        if (!contour->calcCoincidentWinding()) {
+            return false;
+        }
+    }
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        SkOpContour* contour = (*contourList)[cIndex];
+        contour->calcPartialCoincidentWinding();
+    }
+    return true;
+}
index 23654e5e91d20470128b1e6f2a4a04b13c6bb396..4c1947b635ab54fdce9916b89423b185190692bf 100644 (file)
@@ -9,10 +9,10 @@
 
 #include "SkIntersectionHelper.h"
 #include "SkIntersections.h"
+#include "SkTArray.h"
 
-class SkOpCoincidence;
-
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
-        SkChunkAlloc* allocator);
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
+void AddSelfIntersectTs(SkOpContour* test);
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
 
 #endif
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
new file mode 100644 (file)
index 0000000..2fb35e1
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkIntersections.h"
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsLine.h"
+#include "SkPathOpsPoint.h"
+#include "SkPathOpsQuad.h"
+#include "SkPathOpsRect.h"
+#include "SkReduceOrder.h"
+#include "SkTSort.h"
+
+#if ONE_OFF_DEBUG
+static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
+static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
+#endif
+
+#define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
+#define DEBUG_QUAD_PART_SHOW_SIMPLE DEBUG_QUAD_PART && 0
+#define SWAP_TOP_DEBUG 0
+
+static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
+
+static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) {
+    SkDCubic part = cubic.subDivide(tStart, tEnd);
+    SkDQuad quad = part.toQuad();
+    // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
+    // extremely shallow quadratic?
+    int order = reducer->reduce(quad);
+#if DEBUG_QUAD_PART
+    SkDebugf("%s cubic=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+            " t=(%1.9g,%1.9g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY,
+            cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
+            cubic[3].fX, cubic[3].fY, tStart, tEnd);
+    SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n"
+             "  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
+            part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY,
+            part[3].fX, part[3].fY, quad[0].fX, quad[0].fY,
+            quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+#if DEBUG_QUAD_PART_SHOW_SIMPLE
+    SkDebugf("%s simple=(%1.9g,%1.9g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY);
+    if (order > 1) {
+        SkDebugf(" %1.9g,%1.9g", reducer->fQuad[1].fX, reducer->fQuad[1].fY);
+    }
+    if (order > 2) {
+        SkDebugf(" %1.9g,%1.9g", reducer->fQuad[2].fX, reducer->fQuad[2].fY);
+    }
+    SkDebugf(")\n");
+    SkASSERT(order < 4 && order > 0);
+#endif
+#endif
+    return order;
+}
+
+static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
+        int order2, SkIntersections& i) {
+    if (order1 == 3 && order2 == 3) {
+        i.intersect(simple1, simple2);
+    } else if (order1 <= 2 && order2 <= 2) {
+        i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
+    } else if (order1 == 3 && order2 <= 2) {
+        i.intersect(simple1, (const SkDLine&) simple2);
+    } else {
+        SkASSERT(order1 <= 2 && order2 == 3);
+        i.intersect(simple2, (const SkDLine&) simple1);
+        i.swapPts();
+    }
+}
+
+// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
+// chase intersections near quadratic ends, requiring odd hacks to find them.
+static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2,
+        double t2s, double t2e, double precisionScale, SkIntersections& i) {
+    i.upDepth();
+    SkDCubic c1 = cubic1.subDivide(t1s, t1e);
+    SkDCubic c2 = cubic2.subDivide(t2s, t2e);
+    SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1;
+    // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
+    c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1);
+    SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2;
+    c2.toQuadraticTs(c2.calcPrecision() * 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;
+        SkReduceOrder 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;
+            if (&cubic1 == &cubic2 && t1Start >= t2Start) {
+                t2Start = t2;
+                continue;
+            }
+            SkReduceOrder s2;
+            int o2 = quadPart(cubic2, t2Start, t2, &s2);
+        #if ONE_OFF_DEBUG
+            char tab[] = "                  ";
+            if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
+                    && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
+                SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab,
+                        __FUNCTION__, t1Start, t1, t2Start, t2);
+                SkIntersections xlocals;
+                xlocals.allowNear(false);
+                xlocals.allowFlatMeasure(true);
+                intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals);
+                SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
+            }
+        #endif
+            SkIntersections locals;
+            locals.allowNear(false);
+            locals.allowFlatMeasure(true);
+            intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals);
+            int tCount = locals.used();
+            for (int tIdx = 0; tIdx < tCount; ++tIdx) {
+                double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx];
+                double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx];
+    // if the computed t is not sufficiently precise, iterate
+                SkDPoint p1 = cubic1.ptAtT(to1);
+                SkDPoint p2 = cubic2.ptAtT(to2);
+                if (p1.approximatelyEqual(p2)) {
+    // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
+//                    SkASSERT(!locals.isCoincident(tIdx));
+                    if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
+                        if (i.swapped()) {  //  FIXME: insert should respect swap
+                            i.insert(to2, to1, p1);
+                        } else {
+                            i.insert(to1, to2, p1);
+                        }
+                    }
+                } else {
+/*for random cubics, 16 below catches 99.997% of the intersections. To test for the remaining 0.003%
+  look for nearly coincident curves. and check each 1/16th section.
+*/
+                    double offset = precisionScale / 16;  // FIXME: const is arbitrary: test, refine
+                    double c1Bottom = tIdx == 0 ? 0 :
+                            (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2;
+                    double c1Min = SkTMax(c1Bottom, to1 - offset);
+                    double c1Top = tIdx == tCount - 1 ? 1 :
+                            (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2;
+                    double c1Max = SkTMin(c1Top, to1 + offset);
+                    double c2Min = SkTMax(0., to2 - offset);
+                    double c2Max = SkTMin(1., to2 + offset);
+                #if ONE_OFF_DEBUG
+                    SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+                            __FUNCTION__,
+                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+                    SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+                            " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+                            i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
+                            to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+                    SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+                            " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+                            c1Max, c2Min, c2Max);
+                #endif
+                    intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+                #if ONE_OFF_DEBUG
+                    SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+                #endif
+                    if (tCount > 1) {
+                        c1Min = SkTMax(0., to1 - offset);
+                        c1Max = SkTMin(1., to1 + offset);
+                        double c2Bottom = tIdx == 0 ? to2 :
+                                (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2;
+                        double c2Top = tIdx == tCount - 1 ? to2 :
+                                (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2;
+                        if (c2Bottom > c2Top) {
+                            SkTSwap(c2Bottom, c2Top);
+                        }
+                        if (c2Bottom == to2) {
+                            c2Bottom = 0;
+                        }
+                        if (c2Top == to2) {
+                            c2Top = 1;
+                        }
+                        c2Min = SkTMax(c2Bottom, to2 - offset);
+                        c2Max = SkTMin(c2Top, to2 + offset);
+                    #if ONE_OFF_DEBUG
+                        SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+                            __FUNCTION__,
+                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+                        SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+                                " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+                                i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
+                                to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+                        SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+                                " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+                                c1Max, c2Min, c2Max);
+                    #endif
+                        intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+                #if ONE_OFF_DEBUG
+                    SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+                #endif
+                        c1Min = SkTMax(c1Bottom, to1 - offset);
+                        c1Max = SkTMin(c1Top, to1 + offset);
+                    #if ONE_OFF_DEBUG
+                        SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+                        __FUNCTION__,
+                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+                        SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+                                " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+                                i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
+                                to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+                        SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+                                " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+                                c1Max, c2Min, c2Max);
+                    #endif
+                        intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+                #if ONE_OFF_DEBUG
+                    SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+                #endif
+                    }
+          //          intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+                    // FIXME: 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.
+                }
+            }
+            t2Start = t2;
+        }
+        t1Start = t1;
+    }
+    i.downDepth();
+}
+
+    // if two ends intersect, check middle for coincidence
+bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
+    if (fUsed < 2) {
+        return false;
+    }
+    int last = fUsed - 1;
+    double tRange1 = fT[0][last] - fT[0][0];
+    double tRange2 = fT[1][last] - fT[1][0];
+    for (int index = 1; index < 5; ++index) {
+        double testT1 = fT[0][0] + tRange1 * index / 5;
+        double testT2 = fT[1][0] + tRange2 * index / 5;
+        SkDPoint testPt1 = c1.ptAtT(testT1);
+        SkDPoint testPt2 = c2.ptAtT(testT2);
+        if (!testPt1.approximatelyEqual(testPt2)) {
+            return false;
+        }
+    }
+    if (fUsed > 2) {
+        fPt[1] = fPt[last];
+        fT[0][1] = fT[0][last];
+        fT[1][1] = fT[1][last];
+        fUsed = 2;
+    }
+    fIsCoincident[0] = fIsCoincident[1] = 0x03;
+    return true;
+}
+
+#define LINE_FRACTION 0.1
+
+// intersect the end of the cubic with the other. Try lines from the end to control and opposite
+// end to determine range of t on opposite cubic.
+bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
+    int t1Index = start ? 0 : 3;
+    double testT = (double) !start;
+    bool swap = swapped();
+    // quad/quad at this point checks to see if exact matches have already been found
+    // cubic/cubic can't reject so easily since cubics can intersect same point more than once
+    SkDLine tmpLine;
+    tmpLine[0] = tmpLine[1] = cubic2[t1Index];
+    tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
+    tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
+    SkIntersections impTs;
+    impTs.allowNear(false);
+    impTs.allowFlatMeasure(true);
+    impTs.intersectRay(cubic1, tmpLine);
+    for (int index = 0; index < impTs.used(); ++index) {
+        SkDPoint realPt = impTs.pt(index);
+        if (!tmpLine[0].approximatelyEqual(realPt)) {
+            continue;
+        }
+        if (swap) {
+            cubicInsert(testT, impTs[0][index], tmpLine[0], cubic2, cubic1);
+        } else {
+            cubicInsert(impTs[0][index], testT, tmpLine[0], cubic1, cubic2);
+        }
+        return true;
+    }
+    return false;
+}
+
+
+void SkIntersections::cubicInsert(double one, double two, const SkDPoint& pt,
+        const SkDCubic& cubic1, const SkDCubic& cubic2) {
+    for (int index = 0; index < fUsed; ++index) {
+        if (fT[0][index] == one) {
+            double oldTwo = fT[1][index];
+            if (oldTwo == two) {
+                return;
+            }
+            SkDPoint mid = cubic2.ptAtT((oldTwo + two) / 2);
+            if (mid.approximatelyEqual(fPt[index])) {
+                return;
+            }
+        }
+        if (fT[1][index] == two) {
+            SkDPoint mid = cubic1.ptAtT((fT[0][index] + two) / 2);
+            if (mid.approximatelyEqual(fPt[index])) {
+                return;
+            }
+        }
+    }
+    insert(one, two, pt);
+}
+
+void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
+                         const SkDRect& bounds2) {
+    SkDLine line;
+    int t1Index = start ? 0 : 3;
+    double testT = (double) !start;
+   // don't bother if the two cubics are connnected
+    static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
+    static const int kMaxLineCubicIntersections = 3;
+    SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
+    line[0] = cubic1[t1Index];
+    // this variant looks for intersections with the end point and lines parallel to other points
+    for (int index = 0; index < kPointsInCubic; ++index) {
+        if (index == t1Index) {
+            continue;
+        }
+        SkDVector dxy1 = cubic1[index] - line[0];
+        dxy1 /= SkDCubic::gPrecisionUnit;
+        line[1] = line[0] + dxy1;
+        SkDRect lineBounds;
+        lineBounds.setBounds(line);
+        if (!bounds2.intersects(&lineBounds)) {
+            continue;
+        }
+        SkIntersections local;
+        if (!local.intersect(cubic2, line)) {
+            continue;
+        }
+        for (int idx2 = 0; idx2 < local.used(); ++idx2) {
+            double foundT = local[0][idx2];
+            if (approximately_less_than_zero(foundT)
+                    || approximately_greater_than_one(foundT)) {
+                continue;
+            }
+            if (local.pt(idx2).approximatelyEqual(line[0])) {
+                if (swapped()) {  // FIXME: insert should respect swap
+                    insert(foundT, testT, line[0]);
+                } else {
+                    insert(testT, foundT, line[0]);
+                }
+            } else {
+                tVals.push_back(foundT);
+            }
+        }
+    }
+    if (tVals.count() == 0) {
+        return;
+    }
+    SkTQSort<double>(tVals.begin(), tVals.end() - 1);
+    double tMin1 = start ? 0 : 1 - LINE_FRACTION;
+    double tMax1 = start ? LINE_FRACTION : 1;
+    int tIdx = 0;
+    do {
+        int tLast = tIdx;
+        while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
+            ++tLast;
+        }
+        double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
+        double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
+        int lastUsed = used();
+        if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
+            ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
+        }
+        if (lastUsed == used()) {
+            tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
+            tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
+            if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
+                ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
+            }
+        }
+        tIdx = tLast + 1;
+    } while (tIdx < tVals.count());
+    return;
+}
+
+const double CLOSE_ENOUGH = 0.001;
+
+static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
+    if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) {
+        return false;
+    }
+    pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2);
+    return true;
+}
+
+static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
+    int last = i.used() - 1;
+    if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
+        return false;
+    }
+    pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2);
+    return true;
+}
+
+static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+    for (int oddMan = 0; oddMan < 4; ++oddMan) {
+        const SkDPoint* endPt[3];
+        for (int opp = 1; opp < 4; ++opp) {
+            int end = oddMan ^ opp;  // choose a value not equal to oddMan
+            endPt[opp - 1] = &c1[end];
+        }
+        for (int triTest = 0; triTest < 3; ++triTest) {
+            double origX = endPt[triTest]->fX;
+            double origY = endPt[triTest]->fY;
+            int oppTest = triTest + 1;
+            if (3 == oppTest) {
+                oppTest = 0;
+            }
+            double adj = endPt[oppTest]->fX - origX;
+            double opp = endPt[oppTest]->fY - origY;
+            if (adj == 0 && opp == 0) {  // if the other point equals the test point, ignore it
+                continue;
+            }
+            double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp;
+            if (approximately_zero(sign)) {
+                goto tryNextHalfPlane;
+            }
+            for (int n = 0; n < 4; ++n) {
+                double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
+                if (test * sign > 0 && !precisely_zero(test)) {
+                    goto tryNextHalfPlane;
+                }
+            }
+        }
+        return true;
+tryNextHalfPlane:
+        ;
+    }
+    return false;
+}
+
+int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
+    if (fMax == 0) {
+        fMax = 9;
+    }
+    bool selfIntersect = &c1 == &c2;
+    if (selfIntersect) {
+        if (c1[0].approximatelyEqual(c1[3])) {
+            insert(0, 1, c1[0]);
+            return fUsed;
+        }
+    } else {
+        // OPTIMIZATION: set exact end bits here to avoid cubic exact end later
+        for (int i1 = 0; i1 < 4; i1 += 3) {
+            for (int i2 = 0; i2 < 4; i2 += 3) {
+                if (c1[i1].approximatelyEqual(c2[i2])) {
+                    insert(i1 >> 1, i2 >> 1, c1[i1]);
+                }
+            }
+        }
+    }
+    SkASSERT(fUsed < 4);
+    if (!selfIntersect) {
+        if (only_end_pts_in_common(c1, c2)) {
+            return fUsed;
+        }
+        if (only_end_pts_in_common(c2, c1)) {
+            return fUsed;
+        }
+    }
+    // quad/quad does linear test here -- cubic does not
+    // cubics which are really lines should have been detected in reduce step earlier
+    int exactEndBits = 0;
+    if (selfIntersect) {
+        if (fUsed) {
+            return fUsed;
+        }
+    } else {
+        exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
+        exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
+        swap();
+        exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
+        exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
+        swap();
+    }
+    if (cubicCheckCoincidence(c1, c2)) {
+        SkASSERT(!selfIntersect);
+        return fUsed;
+    }
+    // FIXME: pass in cached bounds from caller
+    SkDRect c2Bounds;
+    c2Bounds.setBounds(c2);
+    if (!(exactEndBits & 4)) {
+        cubicNearEnd(c1, false, c2, c2Bounds);
+    }
+    if (!(exactEndBits & 8)) {
+        if (selfIntersect && fUsed) {
+            return fUsed;
+        }
+        cubicNearEnd(c1, true, c2, c2Bounds);
+        if (selfIntersect && fUsed && ((approximately_less_than_zero(fT[0][0])
+                    && approximately_less_than_zero(fT[1][0]))
+                    || (approximately_greater_than_one(fT[0][0])
+                    && approximately_greater_than_one(fT[1][0])))) {
+            SkASSERT(fUsed == 1);
+            fUsed = 0;
+            return fUsed;
+        }
+    }
+    if (!selfIntersect) {
+        SkDRect c1Bounds;
+        c1Bounds.setBounds(c1);  // OPTIMIZE use setRawBounds ?
+        swap();
+        if (!(exactEndBits & 1)) {
+            cubicNearEnd(c2, false, c1, c1Bounds);
+        }
+        if (!(exactEndBits & 2)) {
+            cubicNearEnd(c2, true, c1, c1Bounds);
+        }
+        swap();
+    }
+    if (cubicCheckCoincidence(c1, c2)) {
+        SkASSERT(!selfIntersect);
+        return fUsed;
+    }
+    SkIntersections i;
+    i.fAllowNear = false;
+    i.fFlatMeasure = true;
+    i.fMax = 9;
+    ::intersect(c1, 0, 1, c2, 0, 1, 1, i);
+    int compCount = i.used();
+    if (compCount) {
+        int exactCount = used();
+        if (exactCount == 0) {
+            *this = i;
+        } else {
+            // at least one is exact or near, and at least one was computed. Eliminate duplicates
+            for (int exIdx = 0; exIdx < exactCount; ++exIdx) {
+                for (int cpIdx = 0; cpIdx < compCount; ) {
+                    if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) {
+                        i.removeOne(cpIdx);
+                        --compCount;
+                        continue;
+                    }
+                    double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2;
+                    SkDPoint pt = c1.ptAtT(tAvg);
+                    if (!pt.approximatelyEqual(fPt[exIdx])) {
+                        ++cpIdx;
+                        continue;
+                    }
+                    tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2;
+                    pt = c2.ptAtT(tAvg);
+                    if (!pt.approximatelyEqual(fPt[exIdx])) {
+                        ++cpIdx;
+                        continue;
+                    }
+                    i.removeOne(cpIdx);
+                    --compCount;
+                }
+            }
+            // if mid t evaluates to nearly the same point, skip the t
+            for (int cpIdx = 0; cpIdx < compCount - 1; ) {
+                double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2;
+                SkDPoint pt = c1.ptAtT(tAvg);
+                if (!pt.approximatelyEqual(fPt[cpIdx])) {
+                    ++cpIdx;
+                    continue;
+                }
+                tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2;
+                pt = c2.ptAtT(tAvg);
+                if (!pt.approximatelyEqual(fPt[cpIdx])) {
+                    ++cpIdx;
+                    continue;
+                }
+                i.removeOne(cpIdx);
+                --compCount;
+            }
+            // in addition to adding below missing function, think about how to say
+            append(i);
+        }
+    }
+    // If an end point and a second point very close to the end is returned, the second
+    // point may have been detected because the approximate quads
+    // intersected at the end and close to it. Verify that the second point is valid.
+    if (fUsed <= 1) {
+        return fUsed;
+    }
+    SkDPoint pt[2];
+    if (closeStart(c1, 0, *this, pt[0]) && closeStart(c2, 1, *this, pt[1])
+            && pt[0].approximatelyEqual(pt[1])) {
+        removeOne(1);
+    }
+    if (closeEnd(c1, 0, *this, pt[0]) && closeEnd(c2, 1, *this, pt[1])
+            && pt[0].approximatelyEqual(pt[1])) {
+        removeOne(used() - 2);
+    }
+    // vet the pairs of t values to see if the mid value is also on the curve. If so, mark
+    // the span as coincident
+    if (fUsed >= 2 && !coincidentUsed()) {
+        int last = fUsed - 1;
+        int match = 0;
+        for (int index = 0; index < last; ++index) {
+            double mid1 = (fT[0][index] + fT[0][index + 1]) / 2;
+            double mid2 = (fT[1][index] + fT[1][index + 1]) / 2;
+            pt[0] = c1.ptAtT(mid1);
+            pt[1] = c2.ptAtT(mid2);
+            if (pt[0].approximatelyEqual(pt[1])) {
+                match |= 1 << index;
+            }
+        }
+        if (match) {
+#if DEBUG_CONCIDENT
+            if (((match + 1) & match) != 0) {
+                SkDebugf("%s coincident hole\n", __FUNCTION__);
+            }
+#endif
+            // for now, assume that everything from start to finish is coincident
+            if (fUsed > 2) {
+                  fPt[1] = fPt[last];
+                  fT[0][1] = fT[0][last];
+                  fT[1][1] = fT[1][last];
+                  fIsCoincident[0] = 0x03;
+                  fIsCoincident[1] = 0x03;
+                  fUsed = 2;
+            }
+        }
+    }
+    return fUsed;
+}
+
+// Up promote the quad to a cubic.
+// OPTIMIZATION If this is a common use case, optimize by duplicating
+// the intersect 3 loop to avoid the promotion  / demotion code
+int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
+    fMax = 7;
+    SkDCubic up = quad.toCubic();
+    (void) intersect(cubic, up);
+    return used();
+}
+
+/* http://www.ag.jku.at/compass/compasssample.pdf
+( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
+Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
+SINTEF Applied Mathematics http://www.sintef.no )
+describes a method to find the self intersection of a cubic by taking the gradient of the implicit
+form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
+
+int SkIntersections::intersect(const SkDCubic& c) {
+    fMax = 1;
+    // check to see if x or y end points are the extrema. Are other quick rejects possible?
+    if (c.endsAreExtremaInXOrY()) {
+        return false;
+    }
+    // OPTIMIZATION: could quick reject if neither end point tangent ray intersected the line
+    // segment formed by the opposite end point to the control point
+    (void) intersect(c, c);
+    if (used() > 1) {
+        fUsed = 0;
+    } else if (used() > 0) {
+        if (approximately_equal_double(fT[0][0], fT[1][0])) {
+            fUsed = 0;
+        } else {
+            SkASSERT(used() == 1);
+            if (fT[0][0] > fT[1][0]) {
+                swapPts();
+            }
+        }
+    }
+    return used();
+}
index f5fe01503b56a17d29839a824ac322c8b79e1c0a..696c42e835a3521340961b1a6ad22c3d64b79c23 100644 (file)
@@ -93,29 +93,6 @@ public:
         fAllowNear = allow;
     }
 
-    void checkCoincident() {
-        int last = fIntersections->used() - 1;
-        for (int index = 0; index < last; ) {
-            double cubicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
-            SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
-            double t = fLine.nearPoint(cubicMidPt, NULL);
-            if (t < 0) {
-                ++index;
-                continue;
-            }
-            if (fIntersections->isCoincident(index)) {
-                fIntersections->removeOne(index);
-                --last;
-            } else if (fIntersections->isCoincident(index + 1)) {
-                fIntersections->removeOne(index + 1);
-                --last;
-            } else {
-                fIntersections->setCoincident(index++);
-            }
-            fIntersections->setCoincident(index);
-        }
-    }
-
     // see parallel routine in line quadratic intersections
     int intersectRay(double roots[3]) {
         double adj = fLine[1].fX - fLine[0].fX;
@@ -154,11 +131,32 @@ public:
             double cubicT = rootVals[index];
             double lineT = findLineT(cubicT);
             SkDPoint pt;
-            if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized)) {
+    #if ONE_OFF_DEBUG
+                SkDPoint cPt = fCubic.ptAtT(cubicT);
+                SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
+                        cPt.fX, cPt.fY);
+    #endif
+                for (int inner = 0; inner < fIntersections->used(); ++inner) {
+                    if (fIntersections->pt(inner) != pt) {
+                        continue;
+                    }
+                    double existingCubicT = (*fIntersections)[0][inner];
+                    if (cubicT == existingCubicT) {
+                        goto skipInsert;
+                    }
+                    // check if midway on cubic is also same point. If so, discard this
+                    double cubicMidT = (existingCubicT + cubicT) / 2;
+                    SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
+                    if (cubicMidPt.approximatelyEqual(pt)) {
+                        goto skipInsert;
+                    }
+                }
                 fIntersections->insert(cubicT, lineT, pt);
+        skipInsert:
+                ;
             }
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
@@ -188,43 +186,20 @@ public:
         int count = HorizontalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt = { fCubic.ptAtT(cubicT).fX,  axisIntercept };
+            SkDPoint pt;
+            pt.fX = fCubic.ptAtT(cubicT).fX;
+            pt.fY = axisIntercept;
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
                 fIntersections->insert(cubicT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
-        bool uniqueAnswer(double cubicT, const SkDPoint& pt) {
-            for (int inner = 0; inner < fIntersections->used(); ++inner) {
-                if (fIntersections->pt(inner) != pt) {
-                    continue;
-                }
-                double existingCubicT = (*fIntersections)[0][inner];
-                if (cubicT == existingCubicT) {
-                    return false;
-                }
-                // check if midway on cubic is also same point. If so, discard this
-                double cubicMidT = (existingCubicT + cubicT) / 2;
-                SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
-                if (cubicMidPt.approximatelyEqual(pt)) {
-                    return false;
-                }
-            }
-#if ONE_OFF_DEBUG
-            SkDPoint cPt = fCubic.ptAtT(cubicT);
-            SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
-                    cPt.fX, cPt.fY);
-#endif
-            return true;
-        }
-
     static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) {
         double A, B, C, D;
         SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D);
@@ -251,16 +226,17 @@ public:
         int count = VerticalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
+            SkDPoint pt;
+            pt.fX = axisIntercept;
+            pt.fY = fCubic.ptAtT(cubicT).fY;
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
                 fIntersections->insert(cubicT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
@@ -366,7 +342,7 @@ public:
         double lT = *lineT = SkPinT(*lineT);
         SkDPoint lPt = fLine.ptAtT(lT);
         SkDPoint cPt = fCubic.ptAtT(cT);
-        if (!lPt.roughlyEqual(cPt)) {
+        if (!lPt.moreRoughlyEqual(cPt)) {
             return false;
         }
         // FIXME: if points are roughly equal but not approximately equal, need to do
index 2d034b69e84e17d8b4156c3f1ee9525120b3ed70..a28564d4c2c8b3ff724540d70d0c52a3a00fee05 100644 (file)
@@ -19,10 +19,62 @@ If this is a degree-elevated cubic, then both equations will give the same answe
  it's likely not, your best bet is to average them. So,
 
 P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
+
+SkDCubic defined by: P1/2 - anchor points, C1/C2 control points
+|x| is the euclidean norm of x
+mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the
+ control point at C = (3·C2 - P2 + 3·C1 - P1)/4
+
+Algorithm
+
+pick an absolute precision (prec)
+Compute the Tdiv as the root of (cubic) equation
+sqrt(3)/18 Â· |P2 - 3·C2 + 3·C1 - P1|/2 Â· Tdiv ^ 3 = prec
+if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a
+ quadratic, with a defect less than prec, by the mid-point approximation.
+ Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
+0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point
+ approximation
+Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
+
+confirmed by (maybe stolen from)
+http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
+// maybe in turn derived from  http://www.cccg.ca/proceedings/2004/36.pdf
+// also stored at http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/bezier%20cccg04%20paper.pdf
+
 */
 
 #include "SkPathOpsCubic.h"
+#include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
+#include "SkReduceOrder.h"
+#include "SkTArray.h"
+#include "SkTSort.h"
+
+#define USE_CUBIC_END_POINTS 1
+
+static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
+    const double adjust = sqrt(3.) / 36;
+    SkDCubic sub;
+    const SkDCubic* cPtr;
+    if (start == 0) {
+        cPtr = &cubic;
+    } else {
+        // OPTIMIZE: special-case half-split ?
+        sub = cubic.subDivide(start, 1);
+        cPtr = &sub;
+    }
+    const SkDCubic& c = *cPtr;
+    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
+    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
+    double dist = sqrt(dx * dx + dy * dy);
+    double tDiv3 = precision / (adjust * dist);
+    double t = SkDCubeRoot(tDiv3);
+    if (start > 0) {
+        t = start + (1 - start) * t;
+    }
+    return t;
+}
 
 SkDQuad SkDCubic::toQuad() const {
     SkDQuad quad;
@@ -34,3 +86,101 @@ SkDQuad SkDCubic::toQuad() const {
     quad[2] = fPts[3];
     return quad;
 }
+
+static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(cubic, precision, 0);
+    if (tDiv >= 1) {
+        return true;
+    }
+    if (tDiv >= 0.5) {
+        ts->push_back(0.5);
+        return true;
+    }
+    return false;
+}
+
+static void addTs(const SkDCubic& cubic, double precision, double start, double end,
+        SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(cubic, precision, 0);
+    double parts = ceil(1.0 / tDiv);
+    for (double index = 0; index < parts; ++index) {
+        double newT = start + (index / parts) * (end - start);
+        if (newT > 0 && newT < 1) {
+            ts->push_back(newT);
+        }
+    }
+}
+
+// flavor that returns T values only, deferring computing the quads until they are needed
+// FIXME: when called from recursive intersect 2, this could take the original cubic
+// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
+// it would still take the prechopped cubic for reduce order and find cubic inflections
+void SkDCubic::toQuadraticTs(double precision, SkTArray<double, true>* ts) const {
+    SkReduceOrder reducer;
+    int order = reducer.reduce(*this, SkReduceOrder::kAllow_Quadratics);
+    if (order < 3) {
+        return;
+    }
+    double inflectT[5];
+    int inflections = findInflections(inflectT);
+    SkASSERT(inflections <= 2);
+    if (!endsAreExtremaInXOrY()) {
+        inflections += findMaxCurvature(&inflectT[inflections]);
+        SkASSERT(inflections <= 5);
+    }
+    SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
+    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
+    // own subroutine?
+    while (inflections && approximately_less_than_zero(inflectT[0])) {
+        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
+    }
+    int start = 0;
+    int next = 1;
+    while (next < inflections) {
+        if (!approximately_equal(inflectT[start], inflectT[next])) {
+            ++start;
+        ++next;
+            continue;
+        }
+        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
+    }
+
+    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
+        --inflections;
+    }
+    SkDCubicPair pair;
+    if (inflections == 1) {
+        pair = chopAt(inflectT[0]);
+        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
+        if (orderP1 < 2) {
+            --inflections;
+        } else {
+            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
+            if (orderP2 < 2) {
+                --inflections;
+            }
+        }
+    }
+    if (inflections == 0 && add_simple_ts(*this, precision, ts)) {
+        return;
+    }
+    if (inflections == 1) {
+        pair = chopAt(inflectT[0]);
+        addTs(pair.first(), precision, 0, inflectT[0], ts);
+        addTs(pair.second(), precision, inflectT[0], 1, ts);
+        return;
+    }
+    if (inflections > 1) {
+        SkDCubic part = subDivide(0, inflectT[0]);
+        addTs(part, precision, 0, inflectT[0], ts);
+        int last = inflections - 1;
+        for (int idx = 0; idx < last; ++idx) {
+            part = subDivide(inflectT[idx], inflectT[idx + 1]);
+            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
+        }
+        part = subDivide(inflectT[last], 1);
+        addTs(part, precision, inflectT[last], 1, ts);
+        return;
+    }
+    addTs(*this, precision, 0, 1, ts);
+}
index ed96b9c5d7997033d92f4e1c630c50e2814546e6..8fc673f2fb80e36558df1ae889a82863e6e05975 100644 (file)
@@ -7,6 +7,45 @@
 #include "SkIntersections.h"
 #include "SkPathOpsLine.h"
 
+/* Determine the intersection point of two lines. This assumes the lines are not parallel,
+   and that that the lines are infinite.
+   From http://en.wikipedia.org/wiki/Line-line_intersection
+ */
+SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
+    double axLen = a[1].fX - a[0].fX;
+    double ayLen = a[1].fY - a[0].fY;
+    double bxLen = b[1].fX - b[0].fX;
+    double byLen = b[1].fY - b[0].fY;
+    double denom = byLen * axLen - ayLen * bxLen;
+    SkASSERT(denom);
+    double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
+    double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
+    SkDPoint p;
+    p.fX = (term1 * bxLen - axLen * term2) / denom;
+    p.fY = (term1 * byLen - ayLen * term2) / denom;
+    return p;
+}
+
+int SkIntersections::cleanUpCoincidence() {
+    do {
+        int last = fUsed - 1;
+        for (int index = 0; index < last; ++index) {
+            if (fT[0][index] == fT[0][index + 1]) {
+                removeOne(index + (int) (fT[1][index] == 0 || fT[1][index] == 1));
+                goto tryAgain;
+            }
+        }
+        for (int index = 0; index < last; ++index) {
+            if (fT[1][index] == fT[1][index + 1]) {
+                removeOne(index + (int) (fT[0][index] == 0 || fT[0][index] == 1));
+                goto tryAgain;
+            }
+        }
+        return fUsed;
+tryAgain: ;
+    } while (true);
+}
+
 void SkIntersections::cleanUpParallelLines(bool parallel) {
     while (fUsed > 2) {
         removeOne(1);
@@ -19,9 +58,6 @@ void SkIntersections::cleanUpParallelLines(bool parallel) {
             removeOne(endMatch);
         }
     }
-    if (fUsed == 2) {
-        fIsCoincident[0] = fIsCoincident[1] = 0x03;
-    }
 }
 
 void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -45,6 +81,12 @@ int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
     SkDVector ab0 = a[0] - b[0];
     double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
     double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
+#if 0
+    if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
+        fUsed = 0;
+        return 0;
+    }
+#endif
     numerA /= denom;
     numerB /= denom;
     int used;
@@ -148,6 +190,7 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
                     }
                     SkASSERT(a[iA] != b[nearer]);
                     SkASSERT(iA == (bNearA[nearer] > 0.5));
+                    fNearlySame[iA] = true;
                     insertNear(iA, nearer, a[iA], b[nearer]);
                     aNearB[iA] = -1;
                     bNearA[nearer] = -1;
@@ -192,6 +235,18 @@ static double horizontal_intercept(const SkDLine& line, double y) {
      return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
 }
 
+int SkIntersections::horizontal(const SkDLine& line, double y) {
+    fMax = 2;
+    int horizontalType = horizontal_coincident(line, y);
+    if (horizontalType == 1) {
+        fT[0][0] = horizontal_intercept(line, y);
+    } else if (horizontalType == 2) {
+        fT[0][0] = 0;
+        fT[0][1] = 1;
+    }
+    return fUsed = horizontalType;
+}
+
 int SkIntersections::horizontal(const SkDLine& line, double left, double right,
                                 double y, bool flipped) {
     fMax = 3;  // clean up parallel at the end will limit the result to 2 at the most
@@ -268,6 +323,18 @@ static double vertical_intercept(const SkDLine& line, double x) {
     return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
 }
 
+int SkIntersections::vertical(const SkDLine& line, double x) {
+    fMax = 2;
+    int verticalType = vertical_coincident(line, x);
+    if (verticalType == 1) {
+        fT[0][0] = vertical_intercept(line, x);
+    } else if (verticalType == 2) {
+        fT[0][0] = 0;
+        fT[0][1] = 1;
+    }
+    return fUsed = verticalType;
+}
+
 int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
                               double x, bool flipped) {
     fMax = 3;  // cleanup parallel lines will bring this back line
@@ -326,3 +393,14 @@ int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
     return fUsed;
 }
 
+// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
+// 4 subs, 2 muls, 1 cmp
+static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
+    return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
+}
+
+// 16 subs, 8 muls, 6 cmps
+bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
+    return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
+            && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
+}
diff --git a/src/pathops/SkDQuadImplicit.cpp b/src/pathops/SkDQuadImplicit.cpp
new file mode 100644 (file)
index 0000000..f0f66d1
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkDQuadImplicit.h"
+
+/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
+ *
+ * This paper proves that Syvester's method can compute the implicit form of
+ * the quadratic from the parameterized form.
+ *
+ * Given x = a*t*t + b*t + c  (the parameterized form)
+ *       y = d*t*t + e*t + f
+ *
+ * we want to find an equation of the implicit form:
+ *
+ * A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
+ *
+ * The implicit form can be expressed as a 4x4 determinant, as shown.
+ *
+ * The resultant obtained by Syvester's method is
+ *
+ * |   a   b   (c - x)     0     |
+ * |   0   a      b     (c - x)  |
+ * |   d   e   (f - y)     0     |
+ * |   0   d      e     (f - y)  |
+ *
+ * which expands to
+ *
+ * d*d*x*x + -2*a*d*x*y + a*a*y*y
+ *         + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
+ *         + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
+ *         +
+ * |   a   b   c   0   |
+ * |   0   a   b   c   | == 0.
+ * |   d   e   f   0   |
+ * |   0   d   e   f   |
+ *
+ * Expanding the constant determinant results in
+ *
+ *   | a b c |     | b c 0 |
+ * a*| e f 0 | + d*| a b c | ==
+ *   | d e f |     | d e f |
+ *
+ * a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
+ *
+ */
+
+// use the tricky arithmetic path, but leave the original to compare just in case
+static bool straight_forward = false;
+
+SkDQuadImplicit::SkDQuadImplicit(const SkDQuad& q) {
+    double a, b, c;
+    SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
+    double d, e, f;
+    SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
+    // compute the implicit coefficients
+    if (straight_forward) {  // 42 muls, 13 adds
+        fP[kXx_Coeff] = d * d;
+        fP[kXy_Coeff] = -2 * a * d;
+        fP[kYy_Coeff] = a * a;
+        fP[kX_Coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
+        fP[kY_Coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
+        fP[kC_Coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
+                   + d*(b*b*f + c*c*d - c*a*f - c*e*b);
+    } else {  // 26 muls, 11 adds
+        double aa = a * a;
+        double ad = a * d;
+        double dd = d * d;
+        fP[kXx_Coeff] = dd;
+        fP[kXy_Coeff] = -2 * ad;
+        fP[kYy_Coeff] = aa;
+        double be = b * e;
+        double bde = be * d;
+        double cdd = c * dd;
+        double ee = e * e;
+        fP[kX_Coeff] =  -2*cdd + bde - a*ee + 2*ad*f;
+        double aaf = aa * f;
+        double abe = a * be;
+        double ac = a * c;
+        double bb_2ac = b*b - 2*ac;
+        fP[kY_Coeff] = -2*aaf + abe - d*bb_2ac;
+        fP[kC_Coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
+    }
+}
+
+ /* Given a pair of quadratics, determine their parametric coefficients.
+  * If the scaled coefficients are nearly equal, then the part of the quadratics
+  * may be coincident.
+  * OPTIMIZATION -- since comparison short-circuits on no match,
+  * lazily compute the coefficients, comparing the easiest to compute first.
+  * xx and yy first; then xy; and so on.
+  */
+bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const {
+    int first = 0;
+    for (int index = 0; index <= kC_Coeff; ++index) {
+        if (approximately_zero(fP[index]) && approximately_zero(p2.fP[index])) {
+            first += first == index;
+            continue;
+        }
+        if (first == index) {
+            continue;
+        }
+        if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkDQuadImplicit::Match(const SkDQuad& quad1, const SkDQuad& quad2) {
+    SkDQuadImplicit i1(quad1);  // a'xx , b'xy , c'yy , d'x , e'y , f
+    SkDQuadImplicit i2(quad2);
+    return i1.match(i2);
+}
diff --git a/src/pathops/SkDQuadImplicit.h b/src/pathops/SkDQuadImplicit.h
new file mode 100644 (file)
index 0000000..24f1aac
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDQuadImplicit_DEFINED
+#define SkDQuadImplicit_DEFINED
+
+#include "SkPathOpsQuad.h"
+
+class SkDQuadImplicit {
+public:
+    explicit SkDQuadImplicit(const SkDQuad& q);
+
+    bool match(const SkDQuadImplicit& two) const;
+    static bool Match(const SkDQuad& quad1, const SkDQuad& quad2);
+
+    double x2() const { return fP[kXx_Coeff]; }
+    double xy() const { return fP[kXy_Coeff]; }
+    double y2() const { return fP[kYy_Coeff]; }
+    double x() const { return fP[kX_Coeff]; }
+    double y() const { return fP[kY_Coeff]; }
+    double c() const { return fP[kC_Coeff]; }
+
+private:
+    enum Coeffs {
+        kXx_Coeff,
+        kXy_Coeff,
+        kYy_Coeff,
+        kX_Coeff,
+        kY_Coeff,
+        kC_Coeff,
+    };
+
+    double fP[kC_Coeff + 1];
+};
+
+#endif
diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp
new file mode 100644 (file)
index 0000000..fcb9171
--- /dev/null
@@ -0,0 +1,617 @@
+// Another approach is to start with the implicit form of one curve and solve
+// (seek implicit coefficients in QuadraticParameter.cpp
+// by substituting in the parametric form of the other.
+// The downside of this approach is that early rejects are difficult to come by.
+// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
+
+#include "SkDQuadImplicit.h"
+#include "SkIntersections.h"
+#include "SkPathOpsLine.h"
+#include "SkQuarticRoot.h"
+#include "SkTArray.h"
+#include "SkTSort.h"
+
+/* 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
+ * then
+ * 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
+ */
+
+static int findRoots(const SkDQuadImplicit& i, const SkDQuad& quad, double roots[4],
+        bool oneHint, bool flip, int firstCubicRoot) {
+    SkDQuad flipped;
+    const SkDQuad& q = flip ? (flipped = quad.flip()) : quad;
+    double a, b, c;
+    SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
+    double d, e, f;
+    SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
+    const double t4 =     i.x2() *  a * a
+                    +     i.xy() *  a * d
+                    +     i.y2() *  d * d;
+    const double t3 = 2 * i.x2() *  a * b
+                    +     i.xy() * (a * e +     b * d)
+                    + 2 * i.y2() *  d * e;
+    const double t2 =     i.x2() * (b * b + 2 * a * c)
+                    +     i.xy() * (c * d +     b * e + a * f)
+                    +     i.y2() * (e * e + 2 * d * f)
+                    +     i.x()  *  a
+                    +     i.y()  *  d;
+    const double t1 = 2 * i.x2() *  b * c
+                    +     i.xy() * (c * e + b * f)
+                    + 2 * i.y2() *  e * f
+                    +     i.x()  *  b
+                    +     i.y()  *  e;
+    const double t0 =     i.x2() *  c * c
+                    +     i.xy() *  c * f
+                    +     i.y2() *  f * f
+                    +     i.x()  *  c
+                    +     i.y()  *  f
+                    +     i.c();
+    int rootCount = SkReducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
+    if (rootCount < 0) {
+        rootCount = SkQuarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
+    }
+    if (flip) {
+        for (int index = 0; index < rootCount; ++index) {
+            roots[index] = 1 - roots[index];
+        }
+    }
+    return rootCount;
+}
+
+static int addValidRoots(const double roots[4], const int count, double valid[4]) {
+    int result = 0;
+    int index;
+    for (index = 0; index < count; ++index) {
+        if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
+            continue;
+        }
+        double t = 1 - roots[index];
+        if (approximately_less_than_zero(t)) {
+            t = 0;
+        } else if (approximately_greater_than_one(t)) {
+            t = 1;
+        }
+        SkASSERT(t >= 0 && t <= 1);
+        valid[result++] = t;
+    }
+    return result;
+}
+
+static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+    for (int oddMan = 0; oddMan < 3; ++oddMan) {
+        const SkDPoint* endPt[2];
+        for (int opp = 1; opp < 3; ++opp) {
+            int end = oddMan ^ opp;  // choose a value not equal to oddMan
+            if (3 == end) {  // and correct so that largest value is 1 or 2
+                end = opp;
+            }
+            endPt[opp - 1] = &q1[end];
+        }
+        double origX = endPt[0]->fX;
+        double origY = endPt[0]->fY;
+        double adj = endPt[1]->fX - origX;
+        double opp = endPt[1]->fY - origY;
+        double sign = (q1[oddMan].fY - origY) * adj - (q1[oddMan].fX - origX) * opp;
+        if (approximately_zero(sign)) {
+            goto tryNextHalfPlane;
+        }
+        for (int n = 0; n < 3; ++n) {
+            double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
+            if (test * sign > 0 && !precisely_zero(test)) {
+                goto tryNextHalfPlane;
+            }
+        }
+        return true;
+tryNextHalfPlane:
+        ;
+    }
+    return false;
+}
+
+// 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 add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
+                          SkIntersections* i, bool* subDivide) {
+    double tMid = (tMin + tMax) / 2;
+    SkDPoint mid = q2.ptAtT(tMid);
+    SkDLine line;
+    line[0] = line[1] = mid;
+    SkDVector dxdy = q2.dxdyAtT(tMid);
+    line[0] -= dxdy;
+    line[1] += dxdy;
+    SkIntersections rootTs;
+    rootTs.allowNear(false);
+    int roots = rootTs.intersect(q1, line);
+    if (roots == 0) {
+        if (subDivide) {
+            *subDivide = true;
+        }
+        return true;
+    }
+    if (roots == 2) {
+        return false;
+    }
+    SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
+    if (!pt2.approximatelyEqual(mid)) {
+        return false;
+    }
+    i->insertSwap(rootTs[0][0], tMid, pt2);
+    return true;
+}
+
+static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
+                            double t2s, double t2e, SkIntersections* i, bool* subDivide) {
+    SkDQuad hull = q1.subDivide(t1s, t1e);
+    SkDLine line = {{hull[2], hull[0]}};
+    const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
+    const size_t kTestCount = SK_ARRAY_COUNT(testLines);
+    SkSTArray<kTestCount * 2, double, true> tsFound;
+    for (size_t index = 0; index < kTestCount; ++index) {
+        SkIntersections rootTs;
+        rootTs.allowNear(false);
+        int roots = rootTs.intersect(q2, *testLines[index]);
+        for (int idx2 = 0; idx2 < roots; ++idx2) {
+            double t = rootTs[0][idx2];
+#if 0 // def SK_DEBUG   // FIXME : accurate for error = 16, error of 17.5 seen
+// {{{136.08723965397621, 1648.2814535211637}, {593.49031197259478, 1190.8784277439891}, {593.49031197259478, 544.0128173828125}}}
+// {{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
+
+            SkDPoint qPt = q2.ptAtT(t);
+            SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
+            SkASSERT(qPt.approximatelyDEqual(lPt));
+#endif
+            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
+                continue;
+            }
+            tsFound.push_back(rootTs[0][idx2]);
+        }
+    }
+    int tCount = tsFound.count();
+    if (tCount <= 0) {
+        return true;
+    }
+    double tMin, tMax;
+    if (tCount == 1) {
+        tMin = tMax = tsFound[0];
+    } else {
+        SkASSERT(tCount > 1);
+        SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
+        tMin = tsFound[0];
+        tMax = tsFound[tsFound.count() - 1];
+    }
+    SkDPoint end = q2.ptAtT(t2s);
+    bool startInTriangle = hull.pointInHull(end);
+    if (startInTriangle) {
+        tMin = t2s;
+    }
+    end = q2.ptAtT(t2e);
+    bool endInTriangle = hull.pointInHull(end);
+    if (endInTriangle) {
+        tMax = t2e;
+    }
+    int split = 0;
+    SkDVector dxy1, dxy2;
+    if (tMin != tMax || tCount > 2) {
+        dxy2 = q2.dxdyAtT(tMin);
+        for (int index = 1; index < tCount; ++index) {
+            dxy1 = dxy2;
+            dxy2 = q2.dxdyAtT(tsFound[index]);
+            double dot = dxy1.dot(dxy2);
+            if (dot < 0) {
+                split = index - 1;
+                break;
+            }
+        }
+    }
+    if (split == 0) {  // there's one point
+        if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
+            return true;
+        }
+        i->swap();
+        return is_linear_inner(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 (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
+        result = true;
+    } else {
+        i->swap();
+        result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
+    }
+    if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
+        result = true;
+    } else {
+        i->swap();
+        result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
+    }
+    return result;
+}
+
+static double flat_measure(const SkDQuad& q) {
+    SkDVector mid = q[1] - q[0];
+    SkDVector dxy = q[2] - q[0];
+    double length = dxy.length();  // OPTIMIZE: get rid of sqrt
+    return fabs(mid.cross(dxy) / length);
+}
+
+// FIXME ? should this measure both and then use the quad that is the flattest as the line?
+static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
+    if (i->flatMeasure()) {
+        // for backward compatibility, use the old method when called from cubics
+        // FIXME: figure out how to fix cubics when it calls the new path
+        double measure = flat_measure(q1);
+        // OPTIMIZE: (get rid of sqrt) use approximately_zero
+        if (!approximately_zero_sqrt(measure)) {  // approximately_zero_sqrt
+            return false;
+        }
+     } else {
+        if (!q1.isLinear(0, 2)) {
+            return false;
+        }
+    }
+    return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
+}
+
+// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
+// avoid imprecision incurred with chopAt
+static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2,
+        double s2, double e2, SkIntersections* i) {
+    double m1 = flat_measure(*q1);
+    double m2 = flat_measure(*q2);
+    i->reset();
+    const SkDQuad* rounder, *flatter;
+    double sf, midf, ef, sr, er;
+    if (m2 < m1) {
+        rounder = q1;
+        sr = s1;
+        er = e1;
+        flatter = q2;
+        sf = s2;
+        midf = (s2 + e2) / 2;
+        ef = e2;
+    } else {
+        rounder = q2;
+        sr = s2;
+        er = e2;
+        flatter = q1;
+        sf = s1;
+        midf = (s1 + e1) / 2;
+        ef = e1;
+    }
+    bool subDivide = false;
+    is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
+    if (subDivide) {
+        relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
+        relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
+    }
+    if (m2 < m1) {
+        i->swapPts();
+    }
+}
+
+// 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 binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1Seed,
+                          double* t2Seed, SkDPoint* pt) {
+    double tStep = ROUGH_EPSILON;
+    SkDPoint t1[3], t2[3];
+    int calcMask = ~0;
+    do {
+        if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
+        if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*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].fX, t1[1].fY, t2[1].fX, t2[1].fY);
+    #endif
+            if (*t1Seed < 0) {
+                *t1Seed = 0;
+            } else if (*t1Seed > 1) {
+                *t1Seed = 1;
+            }
+            if (*t2Seed < 0) {
+                *t2Seed = 0;
+            } else if (*t2Seed > 1) {
+                *t2Seed = 1;
+            }
+            return true;
+        }
+        if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep));
+        if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep));
+        if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep));
+        if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *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].fX, t1[1].fY, t1[2].fX, t1[2].fY);
+#endif
+    return false;
+}
+
+static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
+        const SkIntersections& orig, bool swap, SkIntersections* i) {
+    if (orig.used() == 1 && orig[!swap][0] == testT) {
+        return;
+    }
+    if (orig.used() == 2 && orig[!swap][1] == testT) {
+        return;
+    }
+    SkDLine tmpLine;
+    int testTIndex = testT << 1;
+    tmpLine[0] = tmpLine[1] = q2[testTIndex];
+    tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY;
+    tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX;
+    SkIntersections impTs;
+    impTs.intersectRay(q1, tmpLine);
+    for (int index = 0; index < impTs.used(); ++index) {
+        SkDPoint realPt = impTs.pt(index);
+        if (!tmpLine[0].approximatelyPEqual(realPt)) {
+            continue;
+        }
+        if (swap) {
+            i->insert(testT, impTs[0][index], tmpLine[0]);
+        } else {
+            i->insert(impTs[0][index], testT, tmpLine[0]);
+        }
+    }
+}
+
+int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
+    fMax = 4;
+    bool exactMatch = false;
+    // if the quads share an end point, check to see if they overlap
+    for (int i1 = 0; i1 < 3; i1 += 2) {
+        for (int i2 = 0; i2 < 3; i2 += 2) {
+            if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
+                insert(i1 >> 1, i2 >> 1, q1[i1]);
+                exactMatch = true;
+            }
+        }
+    }
+    SkASSERT(fUsed < 3);
+    if (only_end_pts_in_common(q1, q2)) {
+        return fUsed;
+    }
+    if (only_end_pts_in_common(q2, q1)) {
+        return fUsed;
+    }
+    // see if either quad is really a line
+    // FIXME: figure out why reduce step didn't find this earlier
+    if (is_linear(q1, q2, this)) {
+        return fUsed;
+    }
+    SkIntersections swapped;
+    swapped.setMax(fMax);
+    if (is_linear(q2, q1, &swapped)) {
+        swapped.swapPts();
+        *this = swapped;
+        return fUsed;
+    }
+    SkIntersections copyI(*this);
+    lookNearEnd(q1, q2, 0, *this, false, &copyI);
+    lookNearEnd(q1, q2, 1, *this, false, &copyI);
+    lookNearEnd(q2, q1, 0, *this, true, &copyI);
+    lookNearEnd(q2, q1, 1, *this, true, &copyI);
+    int innerEqual = 0;
+    if (copyI.fUsed >= 2) {
+        SkASSERT(copyI.fUsed <= 4);
+        double width = copyI[0][1] - copyI[0][0];
+        int midEnd = 1;
+        for (int index = 2; index < copyI.fUsed; ++index) {
+            double testWidth = copyI[0][index] - copyI[0][index - 1];
+            if (testWidth <= width) {
+                continue;
+            }
+            midEnd = index;
+        }
+        for (int index = 0; index < 2; ++index) {
+            double testT = (copyI[0][midEnd] * (index + 1)
+                    + copyI[0][midEnd - 1] * (2 - index)) / 3;
+            SkDPoint testPt1 = q1.ptAtT(testT);
+            testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3;
+            SkDPoint testPt2 = q2.ptAtT(testT);
+            innerEqual += testPt1.approximatelyEqual(testPt2);
+        }
+    }
+    bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2;
+    if (expectCoincident) {
+        reset();
+        insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]);
+        int last = copyI.fUsed - 1;
+        insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]);
+        return fUsed;
+    }
+    SkDQuadImplicit i1(q1);
+    SkDQuadImplicit i2(q2);
+    int index;
+    bool flip1 = q1[2] == q2[0];
+    bool flip2 = q1[0] == q2[2];
+    bool useCubic = q1[0] == q2[0];
+    double roots1[4];
+    int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
+    // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
+    double roots1Copy[4];
+    SkDEBUGCODE(sk_bzero(roots1Copy, sizeof(roots1Copy)));
+    int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
+    SkDPoint pts1[4];
+    for (index = 0; index < r1Count; ++index) {
+        pts1[index] = q1.ptAtT(roots1Copy[index]);
+    }
+    double roots2[4];
+    int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
+    double roots2Copy[4];
+    int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
+    SkDPoint pts2[4];
+    for (index = 0; index < r2Count; ++index) {
+        pts2[index] = q2.ptAtT(roots2Copy[index]);
+    }
+    bool triedBinary = false;
+    if (r1Count == r2Count && r1Count <= 1) {
+        if (r1Count == 1 && used() == 0) {
+            if (pts1[0].approximatelyEqual(pts2[0])) {
+                insert(roots1Copy[0], roots2Copy[0], pts1[0]);
+            } else {
+                // find intersection by chasing t
+                triedBinary = true;
+                if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
+                    insert(roots1Copy[0], roots2Copy[0], pts1[0]);
+                }
+            }
+        }
+        return fUsed;
+    }
+    int closest[4];
+    double dist[4];
+    bool foundSomething = false;
+    for (index = 0; index < r1Count; ++index) {
+        dist[index] = DBL_MAX;
+        closest[index] = -1;
+        for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
+            if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
+                continue;
+            }
+            double dx = pts2[ndex2].fX - pts1[index].fX;
+            double dy = pts2[ndex2].fY - pts1[index].fY;
+            double distance = dx * dx + dy * dy;
+            if (dist[index] <= distance) {
+                continue;
+            }
+            for (int outer = 0; outer < index; ++outer) {
+                if (closest[outer] != ndex2) {
+                    continue;
+                }
+                if (dist[outer] < distance) {
+                    goto next;
+                }
+                closest[outer] = -1;
+            }
+            dist[index] = distance;
+            closest[index] = ndex2;
+            foundSomething = true;
+        next:
+            ;
+        }
+    }
+    if (r1Count && r2Count && !foundSomething) {
+        if (exactMatch) {
+            SkASSERT(fUsed > 0);
+            return fUsed;
+        }
+        relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
+        if (fUsed) {
+            return fUsed;
+        }
+        // maybe the curves are nearly coincident
+        if (!triedBinary && binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
+            insert(roots1Copy[0], roots2Copy[0], pts1[0]);
+        }
+        return fUsed;
+    }
+    int used = 0;
+    do {
+        double lowest = DBL_MAX;
+        int lowestIndex = -1;
+        for (index = 0; index < r1Count; ++index) {
+            if (closest[index] < 0) {
+                continue;
+            }
+            if (roots1Copy[index] < lowest) {
+                lowestIndex = index;
+                lowest = roots1Copy[index];
+            }
+        }
+        if (lowestIndex < 0) {
+            break;
+        }
+        insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
+                pts1[lowestIndex]);
+        closest[lowestIndex] = -1;
+    } while (++used < r1Count);
+    return fUsed;
+}
+
+void SkIntersections::alignQuadPts(const SkPoint q1[3], const SkPoint q2[3]) {
+    for (int index = 0; index < used(); ++index) {
+        const SkPoint result = pt(index).asSkPoint();
+        if (q1[0] == result || q1[2] == result || q2[0] == result || q2[2] == result) {
+            continue;
+        }
+        if (SkDPoint::ApproximatelyEqual(q1[0], result)) {
+            fPt[index].set(q1[0]);
+//            SkASSERT(way_roughly_zero(fT[0][index]));  // this value can be bigger than way rough
+            fT[0][index] = 0;
+        } else if (SkDPoint::ApproximatelyEqual(q1[2], result)) {
+            fPt[index].set(q1[2]);
+//            SkASSERT(way_roughly_equal(fT[0][index], 1));
+            fT[0][index] = 1;
+        }
+        if (SkDPoint::ApproximatelyEqual(q2[0], result)) {
+            fPt[index].set(q2[0]);
+//            SkASSERT(way_roughly_zero(fT[1][index]));
+            fT[1][index] = 0;
+        } else if (SkDPoint::ApproximatelyEqual(q2[2], result)) {
+            fPt[index].set(q2[2]);
+//            SkASSERT(way_roughly_equal(fT[1][index], 1));
+            fT[1][index] = 1;
+        }
+    }
+}
index b8a9a641dd528e1b5418e44c1e273b92cac31758..ef8edb02cda0959a3700a971c9d3c7d7ac674ab2 100644 (file)
@@ -105,29 +105,6 @@ public:
         fAllowNear = allow;
     }
 
-    void checkCoincident() {
-        int last = fIntersections->used() - 1;
-        for (int index = 0; index < last; ) {
-            double quadMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
-            SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
-            double t = fLine.nearPoint(quadMidPt, NULL);
-            if (t < 0) {
-                ++index;
-                continue;
-            }
-            if (fIntersections->isCoincident(index)) {
-                fIntersections->removeOne(index);
-                --last;
-            } else if (fIntersections->isCoincident(index + 1)) {
-                fIntersections->removeOne(index + 1);
-                --last;
-            } else {
-                fIntersections->setCoincident(index++);
-            }
-            fIntersections->setCoincident(index);
-        }
-    }
-
     int intersectRay(double roots[2]) {
     /*
         solve by rotating line+quad so line is horizontal, then finding the roots
@@ -163,17 +140,20 @@ public:
         if (fAllowNear) {
             addNearEndPoints();
         }
-        double rootVals[2];
-        int roots = intersectRay(rootVals);
-        for (int index = 0; index < roots; ++index) {
-            double quadT = rootVals[index];
-            double lineT = findLineT(quadT);
-            SkDPoint pt;
-            if (pinTs(&quadT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(quadT, pt)) {
-                fIntersections->insert(quadT, lineT, pt);
+        if (fIntersections->used() == 2) {
+            // FIXME : need sharable code that turns spans into coincident if middle point is on
+        } else {
+            double rootVals[2];
+            int roots = intersectRay(rootVals);
+            for (int index = 0; index < roots; ++index) {
+                double quadT = rootVals[index];
+                double lineT = findLineT(quadT);
+                SkDPoint pt;
+                if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) {
+                    fIntersections->insert(quadT, lineT, pt);
+                }
             }
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
@@ -198,41 +178,16 @@ public:
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
                 fIntersections->insert(quadT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
-    bool uniqueAnswer(double quadT, const SkDPoint& pt) {
-        for (int inner = 0; inner < fIntersections->used(); ++inner) {
-            if (fIntersections->pt(inner) != pt) {
-                continue;
-            }
-            double existingQuadT = (*fIntersections)[0][inner];
-            if (quadT == existingQuadT) {
-                return false;
-            }
-            // check if midway on quad is also same point. If so, discard this
-            double quadMidT = (existingQuadT + quadT) / 2;
-            SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
-            if (quadMidPt.approximatelyEqual(pt)) {
-                return false;
-            }
-        }
-#if ONE_OFF_DEBUG
-        SkDPoint qPt = fQuad.ptAtT(quadT);
-        SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
-                qPt.fX, qPt.fY);
-#endif
-        return true;
-    }
-
     int verticalIntersect(double axisIntercept, double roots[2]) {
         double D = fQuad[2].fX;  // f
         double E = fQuad[1].fX;  // e
@@ -254,14 +209,13 @@ public:
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
                 fIntersections->insert(quadT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
-        checkCoincident();
         return fIntersections->used();
     }
 
index c633fd02dfdffbb397a18206bd4fdd83a6b4bc54..3569c934de14a29d17224d2330d8bdfb8ce3676f 100644 (file)
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 #include "SkOpContour.h"
-#include "SkOpSegment.h"
 #include "SkPath.h"
 
 #ifdef SK_DEBUG
@@ -22,9 +21,42 @@ public:
         kCubic_Segment = SkPath::kCubic_Verb,
     };
 
+    bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
+        return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
+    }
+
+    // FIXME: does it make sense to write otherIndex now if we're going to
+    // fix it up later?
+    void addOtherT(int index, double otherT, int otherIndex) {
+        fContour->addOtherT(fIndex, index, otherT, otherIndex);
+    }
+
+    bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
+            bool swap) {
+        return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
+                swap);
+    }
+
+    // Avoid collapsing t values that are close to the same since
+    // we walk ts to describe consecutive intersections. Since a pair of ts can
+    // be nearly equal, any problems caused by this should be taken care
+    // of later.
+    // On the edge or out of range values are negative; add 2 to get end
+    int addT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
+        return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT);
+    }
+
+    int addSelfT(const SkPoint& pt, double newT) {
+        return fContour->addSelfT(fIndex, pt, newT);
+    }
+
     bool advance() {
-        fSegment = fSegment->next();
-        return fSegment != NULL;
+        return ++fIndex < fLast;
+    }
+
+    void alignTPt(SkIntersectionHelper& other, bool swap, int index,
+            SkIntersections* ts, SkPoint* point) {
+        fContour->alignTPt(fIndex, other.fContour, other.fIndex, swap, index, ts, point);
     }
 
     SkScalar bottom() const {
@@ -32,15 +64,30 @@ public:
     }
 
     const SkPathOpsBounds& bounds() const {
-        return fSegment->bounds();
+        return fContour->segments()[fIndex].bounds();
     }
 
-    SkOpContour* contour() const {
-        return fSegment->contour();
+    void init(SkOpContour* contour) {
+        fContour = contour;
+        fIndex = 0;
+        fLast = contour->segments().count();
     }
 
-    void init(SkOpContour* contour) {
-        fSegment = contour->first();
+    bool isAdjacent(const SkIntersectionHelper& next) {
+        return fContour == next.fContour && fIndex + 1 == next.fIndex;
+    }
+
+    bool isFirstLast(const SkIntersectionHelper& next) {
+        return fContour == next.fContour && fIndex == 0
+                && next.fIndex == fLast - 1;
+    }
+
+    bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const {
+        const SkOpSegment& segment = fContour->segments()[fIndex];
+        double mid = (t1 + t2) / 2;
+        SkDPoint midPtByT = segment.dPtAtT(mid);
+        SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
+        return midPtByT.approximatelyPEqual(midPtByAvg);
     }
 
     SkScalar left() const {
@@ -48,40 +95,41 @@ public:
     }
 
     const SkPoint* pts() const {
-        return fSegment->pts();
+        return fContour->segments()[fIndex].pts();
     }
 
     SkScalar right() const {
         return bounds().fRight;
     }
 
-    SkOpSegment* segment() const {
-        return fSegment;
-    }
-
     SegmentType segmentType() const {
-        SegmentType type = (SegmentType) fSegment->verb();
+        const SkOpSegment& segment = fContour->segments()[fIndex];
+        SegmentType type = (SegmentType) segment.verb();
         if (type != kLine_Segment) {
             return type;
         }
-        if (fSegment->isHorizontal()) {
+        if (segment.isHorizontal()) {
             return kHorizontalLine_Segment;
         }
-        if (fSegment->isVertical()) {
+        if (segment.isVertical()) {
             return kVerticalLine_Segment;
         }
         return kLine_Segment;
     }
 
     bool startAfter(const SkIntersectionHelper& after) {
-        fSegment = after.fSegment->next();
-        return fSegment != NULL;
+        fIndex = after.fIndex;
+        return advance();
     }
 
     SkScalar top() const {
         return bounds().fTop;
     }
 
+    SkPath::Verb verb() const {
+        return fContour->segments()[fIndex].verb();
+    }
+
     SkScalar x() const {
         return bounds().fLeft;
     }
@@ -99,5 +147,10 @@ public:
     }
 
 private:
-    SkOpSegment* fSegment;
+    // utility callable by the user from the debugger when the implementation code is linked in
+    void dump() const;
+
+    SkOpContour* fContour;
+    int fIndex;
+    int fLast;
 };
index 007efa7ff1110a337738dbb80d555514b2d0aefb..e9875cf69df37cfc29617c50a71065b2f73177ec 100644 (file)
@@ -7,25 +7,26 @@
 
 #include "SkIntersections.h"
 
-int SkIntersections::closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt,
-        double* closestDist) const {
-    int closest = -1;
-    *closestDist = SK_ScalarMax;
-    for (int index = 0; index < fUsed; ++index) {
-        if (!between(rangeStart, fT[0][index], rangeEnd)) {
-            continue;
-        }
-        const SkDPoint& iPt = fPt[index];
-        double dist = testPt.distanceSquared(iPt);
-        if (*closestDist > dist) {
-            *closestDist = dist;
-            closest = index;
-        }
+void SkIntersections::append(const SkIntersections& i) {
+    for (int index = 0; index < i.fUsed; ++index) {
+        insert(i[0][index], i[1][index], i.pt(index));
     }
-    return closest;
 }
 
-// called only by test code
+int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
+    NULL,
+    &SkIntersections::verticalLine,
+    &SkIntersections::verticalQuad,
+    &SkIntersections::verticalCubic
+};
+
+int ( SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine&) = {
+    NULL,
+    &SkIntersections::lineRay,
+    &SkIntersections::quadRay,
+    &SkIntersections::cubicRay
+};
+
 int SkIntersections::coincidentUsed() const {
     if (!fIsCoincident[0]) {
         SkASSERT(!fIsCoincident[1]);
@@ -47,12 +48,12 @@ int SkIntersections::coincidentUsed() const {
     return count;
 }
 
-int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
-    NULL,
-    &SkIntersections::verticalLine,
-    &SkIntersections::verticalQuad,
-    &SkIntersections::verticalCubic
-};
+int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
+    SkDCubic cubic;
+    cubic.set(pts);
+    fMax = 3;
+    return intersectRay(cubic, line);
+}
 
 void SkIntersections::flip() {
     for (int index = 0; index < fUsed; ++index) {
@@ -104,6 +105,7 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
     int remaining = fUsed - index;
     if (remaining > 0) {
         memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
+        memmove(&fPt2[index + 1], &fPt2[index], sizeof(fPt2[0]) * remaining);
         memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
         memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
         int clearMask = ~((1 << index) - 1);
@@ -123,53 +125,39 @@ void SkIntersections::insertNear(double one, double two, const SkDPoint& pt1, co
     SkASSERT(one == 0 || one == 1);
     SkASSERT(two == 0 || two == 1);
     SkASSERT(pt1 != pt2);
-    fNearlySame[one ? 1 : 0] = true;
+    SkASSERT(fNearlySame[(int) one]);
     (void) insert(one, two, pt1);
-    fPt2[one ? 1 : 0] = pt2;
+    fPt2[one ? fUsed - 1 : 0] = pt2;
 }
 
-int SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
+void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
     int index = insertSwap(one, two, pt);
-    if (index >= 0) {
-        setCoincident(index);
-    }
-    return index;
-}
-
-void SkIntersections::setCoincident(int index) {
-    SkASSERT(index >= 0);
     int bit = 1 << index;
     fIsCoincident[0] |= bit;
     fIsCoincident[1] |= bit;
 }
 
-void SkIntersections::merge(const SkIntersections& a, int aIndex, const SkIntersections& b,
-        int bIndex) {
-    this->reset();
-    fT[0][0] = a.fT[0][aIndex];
-    fT[1][0] = b.fT[0][bIndex];
-    fPt[0] = a.fPt[aIndex];
-    fPt2[0] = b.fPt[bIndex];
-    fUsed = 1;
+int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
+    SkDLine l;
+    l.set(pts);
+    fMax = 2;
+    return intersectRay(l, line);
 }
 
-int SkIntersections::mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const {
-    int result = -1;
-    for (int index = 0; index < fUsed; ++index) {
-        if (!between(rangeStart, fT[0][index], rangeEnd)) {
-            continue;
-        }
-        if (result < 0) {
-            result = index;
-            continue;
-        }
-        SkDVector best = fPt[result] - origin;
-        SkDVector test = fPt[index] - origin;
-        if (test.crossCheck(best) < 0) {
-            result = index;
-        }
+void SkIntersections::offset(int base, double start, double end) {
+    for (int index = base; index < fUsed; ++index) {
+        double val = fT[fSwap][index];
+        val *= end - start;
+        val += start;
+        fT[fSwap][index] = val;
     }
-    return result;
+}
+
+int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
+    SkDQuad quad;
+    quad.set(pts);
+    fMax = 2;
+    return intersectRay(quad, line);
 }
 
 void SkIntersections::quickRemoveOne(int index, int replace) {
@@ -184,6 +172,7 @@ void SkIntersections::removeOne(int index) {
         return;
     }
     memmove(&fPt[index], &fPt[index + 1], sizeof(fPt[0]) * remaining);
+    memmove(&fPt2[index], &fPt2[index + 1], sizeof(fPt2[0]) * remaining);
     memmove(&fT[0][index], &fT[0][index + 1], sizeof(fT[0][0]) * remaining);
     memmove(&fT[1][index], &fT[1][index + 1], sizeof(fT[1][0]) * remaining);
 //    SkASSERT(fIsCoincident[0] == 0);
@@ -193,6 +182,13 @@ void SkIntersections::removeOne(int index) {
     fIsCoincident[1] -= ((fIsCoincident[1] >> 1) & ~((1 << index) - 1)) + coBit;
 }
 
+void SkIntersections::swapPts() {
+    int index;
+    for (index = 0; index < fUsed; ++index) {
+        SkTSwap(fT[0][index], fT[1][index]);
+    }
+}
+
 int SkIntersections::verticalLine(const SkPoint a[2], SkScalar top, SkScalar bottom,
         SkScalar x, bool flipped) {
     SkDLine line;
index 15bac19defdf2a45078187f587751ba1d3ede028..a1bde512db075a813facec448336968e0ca646c3 100644 (file)
@@ -16,6 +16,7 @@ class SkIntersections {
 public:
     SkIntersections()
         : fSwap(0)
+        , fFlatMeasure(false)
 #ifdef SK_DEBUG
         , fDepth(0)
 #endif
@@ -23,6 +24,7 @@ public:
         sk_bzero(fPt, sizeof(fPt));
         sk_bzero(fPt2, sizeof(fPt2));
         sk_bzero(fT, sizeof(fT));
+        sk_bzero(fIsCoincident, sizeof(fIsCoincident));
         sk_bzero(fNearlySame, sizeof(fNearlySame));
         reset();
         fMax = 0;  // require that the caller set the max
@@ -30,7 +32,7 @@ public:
 
     class TArray {
     public:
-        explicit TArray(const double ts[10]) : fTArray(ts) {}
+        explicit TArray(const double ts[9]) : fTArray(ts) {}
         double operator[](int n) const {
             return fTArray[n];
         }
@@ -38,15 +40,28 @@ public:
     };
     TArray operator[](int n) const { return TArray(fT[n]); }
 
+    void allowFlatMeasure(bool flatAllowed) {
+        fFlatMeasure = flatAllowed;
+    }
+
     void allowNear(bool nearAllowed) {
         fAllowNear = nearAllowed;
     }
 
-    void clearCoincidence(int index) {
-        SkASSERT(index >= 0);
-        int bit = 1 << index;
-        fIsCoincident[0] &= ~bit;
-        fIsCoincident[1] &= ~bit;
+    int cubic(const SkPoint a[4]) {
+        SkDCubic cubic;
+        cubic.set(a);
+        fMax = 1;  // self intersect
+        return intersect(cubic);
+    }
+
+    int cubicCubic(const SkPoint a[4], const SkPoint b[4]) {
+        SkDCubic aCubic;
+        aCubic.set(a);
+        SkDCubic bCubic;
+        bCubic.set(b);
+        fMax = 9;
+        return intersect(aCubic, bCubic);
     }
 
     int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@@ -73,6 +88,19 @@ public:
         return intersect(cubic, line);
     }
 
+    int cubicQuad(const SkPoint a[4], const SkPoint b[3]) {
+        SkDCubic cubic;
+        cubic.set(a);
+        SkDQuad quad;
+        quad.set(b);
+        fMax = 7;
+        return intersect(cubic, quad);
+    }
+
+    bool flatMeasure() const {
+        return fFlatMeasure;
+    }
+
     bool hasT(double t) const {
         SkASSERT(t == 0 || t == 1);
         return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
@@ -150,11 +178,19 @@ public:
         return intersect(quad, line);
     }
 
+    int quadQuad(const SkPoint a[3], const SkPoint b[3]) {
+        SkDQuad aQuad;
+        aQuad.set(a);
+        SkDQuad bQuad;
+        bQuad.set(b);
+        fMax = 4;
+        return intersect(aQuad, bQuad);
+    }
+
     // leaves swap, max alone
     void reset() {
         fAllowNear = true;
         fUsed = 0;
-        sk_bzero(fIsCoincident, sizeof(fIsCoincident));
     }
 
     void set(bool swap, int tIndex, double t) {
@@ -169,6 +205,8 @@ public:
         fSwap ^= true;
     }
 
+    void swapPts();
+
     bool swapped() const {
         return fSwap;
     }
@@ -181,27 +219,19 @@ public:
         SkASSERT(--fDepth >= 0);
     }
 
-    bool unBumpT(int index) {
-        SkASSERT(fUsed == 1);
-        fT[0][index] = fT[0][index] * (1 + BUMP_EPSILON * 2) - BUMP_EPSILON;
-        if (!between(0, fT[0][index], 1)) {
-            fUsed = 0;
-            return false;
-        }
-        return true;
-    }
-
     void upDepth() {
         SkASSERT(++fDepth < 16);
     }
 
     void alignQuadPts(const SkPoint a[3], const SkPoint b[3]);
+    void append(const SkIntersections& );
     int cleanUpCoincidence();
-    int closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt, double* dist) const;
     int coincidentUsed() const;
     void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1,
                      const SkDCubic& c2);
+    int cubicRay(const SkPoint pts[4], const SkDLine& line);
     void flip();
+    int horizontal(const SkDLine&, double y);
     int horizontal(const SkDLine&, double left, double right, double y, bool flipped);
     int horizontal(const SkDQuad&, double left, double right, double y, bool flipped);
     int horizontal(const SkDQuad&, double left, double right, double y, double tRange[2]);
@@ -212,20 +242,25 @@ public:
     int insert(double one, double two, const SkDPoint& pt);
     void insertNear(double one, double two, const SkDPoint& pt1, const SkDPoint& pt2);
     // start if index == 0 : end if index == 1
-    int insertCoincident(double one, double two, const SkDPoint& pt);
+    void insertCoincident(double one, double two, const SkDPoint& pt);
     int intersect(const SkDLine&, const SkDLine&);
     int intersect(const SkDQuad&, const SkDLine&);
     int intersect(const SkDQuad&, const SkDQuad&);
+    int intersect(const SkDCubic&);  // return true if cubic self-intersects
     int intersect(const SkDCubic&, const SkDLine&);
+    int intersect(const SkDCubic&, const SkDQuad&);
     int intersect(const SkDCubic&, const SkDCubic&);
     int intersectRay(const SkDLine&, const SkDLine&);
     int intersectRay(const SkDQuad&, const SkDLine&);
     int intersectRay(const SkDCubic&, const SkDLine&);
-    void merge(const SkIntersections& , int , const SkIntersections& , int );
-    int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
+    static SkDPoint Line(const SkDLine&, const SkDLine&);
+    int lineRay(const SkPoint pts[2], const SkDLine& line);
+    void offset(int base, double start, double end);
     void quickRemoveOne(int index, int replace);
+    int quadRay(const SkPoint pts[3], const SkDLine& line);
     void removeOne(int index);
-    void setCoincident(int index);
+    static bool Test(const SkDLine& , const SkDLine&);
+    int vertical(const SkDLine&, double x);
     int vertical(const SkDLine&, double top, double bottom, double x, bool flipped);
     int vertical(const SkDQuad&, double top, double bottom, double x, bool flipped);
     int vertical(const SkDCubic&, double top, double bottom, double x, bool flipped);
@@ -241,8 +276,6 @@ public:
 #endif
     }
 
-    void dump() const;  // implemented for testing only
-
 private:
     bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2);
     bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2);
@@ -250,20 +283,22 @@ private:
     void cleanUpParallelLines(bool parallel);
     void computePoints(const SkDLine& line, int used);
 
-    SkDPoint fPt[10];  // FIXME: since scans store points as SkPoint, this should also
-    SkDPoint fPt2[2];  // used by nearly same to store alternate intersection point
-    double fT[2][10];
+    SkDPoint fPt[9];  // FIXME: since scans store points as SkPoint, this should also
+    SkDPoint fPt2[9];  // used by nearly same to store alternate intersection point
+    double fT[2][9];
     uint16_t fIsCoincident[2];  // bit set for each curve's coincident T
     bool fNearlySame[2];  // true if end points nearly match
     unsigned char fUsed;
     unsigned char fMax;
     bool fAllowNear;
     bool fSwap;
+    bool fFlatMeasure;  // backwards-compatibility when cubics uses quad intersection
 #ifdef SK_DEBUG
     int fDepth;
 #endif
 };
 
+extern int (SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine& );
 extern int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar top, SkScalar bottom,
             SkScalar x, bool flipped);
 
index c13a51a8cca39c6da63b725ba747c30a11648597..b3a188c1e82307434d848e08dabbab16c6806e03 100644 (file)
@@ -4,26 +4,26 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "SkIntersections.h"
 #include "SkOpAngle.h"
 #include "SkOpSegment.h"
 #include "SkPathOpsCurve.h"
 #include "SkTSort.h"
 
+#if DEBUG_ANGLE
+#include "SkString.h"
+#endif
+
 /* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest
    positive y. The largest angle has a positive x and a zero y. */
 
 #if DEBUG_ANGLE
-    static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, int append,
-             bool compare) {
+    static bool CompareResult(SkString* bugOut, int append, bool compare) {
         SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append);
-        SkDebugf("%sPart %s\n", func, bugPart[0].c_str());
-        SkDebugf("%sPart %s\n", func, bugPart[1].c_str());
-        SkDebugf("%sPart %s\n", func, bugPart[2].c_str());
         return compare;
     }
 
-    #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
-            compare)
+    #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare)
 #else
     #define COMPARE_RESULT(append, compare) compare
 #endif
 */
 
 // return true if lh < this < rh
-bool SkOpAngle::after(SkOpAngle* test) {
-    SkOpAngle* lh = test;
-    SkOpAngle* rh = lh->fNext;
-    SkASSERT(lh != rh);
+bool SkOpAngle::after(const SkOpAngle* test) const {
+    const SkOpAngle& lh = *test;
+    const SkOpAngle& rh = *lh.fNext;
+    SkASSERT(&lh != &rh);
 #if DEBUG_ANGLE
     SkString bugOut;
     bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
-            lh->fStart->t(), lh->fEnd->t(),
-            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
-            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
-            rh->fStart->t(), rh->fEnd->t());
-    SkString bugPart[3] = { lh->debugPart(), this->debugPart(), rh->debugPart() };
+            lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
+            lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
+            fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
+            fSegment->t(fEnd),
+            rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
+            rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
 #endif
-    if (lh->fComputeSector && !lh->computeSector()) {
+    if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) {
         return COMPARE_RESULT(1, true);
     }
-    if (fComputeSector && !this->computeSector()) {
+    if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) {
         return COMPARE_RESULT(2, true);
     }
-    if (rh->fComputeSector && !rh->computeSector()) {
+    if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) {
         return COMPARE_RESULT(3, true);
     }
 #if DEBUG_ANGLE  // reset bugOut with computed sectors
     bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
-            lh->fStart->t(), lh->fEnd->t(),
-            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
-            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
-            rh->fStart->t(), rh->fEnd->t());
+            lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
+            lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
+            fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
+            fSegment->t(fEnd),
+            rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
+            rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
 #endif
-    bool ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
-    bool lrOverlap = lh->fSectorMask & rh->fSectorMask;
+    bool ltrOverlap = (lh.fSectorMask | rh.fSectorMask) & fSectorMask;
+    bool lrOverlap = lh.fSectorMask & rh.fSectorMask;
     int lrOrder;  // set to -1 if either order works
     if (!lrOverlap) {  // no lh/rh sector overlap
         if (!ltrOverlap) {  // no lh/this/rh sector overlap
-            return COMPARE_RESULT(4,  (lh->fSectorEnd > rh->fSectorStart)
-                    ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
+            return COMPARE_RESULT(4,  (lh.fSectorEnd > rh.fSectorStart)
+                    ^ (fSectorStart > lh.fSectorEnd) ^ (fSectorStart > rh.fSectorStart));
         }
-        int lrGap = (rh->fSectorStart - lh->fSectorStart + 32) & 0x1f;
+        int lrGap = (rh.fSectorStart - lh.fSectorStart + 32) & 0x1f;
         /* A tiny change can move the start +/- 4. The order can only be determined if
            lr gap is not 12 to 20 or -12 to -20.
                -31 ..-21      1
@@ -114,24 +115,24 @@ bool SkOpAngle::after(SkOpAngle* test) {
          */
         lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1;
     } else {
-        lrOrder = (int) lh->orderable(rh);
+        lrOrder = (int) lh.orderable(rh);
         if (!ltrOverlap) {
             return COMPARE_RESULT(5, !lrOrder);
         }
     }
     int ltOrder;
-    SkASSERT((lh->fSectorMask & fSectorMask) || (rh->fSectorMask & fSectorMask));
-    if (lh->fSectorMask & fSectorMask) {
-        ltOrder = (int) lh->orderable(this);
+    SkASSERT((lh.fSectorMask & fSectorMask) || (rh.fSectorMask & fSectorMask));
+    if (lh.fSectorMask & fSectorMask) {
+        ltOrder = (int) lh.orderable(*this);
     } else {
-        int ltGap = (fSectorStart - lh->fSectorStart + 32) & 0x1f;
+        int ltGap = (fSectorStart - lh.fSectorStart + 32) & 0x1f;
         ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1;
     }
     int trOrder;
-    if (rh->fSectorMask & fSectorMask) {
+    if (rh.fSectorMask & fSectorMask) {
         trOrder = (int) orderable(rh);
     } else {
-        int trGap = (rh->fSectorStart - fSectorStart + 32) & 0x1f;
+        int trGap = (rh.fSectorStart - fSectorStart + 32) & 0x1f;
         trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1;
     }
     if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) {
@@ -144,20 +145,20 @@ bool SkOpAngle::after(SkOpAngle* test) {
     if (ltOrder == 0 && lrOrder == 0) {
         SkASSERT(trOrder < 0);
         // FIXME : once this is verified to work, remove one opposite angle call
-        SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
-        bool ltOpposite = lh->oppositePlanes(this);
+        SkDEBUGCODE(bool lrOpposite = lh.oppositePlanes(rh));
+        bool ltOpposite = lh.oppositePlanes(*this);
         SkASSERT(lrOpposite != ltOpposite);
         return COMPARE_RESULT(8, ltOpposite);
     } else if (ltOrder == 1 && trOrder == 0) {
         SkASSERT(lrOrder < 0);
-        SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
+        SkDEBUGCODE(bool ltOpposite = lh.oppositePlanes(*this));
         bool trOpposite = oppositePlanes(rh);
         SkASSERT(ltOpposite != trOpposite);
         return COMPARE_RESULT(9, trOpposite);
     } else if (lrOrder == 1 && trOrder == 1) {
         SkASSERT(ltOrder < 0);
         SkDEBUGCODE(bool trOpposite = oppositePlanes(rh));
-        bool lrOpposite = lh->oppositePlanes(rh);
+        bool lrOpposite = lh.oppositePlanes(rh);
         SkASSERT(lrOpposite != trOpposite);
         return COMPARE_RESULT(10, lrOpposite);
     }
@@ -172,50 +173,77 @@ bool SkOpAngle::after(SkOpAngle* test) {
 
 // given a line, see if the opposite curve's convex hull is all on one side
 // returns -1=not on one side    0=this CW of test   1=this CCW of test
-int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
+int SkOpAngle::allOnOneSide(const SkOpAngle& test) const {
     SkASSERT(!fIsCurve);
-    SkASSERT(test->fIsCurve);
-    const SkDPoint& origin = test->fCurvePart[0];
+    SkASSERT(test.fIsCurve);
+    const SkDPoint& origin = test.fCurvePart[0];
     SkVector line;
-    if (segment()->verb() == SkPath::kLine_Verb) {
-        const SkPoint* linePts = segment()->pts();
-        int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
+    if (fSegment->verb() == SkPath::kLine_Verb) {
+        const SkPoint* linePts = fSegment->pts();
+        int lineStart = fStart < fEnd ? 0 : 1;
         line = linePts[lineStart ^ 1] - linePts[lineStart];
     } else {
         SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() };
         line = shortPts[1] - shortPts[0];
     }
     float crosses[3];
-    SkPath::Verb testVerb = test->segment()->verb();
+    SkPath::Verb testVerb = test.fSegment->verb();
     int iMax = SkPathOpsVerbToPoints(testVerb);
 //    SkASSERT(origin == test.fCurveHalf[0]);
-    const SkDCubic& testCurve = test->fCurvePart;
-    for (int index = 1; index <= iMax; ++index) {
-        float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
-        float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
-        crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
-    }
-    if (crosses[0] * crosses[1] < 0) {
-        return -1;
-    }
-    if (SkPath::kCubic_Verb == testVerb) {
-        if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
+    const SkDCubic& testCurve = test.fCurvePart;
+//    do {
+        for (int index = 1; index <= iMax; ++index) {
+            float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
+            float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
+            crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
+        }
+        if (crosses[0] * crosses[1] < 0) {
             return -1;
         }
-    }
-    if (crosses[0]) {
-        return crosses[0] < 0;
-    }
-    if (crosses[1]) {
-        return crosses[1] < 0;
-    }
-    if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
-        return crosses[2] < 0;
-    }
+        if (SkPath::kCubic_Verb == testVerb) {
+            if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
+                return -1;
+            }
+        }
+        if (crosses[0]) {
+            return crosses[0] < 0;
+        }
+        if (crosses[1]) {
+            return crosses[1] < 0;
+        }
+        if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
+            return crosses[2] < 0;
+        }
     fUnorderable = true;
     return -1;
 }
 
+bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const {
+    double absX = fabs(x);
+    double absY = fabs(y);
+    double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
+    int exponent;
+    (void) frexp(length, &exponent);
+    double epsilon = ldexp(FLT_EPSILON, exponent);
+    SkPath::Verb verb = fSegment->verb();
+    SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb);
+    // FIXME: the quad and cubic factors are made up ; determine actual values
+    double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon;
+    double xSlop = slop;
+    double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
+    double x1 = x - xSlop;
+    double y1 = y + ySlop;
+    double x_ry1 = x1 * ry;
+    double rx_y1 = rx * y1;
+    *result = x_ry1 < rx_y1;
+    double x2 = x + xSlop;
+    double y2 = y - ySlop;
+    double x_ry2 = x2 * ry;
+    double rx_y2 = rx * y2;
+    bool less2 = x_ry2 < rx_y2;
+    return *result == less2;
+}
+
 bool SkOpAngle::checkCrossesZero() const {
     int start = SkTMin(fSectorStart, fSectorEnd);
     int end = SkTMax(fSectorStart, fSectorEnd);
@@ -223,94 +251,31 @@ bool SkOpAngle::checkCrossesZero() const {
     return crossesZero;
 }
 
-// loop looking for a pair of angle parts that are too close to be sorted
-/* This is called after other more simple intersection and angle sorting tests have been exhausted.
-   This should be rarely called -- the test below is thorough and time consuming.
-   This checks the distance between start points; the distance between 
-*/
-void SkOpAngle::checkNearCoincidence() {
-    SkOpAngle* test = this;
-    do {
-        SkOpSegment* testSegment = test->segment();
-        double testStartT = test->start()->t();
-        SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
-        double testEndT = test->end()->t();
-        SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
-        double testLenSq = testStartPt.distanceSquared(testEndPt);
-        if (0) {
-            SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
-        }
-        double testMidT = (testStartT + testEndT) / 2;
-        SkOpAngle* next = test;
-        while ((next = next->fNext) != this) {
-            SkOpSegment* nextSegment = next->segment();
-            double testMidDistSq = testSegment->distSq(testMidT, next);
-            double testEndDistSq = testSegment->distSq(testEndT, next);
-            double nextStartT = next->start()->t();
-            SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
-            double distSq = testStartPt.distanceSquared(nextStartPt);
-            double nextEndT = next->end()->t();
-            double nextMidT = (nextStartT + nextEndT) / 2;
-            double nextMidDistSq = nextSegment->distSq(nextMidT, test);
-            double nextEndDistSq = nextSegment->distSq(nextEndT, test);
-            if (0) {
-                SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
-                        testSegment->debugID(), nextSegment->debugID());
-                SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
-                SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
-                SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
-                SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
-                SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
-                double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
-                SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
-                SkDebugf("\n");
-            }
-        }
-        test = test->fNext;
-    } while (test->fNext != this); 
-}
-
-bool SkOpAngle::checkParallel(SkOpAngle* rh) {
+bool SkOpAngle::checkParallel(const SkOpAngle& rh) const {
     SkDVector scratch[2];
     const SkDVector* sweep, * tweep;
-    if (!this->fUnorderedSweep) {
-        sweep = this->fSweep;
+    if (!fUnorderedSweep) {
+        sweep = fSweep;
     } else {
-        scratch[0] = this->fCurvePart[1] - this->fCurvePart[0];
+        scratch[0] = fCurvePart[1] - fCurvePart[0];
         sweep = &scratch[0];
     }
-    if (!rh->fUnorderedSweep) {
-        tweep = rh->fSweep;
+    if (!rh.fUnorderedSweep) {
+        tweep = rh.fSweep;
     } else {
-        scratch[1] = rh->fCurvePart[1] - rh->fCurvePart[0];
+        scratch[1] = rh.fCurvePart[1] - rh.fCurvePart[0];
         tweep = &scratch[1];
     }
     double s0xt0 = sweep->crossCheck(*tweep);
     if (tangentsDiverge(rh, s0xt0)) {
         return s0xt0 < 0;
     }
-    // compute the perpendicular to the endpoints and see where it intersects the opposite curve
-    // if the intersections within the t range, do a cross check on those
-    bool inside;
-    if (this->endToSide(rh, &inside)) {
-        return inside;
-    }
-    if (rh->endToSide(this, &inside)) {
-        return !inside;
-    }
-    if (this->midToSide(rh, &inside)) {
-        return inside;
-    }
-    if (rh->midToSide(this, &inside)) {
-        return !inside;
-    }
-    // compute the cross check from the mid T values (last resort)
-    SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
-    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+    SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
+    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
     double m0xm1 = m0.crossCheck(m1);
     if (m0xm1 == 0) {
-        this->fUnorderable = true;
-        rh->fUnorderable = true;
+        fUnorderable = true;
+        rh.fUnorderable = true;
         return true;
     }
     return m0xm1 < 0;
@@ -323,51 +288,48 @@ bool SkOpAngle::computeSector() {
     if (fComputedSector) {
         return !fUnorderable;
     }
+//    SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small());
     fComputedSector = true;
-    bool stepUp = fStart->t() < fEnd->t();
-    const SkOpSpanBase* checkEnd = fEnd;
-    if (checkEnd->final() && stepUp) {
-        fUnorderable = true;
-        return false;
-    }
+    int step = fStart < fEnd ? 1 : -1;
+    int limit = step > 0 ? fSegment->count() : -1;
+    int checkEnd = fEnd;
     do {
 // advance end
-        const SkOpSegment* other = checkEnd->segment();
-        const SkOpSpanBase* oSpan = other->head();
-        do {
-            if (oSpan->segment() != segment()) {
+        const SkOpSpan& span = fSegment->span(checkEnd);
+        const SkOpSegment* other = span.fOther;
+        int oCount = other->count();
+        for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+            const SkOpSpan& oSpan = other->span(oIndex);
+            if (oSpan.fOther != fSegment) {
                 continue;
             }
-            if (oSpan == checkEnd) {
+            if (oSpan.fOtherIndex == checkEnd) {
                 continue;
             }
-            if (!approximately_equal(oSpan->t(), checkEnd->t())) {
+            if (!approximately_equal(oSpan.fOtherT, span.fT)) {
                 continue;
             }
             goto recomputeSector;
-        } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
-        checkEnd = stepUp ? !checkEnd->final()
-                ? checkEnd->upCast()->next() : NULL
-                : checkEnd->prev();
-    } while (checkEnd);
+        }
+        checkEnd += step;
+    } while (checkEnd != limit);
 recomputeSector:
-    SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head() 
-            : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
-    if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
+    if (checkEnd == fEnd || checkEnd - step == fEnd) {
         fUnorderable = true;
         return false;
     }
-    SkOpSpanBase* saveEnd = fEnd;
-    fComputedEnd = fEnd = computedEnd;
+    int saveEnd = fEnd;
+    fComputedEnd = fEnd = checkEnd - step;
     setSpans();
     setSector();
     fEnd = saveEnd;
     return !fUnorderable;
 }
 
-int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
-    const SkDVector* sweep = this->fSweep;
-    const SkDVector* tweep = rh->fSweep;
+// returns -1 if overlaps   0 if no overlap cw    1 if no overlap ccw
+int SkOpAngle::convexHullOverlaps(const SkOpAngle& rh) const {
+    const SkDVector* sweep = fSweep;
+    const SkDVector* tweep = rh.fSweep;
     double s0xs1 = sweep[0].crossCheck(sweep[1]);
     double s0xt0 = sweep[0].crossCheck(tweep[0]);
     double s1xt0 = sweep[1].crossCheck(tweep[0]);
@@ -397,8 +359,8 @@ int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
     // if the outside sweeps are greater than 180 degress:
         // first assume the inital tangents are the ordering
         // if the midpoint direction matches the inital order, that is enough
-    SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
-    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+    SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
+    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
     double m0xm1 = m0.crossCheck(m1);
     if (s0xt0 > 0 && m0xm1 > 0) {
         return 0;
@@ -432,30 +394,34 @@ double SkOpAngle::distEndRatio(double dist) const {
     return sqrt(longest) / dist;
 }
 
-bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
-    SkPath::Verb lVerb = this->segment()->verb();
-    SkPath::Verb rVerb = rh->segment()->verb();
+bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
+    SkPath::Verb lVerb = fSegment->verb();
+    SkPath::Verb rVerb = rh.fSegment->verb();
     int lPts = SkPathOpsVerbToPoints(lVerb);
     int rPts = SkPathOpsVerbToPoints(rVerb);
-    SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
-            {{this->fCurvePart[0], this->fCurvePart[lPts]}}};
+    SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}},
+            {{fCurvePart[0], fCurvePart[lPts]}}};
     if (rays[0][1] == rays[1][1]) {
         return checkParallel(rh);
     }
     double smallTs[2] = {-1, -1};
     bool limited[2] = {false, false};
     for (int index = 0; index < 2; ++index) {
+        const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
+        SkIntersections i;
         int cPts = index ? rPts : lPts;
+        (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i);
         // if the curve is a line, then the line and the ray intersect only at their crossing
         if (cPts == 1) { // line
             continue;
         }
-        const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
-        SkIntersections i;
-        (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i);
-        double tStart = index ? rh->fStart->t() : this->fStart->t();
-        double tEnd = index ? rh->fComputedEnd->t() : this->fComputedEnd->t();
-        bool testAscends = tStart < (index ? rh->fComputedEnd->t() : this->fComputedEnd->t());
+//      SkASSERT(i.used() >= 1);
+//        if (i.used() <= 1) {
+//            continue;
+//        }
+        double tStart = segment.t(index ? rh.fStart : fStart);
+        double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd);
+        bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd;
         double t = testAscends ? 0 : 1;
         for (int idx2 = 0; idx2 < i.used(); ++idx2) {
             double testT = i[0][idx2];
@@ -469,6 +435,29 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
             limited[index] = approximately_equal_orderable(t, tEnd);
         }
     }
+#if 0
+    if (smallTs[0] < 0 && smallTs[1] < 0) {  // if neither ray intersects, do endpoint sort
+        double m0xm1 = 0;
+        if (lVerb == SkPath::kLine_Verb) {
+            SkASSERT(rVerb != SkPath::kLine_Verb);
+            SkDVector m0 = rays[1][1] - fCurvePart[0];
+            SkDPoint endPt;
+            endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]);
+            SkDVector m1 = endPt - fCurvePart[0];
+            m0xm1 = m0.crossCheck(m1);
+        }
+        if (rVerb == SkPath::kLine_Verb) {
+            SkDPoint endPt;
+            endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]);
+            SkDVector m0 = endPt - fCurvePart[0];
+            SkDVector m1 = rays[0][1] - fCurvePart[0];
+            m0xm1 = m0.crossCheck(m1);
+        }
+        if (m0xm1 != 0) {
+            return m0xm1 < 0;
+        }
+    }
+#endif
     bool sRayLonger = false;
     SkDVector sCept = {0, 0};
     double sCeptT = -1;
@@ -478,7 +467,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         if (smallTs[index] < 0) {
             continue;
         }
-        const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
+        const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
         const SkDPoint& dPt = segment.dPtAtT(smallTs[index]);
         SkDVector cept = dPt - rays[index][0];
         // If this point is on the curve, it should have been detected earlier by ordinary
@@ -509,7 +498,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         double minX, minY, maxX, maxY;
         minX = minY = SK_ScalarInfinity;
         maxX = maxY = -SK_ScalarInfinity;
-        const SkDCubic& curve = index ? rh->fCurvePart : this->fCurvePart;
+        const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart;
         int ptCount = index ? rPts : lPts;
         for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
             minX = SkTMin(minX, curve[idx2].fX);
@@ -519,7 +508,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         }
         double maxWidth = SkTMax(maxX - minX, maxY - minY);
         delta /= maxWidth;
-        if (delta > 1e-3 && (useIntersect ^= true)) {  // FIXME: move this magic number
+        if (delta > 1e-4 && (useIntersect ^= true)) {  // FIXME: move this magic number
             sRayLonger = rayLonger;
             sCept = cept;
             sCeptT = smallTs[index];
@@ -527,9 +516,9 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         }
     }
     if (useIntersect) {
-        const SkDCubic& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
-        const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
-        double tStart = sIndex ? rh->fStart->t() : fStart->t();
+        const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart;
+        const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment;
+        double tStart = segment.t(sIndex ? rh.fStart : fStart);
         SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
         double septDir = mid.crossCheck(sCept);
         if (!septDir) {
@@ -541,65 +530,12 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
     }
 }
 
-bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
-    const SkOpSegment* segment = this->segment();
-    SkPath::Verb verb = segment->verb();
-    int pts = SkPathOpsVerbToPoints(verb);
-    SkDLine rayEnd;
-    rayEnd[0].set(this->fEnd->pt());
-    rayEnd[1] = rayEnd[0];
-    SkDVector slopeAtEnd = (*CurveDSlopeAtT[pts])(segment->pts(), this->fEnd->t());
-    rayEnd[1].fX += slopeAtEnd.fY;
-    rayEnd[1].fY -= slopeAtEnd.fX;
-    SkIntersections iEnd;
-    const SkOpSegment* oppSegment = rh->segment();
-    SkPath::Verb oppVerb = oppSegment->verb();
-    int oppPts = SkPathOpsVerbToPoints(oppVerb);
-    (*CurveIntersectRay[oppPts])(oppSegment->pts(), rayEnd, &iEnd);
-    double endDist;
-    int closestEnd = iEnd.closestTo(rh->fStart->t(), rh->fEnd->t(), rayEnd[0], &endDist);
-    if (closestEnd < 0) {
-        return false;
-    }
-    if (!endDist) {
-        return false;
-    }
-    SkDPoint start;
-    start.set(this->fStart->pt());
-    // OPTIMIZATION: multiple times in the code we find the max scalar
-    double minX, minY, maxX, maxY;
-    minX = minY = SK_ScalarInfinity;
-    maxX = maxY = -SK_ScalarInfinity;
-    const SkDCubic& curve = rh->fCurvePart;
-    for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
-        minX = SkTMin(minX, curve[idx2].fX);
-        minY = SkTMin(minY, curve[idx2].fY);
-        maxX = SkTMax(maxX, curve[idx2].fX);
-        maxY = SkTMax(maxY, curve[idx2].fY);
-    }
-    double maxWidth = SkTMax(maxX - minX, maxY - minY);
-    endDist /= maxWidth;
-    if (endDist < 5e-11) {  // empirically found
-        return false;
-    }
-    const SkDPoint* endPt = &rayEnd[0];
-    SkDPoint oppPt = iEnd.pt(closestEnd);
-    SkDVector vLeft = *endPt - start;
-    SkDVector vRight = oppPt - start;
-    double dir = vLeft.crossCheck(vRight);
-    if (!dir) {
-        return false;
-    }
-    *inside = dir < 0;
-    return true;
-}
-
 // Most of the time, the first one can be found trivially by detecting the smallest sector value.
 // If all angles have the same sector value, actual sorting is required.
-SkOpAngle* SkOpAngle::findFirst() {
-    SkOpAngle* best = this;
+const SkOpAngle* SkOpAngle::findFirst() const {
+    const SkOpAngle* best = this;
     int bestStart = SkTMin(fSectorStart, fSectorEnd);
-    SkOpAngle* angle = this;
+    const SkOpAngle* angle = this;
     while ((angle = angle->fNext) != this) {
         int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
         if (angleEnd < bestStart) {
@@ -612,7 +548,7 @@ SkOpAngle* SkOpAngle::findFirst() {
         }
     }
     // back up to the first possible angle
-    SkOpAngle* firstBest = best;
+    const SkOpAngle* firstBest = best;
     angle = best;
     int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd);
     while ((angle = angle->previous()) != firstBest) {
@@ -636,7 +572,7 @@ SkOpAngle* SkOpAngle::findFirst() {
         if (angle->fStop) {
             return firstBest;
         }
-        bool orderable = best->orderable(angle);  // note: may return an unorderable angle
+        bool orderable = best->orderable(*angle);  // note: may return an unorderable angle
         if (orderable == 0) {
             return angle;
         }
@@ -703,11 +639,6 @@ int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const {
     return sector;
 }
 
-SkOpGlobalState* SkOpAngle::globalState() const {
-    return this->segment()->globalState();
-}
-
-
 // OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
 // OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
 void SkOpAngle::insert(SkOpAngle* angle) {
@@ -731,6 +662,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
     }
     SkOpAngle* next = fNext;
     if (next->fNext == this) {
+        if (angle->overlap(*this)) {  // angles are essentially coincident
+            return;
+        }
         if (singleton || angle->after(this)) {
             this->fNext = angle;
             angle->fNext = next;
@@ -744,6 +678,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
     SkOpAngle* last = this;
     do {
         SkASSERT(last->fNext == next);
+        if (angle->overlap(*last) || angle->overlap(*next)) {
+            return;
+        }
         if (angle->after(last)) {
             last->fNext = angle;
             angle->fNext = next;
@@ -752,49 +689,48 @@ void SkOpAngle::insert(SkOpAngle* angle) {
         }
         last = next;
         next = next->fNext;
-        if (last == this) {
-            if (next->fUnorderable) {
-                fUnorderable = true;
-            } else {
-                globalState()->setAngleCoincidence();
-                this->fNext = angle;
-                angle->fNext = next;
-                angle->fCheckCoincidence = true;
-            }
+        if (last == this && next->fUnorderable) {
+            fUnorderable = true;
             return;
         }
+        SkASSERT(last != this);
     } while (true);
 }
 
-SkOpSpanBase* SkOpAngle::lastMarked() const {
+bool SkOpAngle::isHorizontal() const {
+    return !fIsCurve && fSweep[0].fY == 0;
+}
+
+SkOpSpan* SkOpAngle::lastMarked() const {
     if (fLastMarked) {
-        if (fLastMarked->chased()) {
+        if (fLastMarked->fChased) {
             return NULL;
         }
-        fLastMarked->setChased(true);
+        fLastMarked->fChased = true;
     }
     return fLastMarked;
 }
 
-bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
+bool SkOpAngle::loopContains(const SkOpAngle& test) const {
     if (!fNext) {
         return false;
     }
     const SkOpAngle* first = this;
     const SkOpAngle* loop = this;
-    const SkOpSegment* tSegment = angle->fStart->segment();
-    double tStart = angle->fStart->t();
-    double tEnd = angle->fEnd->t();
+    const SkOpSegment* tSegment = test.fSegment;
+    double tStart = tSegment->span(test.fStart).fT;
+    double tEnd = tSegment->span(test.fEnd).fT;
     do {
-        const SkOpSegment* lSegment = loop->fStart->segment();
+        const SkOpSegment* lSegment = loop->fSegment;
+        // FIXME : use precisely_equal ? or compare points exactly ?
         if (lSegment != tSegment) {
             continue;
         }
-        double lStart = loop->fStart->t();
+        double lStart = lSegment->span(loop->fStart).fT;
         if (lStart != tEnd) {
             continue;
         }
-        double lEnd = loop->fEnd->t();
+        double lEnd = lSegment->span(loop->fEnd).fT;
         if (lEnd == tStart) {
             return true;
         }
@@ -846,65 +782,39 @@ bool SkOpAngle::merge(SkOpAngle* angle) {
         working = next;
     } while (working != angle);
     // it's likely that a pair of the angles are unorderable
+#if 0 && DEBUG_ANGLE
+    SkOpAngle* last = angle;
+    working = angle->fNext;
+    do {
+        SkASSERT(last->fNext == working);
+        last->fNext = working->fNext;
+        SkASSERT(working->after(last));
+        last->fNext = working;
+        last = working;
+        working = working->fNext;
+    } while (last != angle);
+#endif
     debugValidateNext();
     return true;
 }
 
 double SkOpAngle::midT() const {
-    return (fStart->t() + fEnd->t()) / 2;
-}
-
-bool SkOpAngle::midToSide(const SkOpAngle* rh, bool* inside) const {
-    const SkOpSegment* segment = this->segment();
-    SkPath::Verb verb = segment->verb();
-    int pts = SkPathOpsVerbToPoints(verb);
-    const SkPoint& startPt = this->fStart->pt();
-    const SkPoint& endPt = this->fEnd->pt();
-    SkDPoint dStartPt;
-    dStartPt.set(startPt);
-    SkDLine rayMid;
-    rayMid[0].fX = (startPt.fX + endPt.fX) / 2;
-    rayMid[0].fY = (startPt.fY + endPt.fY) / 2;
-    rayMid[1].fX = rayMid[0].fX + (endPt.fY - startPt.fY);
-    rayMid[1].fY = rayMid[0].fY - (endPt.fX - startPt.fX);
-    SkIntersections iMid;
-    (*CurveIntersectRay[pts])(segment->pts(), rayMid, &iMid);
-    int iOutside = iMid.mostOutside(this->fStart->t(), this->fEnd->t(), dStartPt);
-    if (iOutside < 0) {
-        return false;
-    }
-    const SkOpSegment* oppSegment = rh->segment();
-    SkPath::Verb oppVerb = oppSegment->verb();
-    int oppPts = SkPathOpsVerbToPoints(oppVerb);
-    SkIntersections oppMid;
-    (*CurveIntersectRay[oppPts])(oppSegment->pts(), rayMid, &oppMid);
-    int oppOutside = oppMid.mostOutside(rh->fStart->t(), rh->fEnd->t(), dStartPt);
-    if (oppOutside < 0) {
-        return false;
-    }
-    SkDVector iSide = iMid.pt(iOutside) - dStartPt;
-    SkDVector oppSide = oppMid.pt(oppOutside) - dStartPt;
-    double dir = iSide.crossCheck(oppSide);
-    if (!dir) {
-        return false;
-    }
-    *inside = dir < 0;
-    return true;
+    return (fSegment->t(fStart) + fSegment->t(fEnd)) / 2;
 }
 
-bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
-    int startSpan = abs(rh->fSectorStart - fSectorStart);
+bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const {
+    int startSpan = abs(rh.fSectorStart - fSectorStart);
     return startSpan >= 8;
 }
 
-bool SkOpAngle::orderable(SkOpAngle* rh) {
+bool SkOpAngle::orderable(const SkOpAngle& rh) const {
     int result;
     if (!fIsCurve) {
-        if (!rh->fIsCurve) {
+        if (!rh.fIsCurve) {
             double leftX = fTangentHalf.dx();
             double leftY = fTangentHalf.dy();
-            double rightX = rh->fTangentHalf.dx();
-            double rightY = rh->fTangentHalf.dy();
+            double rightX = rh.fTangentHalf.dx();
+            double rightY = rh.fTangentHalf.dy();
             double x_ry = leftX * rightY;
             double rx_y = rightX * leftY;
             if (x_ry == rx_y) {
@@ -919,14 +829,14 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
         if ((result = allOnOneSide(rh)) >= 0) {
             return result;
         }
-        if (fUnorderable || approximately_zero(rh->fSide)) {
+        if (fUnorderable || approximately_zero(rh.fSide)) {
             goto unorderable;
         }
-    } else if (!rh->fIsCurve) {
-        if ((result = rh->allOnOneSide(this)) >= 0) {
+    } else if (!rh.fIsCurve) {
+        if ((result = rh.allOnOneSide(*this)) >= 0) {
             return !result;
         }
-        if (rh->fUnorderable || approximately_zero(fSide)) {
+        if (rh.fUnorderable || approximately_zero(fSide)) {
             goto unorderable;
         }
     }
@@ -936,10 +846,27 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
     return endsIntersect(rh);
 unorderable:
     fUnorderable = true;
-    rh->fUnorderable = true;
+    rh.fUnorderable = true;
     return true;
 }
 
+bool SkOpAngle::overlap(const SkOpAngle& other) const {
+    int min = SkTMin(fStart, fEnd);
+    const SkOpSpan& span = fSegment->span(min);
+    const SkOpSegment* oSeg = other.fSegment;
+    int oMin = SkTMin(other.fStart, other.fEnd);
+    const SkOpSpan& oSpan = oSeg->span(oMin);
+    if (!span.fSmall && !oSpan.fSmall) {
+        return false;
+    }
+    if (fSegment->span(fStart).fPt != oSeg->span(other.fStart).fPt) {
+        return false;
+    }
+    // see if small span is contained by opposite span
+    return span.fSmall ? oSeg->containsPt(fSegment->span(fEnd).fPt, other.fEnd, other.fStart)
+            : fSegment->containsPt(oSeg->span(other.fEnd).fPt, fEnd, fStart);
+}
+
 // OPTIMIZE: if this shows up in a profile, add a previous pointer
 // as is, this should be rarely called
 SkOpAngle* SkOpAngle::previous() const {
@@ -953,32 +880,26 @@ SkOpAngle* SkOpAngle::previous() const {
     } while (true);
 }
 
-SkOpSegment* SkOpAngle::segment() const {
-    return fStart->segment();
-}
-
-void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
+void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
+    fSegment = segment;
     fStart = start;
     fComputedEnd = fEnd = end;
-    SkASSERT(start != end);
     fNext = NULL;
-    fComputeSector = fComputedSector = fCheckCoincidence = false;
+    fComputeSector = fComputedSector = false;
     fStop = false;
     setSpans();
     setSector();
-    PATH_OPS_DEBUG_CODE(fID = start->globalState()->nextAngleID());
 }
 
 void SkOpAngle::setCurveHullSweep() {
     fUnorderedSweep = false;
     fSweep[0] = fCurvePart[1] - fCurvePart[0];
-    const SkOpSegment* segment = fStart->segment();
-    if (SkPath::kLine_Verb == segment->verb()) {
+    if (SkPath::kLine_Verb == fSegment->verb()) {
         fSweep[1] = fSweep[0];
         return;
     }
     fSweep[1] = fCurvePart[2] - fCurvePart[0];
-    if (SkPath::kCubic_Verb != segment->verb()) {
+    if (SkPath::kCubic_Verb != fSegment->verb()) {
         if (!fSweep[0].fX && !fSweep[0].fY) {
             fSweep[0] = fSweep[1];
         }
@@ -1012,16 +933,64 @@ void SkOpAngle::setCurveHullSweep() {
     fSweep[1] = thirdSweep;
 }
 
+void SkOpAngle::setSector() {
+    SkPath::Verb verb = fSegment->verb();
+    if (SkPath::kLine_Verb != verb && small()) {
+        goto deferTilLater;
+    }
+    fSectorStart = findSector(verb, fSweep[0].fX, fSweep[0].fY);
+    if (fSectorStart < 0) {
+        goto deferTilLater;
+    }
+    if (!fIsCurve) {  // if it's a line or line-like, note that both sectors are the same
+        SkASSERT(fSectorStart >= 0);
+        fSectorEnd = fSectorStart;
+        fSectorMask = 1 << fSectorStart;
+        return;
+    }
+    SkASSERT(SkPath::kLine_Verb != verb);
+    fSectorEnd = findSector(verb, fSweep[1].fX, fSweep[1].fY);
+    if (fSectorEnd < 0) {
+deferTilLater:
+        fSectorStart = fSectorEnd = -1;
+        fSectorMask = 0;
+        fComputeSector = true;  // can't determine sector until segment length can be found
+        return;
+    }
+    if (fSectorEnd == fSectorStart) {
+        SkASSERT((fSectorStart & 3) != 3);  // if the sector has no span, it can't be an exact angle
+        fSectorMask = 1 << fSectorStart;
+        return;
+    }
+    bool crossesZero = checkCrossesZero();
+    int start = SkTMin(fSectorStart, fSectorEnd);
+    bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
+    // bump the start and end of the sector span if they are on exact compass points
+    if ((fSectorStart & 3) == 3) {
+        fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
+    }
+    if ((fSectorEnd & 3) == 3) {
+        fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
+    }
+    crossesZero = checkCrossesZero();
+    start = SkTMin(fSectorStart, fSectorEnd);
+    int end = SkTMax(fSectorStart, fSectorEnd);
+    if (!crossesZero) {
+        fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
+    } else {
+        fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
+    }
+}
+
 void SkOpAngle::setSpans() {
-    fUnorderable = false;
+    fUnorderable = fSegment->isTiny(this);
     fLastMarked = NULL;
-    const SkOpSegment* segment = fStart->segment();
-    const SkPoint* pts = segment->pts();
+    const SkPoint* pts = fSegment->pts();
     SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
             = SK_ScalarNaN);
-    segment->subDivide(fStart, fEnd, &fCurvePart);
+    fSegment->subDivide(fStart, fEnd, &fCurvePart);
     setCurveHullSweep();
-    const SkPath::Verb verb = segment->verb();
+    const SkPath::Verb verb = fSegment->verb();
     if (verb != SkPath::kLine_Verb
             && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
         SkDLine lineHalf;
@@ -1033,9 +1002,9 @@ void SkOpAngle::setSpans() {
     switch (verb) {
     case SkPath::kLine_Verb: {
         SkASSERT(fStart != fEnd);
-        const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
+        const SkPoint& cP1 = pts[fStart < fEnd];
         SkDLine lineHalf;
-        lineHalf[0].set(fStart->pt());
+        lineHalf[0].set(fSegment->span(fStart).fPt);
         lineHalf[1].set(cP1);
         fTangentHalf.lineEndPoints(lineHalf);
         fSide = 0;
@@ -1054,8 +1023,8 @@ void SkOpAngle::setSpans() {
         double testTs[4];
         // OPTIMIZATION: keep inflections precomputed with cubic segment?
         int testCount = SkDCubic::FindInflections(pts, testTs);
-        double startT = fStart->t();
-        double endT = fEnd->t();
+        double startT = fSegment->t(fStart);
+        double endT = fSegment->t(fEnd);
         double limitT = endT;
         int index;
         for (index = 0; index < testCount; ++index) {
@@ -1095,63 +1064,19 @@ void SkOpAngle::setSpans() {
     }
 }
 
-void SkOpAngle::setSector() {
-    const SkOpSegment* segment = fStart->segment();
-    SkPath::Verb verb = segment->verb();
-    fSectorStart = this->findSector(verb, fSweep[0].fX, fSweep[0].fY);
-    if (fSectorStart < 0) {
-        goto deferTilLater;
-    }
-    if (!fIsCurve) {  // if it's a line or line-like, note that both sectors are the same
-        SkASSERT(fSectorStart >= 0);
-        fSectorEnd = fSectorStart;
-        fSectorMask = 1 << fSectorStart;
-        return;
-    }
-    SkASSERT(SkPath::kLine_Verb != verb);
-    fSectorEnd = this->findSector(verb, fSweep[1].fX, fSweep[1].fY);
-    if (fSectorEnd < 0) {
-deferTilLater:
-        fSectorStart = fSectorEnd = -1;
-        fSectorMask = 0;
-        fComputeSector = true;  // can't determine sector until segment length can be found
-        return;
-    }
-    if (fSectorEnd == fSectorStart
-            && (fSectorStart & 3) != 3) { // if the sector has no span, it can't be an exact angle
-        fSectorMask = 1 << fSectorStart;
-        return;
-    }
-    bool crossesZero = this->checkCrossesZero();
-    int start = SkTMin(fSectorStart, fSectorEnd);
-    bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
-    // bump the start and end of the sector span if they are on exact compass points
-    if ((fSectorStart & 3) == 3) {
-        fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
-    }
-    if ((fSectorEnd & 3) == 3) {
-        fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
-    }
-    crossesZero = this->checkCrossesZero();
-    start = SkTMin(fSectorStart, fSectorEnd);
-    int end = SkTMax(fSectorStart, fSectorEnd);
-    if (!crossesZero) {
-        fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
-    } else {
-        fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
+bool SkOpAngle::small() const {
+    int min = SkMin32(fStart, fEnd);
+    int max = SkMax32(fStart, fEnd);
+    for (int index = min; index < max; ++index) {
+        const SkOpSpan& mSpan = fSegment->span(index);
+        if (!mSpan.fSmall) {
+            return false;
+        }
     }
+    return true;
 }
 
-int SkOpAngle::sign() const {
-    SkASSERT(fStart->t() != fEnd->t());
-    return fStart->t() < fEnd->t() ? -1 : 1;
-}
-
-SkOpSpan* SkOpAngle::starter() {
-    return fStart->starter(fEnd);
-}
-
-bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
+bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const {
     if (s0xt0 == 0) {
         return false;
     }
@@ -1165,7 +1090,7 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
     // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
     // m = v1.cross(v2) / v1.dot(v2)
     const SkDVector* sweep = fSweep;
-    const SkDVector* tweep = rh->fSweep;
+    const SkDVector* tweep = rh.fSweep;
     double s0dt0 = sweep[0].dot(tweep[0]);
     if (!s0dt0) {
         return true;
@@ -1175,6 +1100,36 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
     double sDist = sweep[0].length() * m;
     double tDist = tweep[0].length() * m;
     bool useS = fabs(sDist) < fabs(tDist);
-    double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
+    double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist));
     return mFactor < 5000;  // empirically found limit
 }
+
+SkOpAngleSet::SkOpAngleSet() 
+    : fAngles(NULL)
+#if DEBUG_ANGLE
+    , fCount(0)
+#endif
+{
+}
+
+SkOpAngleSet::~SkOpAngleSet() {
+    SkDELETE(fAngles);
+}
+
+SkOpAngle& SkOpAngleSet::push_back() {
+    if (!fAngles) {
+        fAngles = SkNEW_ARGS(SkChunkAlloc, (2));
+    }
+    void* ptr = fAngles->allocThrow(sizeof(SkOpAngle));
+    SkOpAngle* angle = (SkOpAngle*) ptr;
+#if DEBUG_ANGLE
+    angle->setID(++fCount);
+#endif
+    return *angle;
+}
+
+void SkOpAngleSet::reset() {
+    if (fAngles) {
+        fAngles->reset();
+    }
+}
index 84b37010c94ecf11f98a004c67c7b5cfe89e731b..1dc425061352f43188d88abea5a6d84512b7827b 100644 (file)
@@ -7,18 +7,17 @@
 #ifndef SkOpAngle_DEFINED
 #define SkOpAngle_DEFINED
 
+#include "SkChunkAlloc.h"
 #include "SkLineParameters.h"
-#if DEBUG_ANGLE
-#include "SkString.h"
-#endif
 
-class SkOpContour;
-class SkOpPtT;
 class SkOpSegment;
-class SkOpSpanBase;
-class SkOpSpan;
+struct SkOpSpan;
 
-struct SkOpAngle {
+// sorting angles
+// given angles of {dx dy ddx ddy dddx dddy} sort them
+class SkOpAngle {
+public:
+    enum { kStackBasedCount = 8 }; // FIXME: determine what this should be
     enum IncludeType {
         kUnaryWinding,
         kUnaryXor,
@@ -26,66 +25,29 @@ struct SkOpAngle {
         kBinaryOpp,
     };
 
-    bool after(SkOpAngle* test);
-    int allOnOneSide(const SkOpAngle* test);
-    bool checkCrossesZero() const;
-    void checkNearCoincidence();
-    bool checkParallel(SkOpAngle* );
-    bool computeSector();
-    int convexHullOverlaps(const SkOpAngle* ) const;
-
-    const SkOpAngle* debugAngle(int id) const;
-    SkOpContour* debugContour(int id);
 
-    int debugID() const {
-        return PATH_OPS_DEBUG_RELEASE(fID, -1);
+    int end() const {
+        return fEnd;
     }
 
-#if DEBUG_SORT
-    void debugLoop() const;
-#endif
+    const SkOpAngle* findFirst() const;
 
-#if DEBUG_ANGLE
-    SkString debugPart() const;
-#endif
-    const SkOpPtT* debugPtT(int id) const;
-    const SkOpSegment* debugSegment(int id) const;
-    const SkOpSpanBase* debugSpan(int id) const;
-    void debugValidate() const; 
-    void debugValidateNext() const;  // in debug builds, verify that angle loop is uncorrupted
-    double distEndRatio(double dist) const;
-    // available to testing only
-    void dump() const;
-    void dumpCurves() const;
-    void dumpLoop() const;
-    void dumpOne(bool functionHeader) const;
-    void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
-    void dumpTest() const;
-
-    SkOpSpanBase* end() const {
-        return fEnd;
+    bool inLoop() const {
+        return !!fNext;
     }
 
-    bool endsIntersect(SkOpAngle* );
-    bool endToSide(const SkOpAngle* rh, bool* inside) const;
-    SkOpAngle* findFirst();
-    int findSector(SkPath::Verb verb, double x, double y) const;
-    SkOpGlobalState* globalState() const;
     void insert(SkOpAngle* );
-    SkOpSpanBase* lastMarked() const;
-    bool loopContains(const SkOpAngle* ) const;
+    bool isHorizontal() const;
+    SkOpSpan* lastMarked() const;
+    bool loopContains(const SkOpAngle& ) const;
     int loopCount() const;
     void markStops();
     bool merge(SkOpAngle* );
-    double midT() const;
-    bool midToSide(const SkOpAngle* rh, bool* inside) const;
 
     SkOpAngle* next() const {
         return fNext;
     }
 
-    bool oppositePlanes(const SkOpAngle* rh) const;
-    bool orderable(SkOpAngle* rh);  // false == this < rh ; true == this > rh
     SkOpAngle* previous() const;
 
     int sectorEnd() const {
@@ -96,57 +58,120 @@ struct SkOpAngle {
         return fSectorStart;
     }
 
-    SkOpSegment* segment() const;
+    void set(const SkOpSegment* segment, int start, int end);
 
-    void set(SkOpSpanBase* start, SkOpSpanBase* end);
-    void setCurveHullSweep();
+    void setLastMarked(SkOpSpan* marked) {
+        fLastMarked = marked;
+    }
 
-    void setID(int id) {
-        PATH_OPS_DEBUG_CODE(fID = id);
+    SkOpSegment* segment() const {
+        return const_cast<SkOpSegment*>(fSegment);
     }
 
-    void setLastMarked(SkOpSpanBase* marked) {
-        fLastMarked = marked;
+    int sign() const {
+        return SkSign32(fStart - fEnd);
     }
 
-    void setSector();
-    void setSpans();
-    int sign() const;
+    bool small() const;
 
-    SkOpSpanBase* start() const {
+    int start() const {
         return fStart;
     }
 
-    SkOpSpan* starter();
-    bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
-
     bool unorderable() const {
         return fUnorderable;
     }
 
-    SkDCubic fCurvePart;  // the curve from start to end
+    // available to testing only
+#if DEBUG_SORT
+    void debugLoop() const;  // called by code during run
+#endif
+#if DEBUG_ANGLE
+    void debugSameAs(const SkOpAngle* compare) const;
+#endif
+    void dump() const;
+    void dumpLoop() const;
+    void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
+
+#if DEBUG_ANGLE
+    int debugID() const { return fID; }
+
+    void setID(int id) {
+        fID = id;
+    }
+#else
+    int debugID() const { return 0; }
+#endif
+
+#if DEBUG_VALIDATE
+    void debugValidateLoop() const;
+#endif
+
+private:
+    bool after(const SkOpAngle* test) const;
+    int allOnOneSide(const SkOpAngle& test) const;
+    bool calcSlop(double x, double y, double rx, double ry, bool* result) const;
+    bool checkCrossesZero() const;
+    bool checkParallel(const SkOpAngle& ) const;
+    bool computeSector();
+    int convexHullOverlaps(const SkOpAngle& ) const;
+    double distEndRatio(double dist) const;
+    int findSector(SkPath::Verb verb, double x, double y) const;
+    bool endsIntersect(const SkOpAngle& ) const;
+    double midT() const;
+    bool oppositePlanes(const SkOpAngle& rh) const;
+    bool orderable(const SkOpAngle& rh) const;  // false == this < rh ; true == this > rh
+    bool overlap(const SkOpAngle& test) const;
+    void setCurveHullSweep();
+    void setSector();
+    void setSpans();
+    bool tangentsDiverge(const SkOpAngle& rh, double s0xt0) const;
+
+    SkDCubic fCurvePart; // the curve from start to end
     double fSide;
     SkLineParameters fTangentHalf;  // used only to sort a pair of lines or line-like sections
+    const SkOpSegment* fSegment;
     SkOpAngle* fNext;
-    SkOpSpanBase* fLastMarked;
+    SkOpSpan* fLastMarked;
     SkDVector fSweep[2];
-    SkOpSpanBase* fStart;
-    SkOpSpanBase* fEnd;
-    SkOpSpanBase* fComputedEnd;
+    int fStart;
+    int fEnd;
+    int fComputedEnd;
     int fSectorMask;
     int8_t fSectorStart;  // in 32nds of a circle
     int8_t fSectorEnd;
     bool fIsCurve;
-    bool fStop;  // set if ordered angle is greater than the previous
-    bool fUnorderable;
+    bool fStop; // set if ordered angle is greater than the previous
+    mutable bool fUnorderable;  // this is editable by orderable()
     bool fUnorderedSweep;  // set when a cubic's first control point between the sweep vectors
     bool fComputeSector;
     bool fComputedSector;
-    bool fCheckCoincidence;
-    PATH_OPS_DEBUG_CODE(int fID);
 
+#if DEBUG_ANGLE
+    int fID;
+#endif
+#if DEBUG_VALIDATE
+    void debugValidateNext() const;  // in debug builds, verify that angle loop is uncorrupted
+#else
+    void debugValidateNext() const {}
+#endif
+    void dumpOne(bool showFunc) const;  // available to testing only
+    void dumpPartials() const;  // utility to be called by user from debugger
+    friend class PathOpsAngleTester;
 };
 
-
+class SkOpAngleSet {
+public:
+    SkOpAngleSet();
+    ~SkOpAngleSet();
+    SkOpAngle& push_back();
+    void reset();
+private:
+    void dump() const;  // utility to be called by user from debugger
+    SkChunkAlloc* fAngles;
+#if DEBUG_ANGLE
+    int fCount;
+#endif
+};
 
 #endif
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
deleted file mode 100755 (executable)
index 45eee0a..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkOpCoincidence.h"
-#include "SkOpSegment.h"
-#include "SkPathOpsTSect.h"
-
-void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
-        SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
-    SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
-    bool flipped = oppPtTStart->fT > oppPtTEnd->fT;
-    SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
-    coinRec->fNext = this->fHead;
-    coinRec->fCoinPtTStart = coinPtTStart;
-    coinRec->fCoinPtTEnd = coinPtTEnd;
-    coinRec->fOppPtTStart = oppPtTStart;
-    coinRec->fOppPtTEnd = oppPtTEnd;
-    coinRec->fFlipped = flipped;
-    this->fHead = coinRec;
-}
-
-static void tRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
-        const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
-    double denom = overE->fT - overS->fT;
-    double start = 0 < denom ? tStart : tEnd;
-    double end = 0 < denom ? tEnd : tStart;
-    double sRatio = (start - overS->fT) / denom;
-    double eRatio = (end - overS->fT) / denom;
-    *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
-    *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
-}
-
-bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
-                      const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
-        SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
-        SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
-    double coinTs, coinTe, oppTs, oppTe;
-    tRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
-    tRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
-    SkOpSegment* coinSeg = coinPtTStart->segment();
-    SkOpSegment* oppSeg = oppPtTStart->segment();
-    SkASSERT(coinSeg != oppSeg);
-    SkCoincidentSpans* check = this->fHead;
-    do {
-        const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
-        if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
-            continue;
-        }
-        const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
-        if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
-            continue;
-        }
-        int cTs = coinTs;
-        int cTe = coinTe;
-        int oTs = oppTs;
-        int oTe = oppTe;
-        if (checkCoinSeg != coinSeg) {
-            SkASSERT(checkOppSeg != oppSeg);
-            SkTSwap(cTs, oTs);
-            SkTSwap(cTe, oTe);
-        }
-        int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
-                       + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
-                       + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
-                       + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
-//        SkASSERT(tweenCount == 0 || tweenCount == 4);
-        if (tweenCount) {
-            return true;
-        }
-    } while ((check = check->fNext));
-    if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
-        SkTSwap(oppTs, oppTe);
-    }
-    if (coinTs > coinTe) {
-        SkTSwap(coinTs, coinTe);
-        SkTSwap(oppTs, oppTe);
-    }
-    SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
-    SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
-    if (cs == ce) {
-        return false;
-    }
-    SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator);
-    SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator);
-    SkASSERT(os != oe);
-    cs->addOpp(os);
-    ce->addOpp(oe);
-    this->add(cs, ce, os, oe, allocator);
-    return true;
-}
-
-bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
-    SkCoincidentSpans* outer = this->fHead;
-    if (!outer) {
-        return true;
-    }
-    do {
-        SkCoincidentSpans* inner = outer;
-        while ((inner = inner->fNext)) {
-            double overS, overE;
-            if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                    inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
-                if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                        inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
-                        outer->fOppPtTStart, outer->fOppPtTEnd,
-                        inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
-                    return false;
-                }
-            } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                    inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
-                if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                        inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
-                        outer->fOppPtTStart, outer->fOppPtTEnd,
-                        inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
-                    return false;
-                }
-            } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
-                    inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
-                if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
-                        inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
-                        outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                        inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
-                    return false;
-                }
-            } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
-                    inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
-                if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
-                        inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
-                        outer->fCoinPtTStart, outer->fCoinPtTEnd,
-                        inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
-                    return false;
-                }
-            }
-        }
-
-    } while ((outer = outer->fNext));
-    return true;
-}
-
-
-bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
-        SkOpPtT* oppPtTEnd, bool flipped) {
-    SkCoincidentSpans* coin = fHead;
-    if (!coin) {
-        return false;
-    }
-    do {
-        if (coin->fCoinPtTStart == coinPtTStart &&  coin->fCoinPtTEnd == coinPtTEnd
-                && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
-                && coin->fFlipped == flipped) {
-            return true;
-        }
-    } while ((coin = coin->fNext));
-    return false;
-}
-
-// walk span sets in parallel, moving winding from one to the other
-bool SkOpCoincidence::apply() {
-    SkCoincidentSpans* coin = fHead;
-    if (!coin) {
-        return true;
-    }
-    do {
-        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
-        SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
-        SkASSERT(start == start->starter(end));
-        bool flipped = coin->fFlipped;
-        SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
-        SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
-        SkASSERT(oStart == oStart->starter(oEnd));
-        SkOpSegment* segment = start->segment();
-        SkOpSegment* oSegment = oStart->segment();
-        bool operandSwap = segment->operand() != oSegment->operand();
-        if (flipped) {
-            do {
-                SkOpSpanBase* oNext = oStart->next();
-                if (oNext == oEnd) {
-                    break;
-                }
-                oStart = oNext->upCast();
-            } while (true);
-        }
-        bool isXor = segment->isXor();
-        bool oppXor = oSegment->isXor();
-        do {
-            int windValue = start->windValue();
-            int oWindValue = oStart->windValue();
-            int oppValue = start->oppValue();
-            int oOppValue = oStart->oppValue();
-            // winding values are added or subtracted depending on direction and wind type
-            // same or opposite values are summed depending on the operand value
-            if (windValue >= oWindValue) {
-                if (operandSwap) {
-                    SkTSwap(oWindValue, oOppValue);
-                }
-                if (flipped) {
-                    windValue -= oWindValue;
-                    oppValue -= oOppValue;
-                } else {
-                    windValue += oWindValue;
-                    oppValue += oOppValue;
-                }
-                if (isXor) {
-                    windValue &= 1;
-                }
-                if (oppXor) {
-                    oppValue &= 1;
-                }
-                oWindValue = oOppValue = 0;
-            } else {
-                if (operandSwap) {
-                    SkTSwap(windValue, oppValue);
-                }
-                if (flipped) {
-                    oWindValue -= windValue;
-                    oOppValue -= oppValue;
-                } else {
-                    oWindValue += windValue;
-                    oOppValue += oppValue;
-                }
-                if (isXor) {
-                    oOppValue &= 1;
-                }
-                if (oppXor) {
-                    oWindValue &= 1;
-                }
-                windValue = oppValue = 0;
-            }
-            start->setWindValue(windValue);
-            start->setOppValue(oppValue);
-            oStart->setWindValue(oWindValue);
-            oStart->setOppValue(oOppValue);
-            if (!windValue && !oppValue) {
-                segment->markDone(start);
-            }
-            if (!oWindValue && !oOppValue) {
-                oSegment->markDone(oStart);
-            }
-            SkOpSpanBase* next = start->next();
-            SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
-            if (next == end) {
-                break;
-            }
-            start = next->upCast();
-            if (!oNext) {
-                return false;
-            }
-            if (!oNext->upCastable()) {
-                return false;
-            }
-            oStart = oNext->upCast();
-        } while (true);
-    } while ((coin = coin->fNext));
-    return true;
-}
-
-void SkOpCoincidence::detach(SkCoincidentSpans* remove) {
-    SkCoincidentSpans* coin = fHead;
-    SkCoincidentSpans* prev = NULL;
-    SkCoincidentSpans* next;
-    do {
-        next = coin->fNext;
-        if (coin == remove) {
-            if (prev) {
-                prev->fNext = next;
-            } else {
-                fHead = next;
-            }
-            break;
-        }
-        prev = coin;
-    } while ((coin = next));
-    SkASSERT(coin);
-}
-
-void SkOpCoincidence::expand() {
-    SkCoincidentSpans* coin = fHead;
-    if (!coin) {
-        return;
-    }
-    do {
-        SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
-        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
-        SkOpSegment* segment = coin->fCoinPtTStart->segment();
-        SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
-        SkOpSpan* prev = start->prev();
-        SkOpPtT* oppPtT;
-        if (prev && (oppPtT = prev->contains(oppSegment))) {
-            double midT = (prev->t() + start->t()) / 2;
-            if (segment->isClose(midT, oppSegment)) {
-                coin->fCoinPtTStart = prev->ptT();
-                coin->fOppPtTStart = oppPtT;
-            }
-        }
-        SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next();
-        if (next && (oppPtT = next->contains(oppSegment))) {
-            double midT = (end->t() + next->t()) / 2;
-            if (segment->isClose(midT, oppSegment)) {
-                coin->fCoinPtTEnd = next->ptT();
-                coin->fOppPtTEnd = oppPtT;
-            }
-        }
-    } while ((coin = coin->fNext));
-}
-
-void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
-    SkCoincidentSpans* coin = fHead;
-    if (!coin) {
-        return;
-    }
-    do {
-        if (coin->fCoinPtTStart == deleted) {
-            if (coin->fCoinPtTEnd->span() == kept->span()) {
-                return this->detach(coin);
-            }
-            coin->fCoinPtTStart = kept;
-        }
-        if (coin->fCoinPtTEnd == deleted) {
-            if (coin->fCoinPtTStart->span() == kept->span()) {
-                return this->detach(coin);
-            }
-            coin->fCoinPtTEnd = kept;
-        }
-        if (coin->fOppPtTStart == deleted) {
-            if (coin->fOppPtTEnd->span() == kept->span()) {
-                return this->detach(coin);
-            }
-            coin->fOppPtTStart = kept;
-        }
-        if (coin->fOppPtTEnd == deleted) {
-            if (coin->fOppPtTStart->span() == kept->span()) {
-                return this->detach(coin);
-            }
-            coin->fOppPtTEnd = kept;
-        }
-    } while ((coin = coin->fNext));
-}
-
-void SkOpCoincidence::mark() {
-    SkCoincidentSpans* coin = fHead;
-    if (!coin) {
-        return;
-    }
-    do {
-        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
-        SkOpSpanBase* oldEnd = end;
-        SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
-        SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
-        SkOpSpanBase* oOldEnd = oEnd;
-        SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
-        bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
-        if (flipped) {
-            SkTSwap(oStart, oEnd);
-        }
-        SkOpSpanBase* next = start;
-        SkOpSpanBase* oNext = oStart;
-        // check to see if coincident span could be bigger
-
-        do {
-            next = next->upCast()->next();
-            oNext = flipped ? oNext->prev() : oNext->upCast()->next();
-            if (next == end || oNext == oEnd) {
-                break;
-            }
-            if (!next->containsCoinEnd(oNext)) {
-                next->insertCoinEnd(oNext);
-            }
-            SkOpSpan* nextSpan = next->upCast();
-            SkOpSpan* oNextSpan = oNext->upCast();
-            if (!nextSpan->containsCoincidence(oNextSpan)) {
-                nextSpan->insertCoincidence(oNextSpan);
-            }
-        } while (true);
-    } while ((coin = coin->fNext));
-}
-
-bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e, 
-        const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
-    if (coin1s->segment() != coin2s->segment()) {
-        return false;
-    }
-    *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
-    *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
-    return *overS < *overE;
-}
index b79b88be8811c8a130d8da6a48102b6d9bc19a6e..287bfd12d4f87a197adb61f4da61140c3cddcbfd 100644 (file)
@@ -19,8 +19,6 @@ struct SkCoincidentSpans {
     SkOpPtT* fOppPtTStart;
     SkOpPtT* fOppPtTEnd;
     bool fFlipped;
-
-    void dump() const;
 };
 
 class SkOpCoincidence {
@@ -30,27 +28,13 @@ public:
     }
 
     void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
-             SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
-    bool addMissing(SkChunkAlloc* allocator);
-    bool apply();
+             SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
+    void apply();
     bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
                   SkOpPtT* oppPtTEnd, bool flipped);
-    void detach(SkCoincidentSpans* );
     void dump() const;
-    void expand();
-    void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
     void mark();
 
-private:
-    bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
-                      const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
-                      SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
-                      SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
-                      SkChunkAlloc* allocator);
-    bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
-                 const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
-                 double* overS, double* overE) const;
-
     SkCoincidentSpans* fHead;
 };
 
index d17b18905beeba9a9c3cb86bc72a1d3901ed7fe6..28c072a3c1340200c2cd7f1b2514bd20b19e2c05 100644 (file)
@@ -4,35 +4,42 @@
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
+#include "SkIntersections.h"
 #include "SkOpContour.h"
-#include "SkOpTAllocator.h"
 #include "SkPathWriter.h"
-#include "SkReduceOrder.h"
 #include "SkTSort.h"
 
-void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator) {
-    switch (verb) {
-        case SkPath::kLine_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
-            memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
-            appendSegment(allocator).addLine(ptStorage, this);
-        } break;
-        case SkPath::kQuad_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
-            memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
-            appendSegment(allocator).addQuad(ptStorage, this);
-        } break;
-        case SkPath::kCubic_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
-            memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
-            appendSegment(allocator).addCubic(ptStorage, this);
-        } break;
-        default:
-            SkASSERT(0);
-    }
-}
-
-SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
+bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
+        const SkIntersections& ts, bool swap) {
+    SkPoint pt0 = ts.pt(0).asSkPoint();
+    SkPoint pt1 = ts.pt(1).asSkPoint();
+    if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
+        // FIXME: one could imagine a case where it would be incorrect to ignore this
+        // suppose two self-intersecting cubics overlap to be coincident --
+        // this needs to check that by some measure the t values are far enough apart
+        // or needs to check to see if the self-intersection bit was set on the cubic segment
+        return false;
+    }
+    SkCoincidence& coincidence = fCoincidences.push_back();
+    coincidence.fOther = other;
+    coincidence.fSegments[0] = index;
+    coincidence.fSegments[1] = otherIndex;
+    coincidence.fTs[swap][0] = ts[0][0];
+    coincidence.fTs[swap][1] = ts[0][1];
+    coincidence.fTs[!swap][0] = ts[1][0];
+    coincidence.fTs[!swap][1] = ts[1][1];
+    coincidence.fPts[swap][0] = pt0;
+    coincidence.fPts[swap][1] = pt1;
+    bool nearStart = ts.nearlySame(0);
+    bool nearEnd = ts.nearlySame(1);
+    coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
+    coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
+    coincidence.fNearly[0] = nearStart;
+    coincidence.fNearly[1] = nearEnd;
+    return true;
+}
+
+SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
     int segmentCount = fSortedSegments.count();
     SkASSERT(segmentCount > 0);
     for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@@ -40,27 +47,627 @@ SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase*
         if (testSegment->done()) {
             continue;
         }
-        SkOpSpanBase* span = testSegment->head();
-        SkOpSpanBase* testS, * testE;
-        while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
-            if (!testSegment->isVertical(testS, testE)) {
-                *start = testS;
-                *end = testE;
+        *start = *end = 0;
+        while (testSegment->nextCandidate(start, end)) {
+            if (!testSegment->isVertical(*start, *end)) {
                 return testSegment;
             }
-            span = span->upCast()->next();
         }
     }
     return NULL;
 }
 
+// if one is very large the smaller may have collapsed to nothing
+static void bump_out_close_span(double* startTPtr, double* endTPtr) {
+    double startT = *startTPtr;
+    double endT = *endTPtr;
+    if (approximately_negative(endT - startT)) {
+        if (endT <= 1 - FLT_EPSILON) {
+            *endTPtr += FLT_EPSILON;
+            SkASSERT(*endTPtr <= 1);
+        } else {
+            *startTPtr -= FLT_EPSILON;
+            SkASSERT(*startTPtr >= 0);
+        }
+    }
+}
+
+// first pass, add missing T values
+// second pass, determine winding values of overlaps
+void SkOpContour::addCoincidentPoints() {
+    int count = fCoincidences.count();
+    for (int index = 0; index < count; ++index) {
+        SkCoincidence& coincidence = fCoincidences[index];
+        int thisIndex = coincidence.fSegments[0];
+        SkOpSegment& thisOne = fSegments[thisIndex];
+        SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        SkOpSegment& other = otherContour->fSegments[otherIndex];
+        if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+            // OPTIMIZATION: remove from array
+            continue;
+        }
+    #if DEBUG_CONCIDENT
+        thisOne.debugShowTs("-");
+        other.debugShowTs("o");
+    #endif
+        double startT = coincidence.fTs[0][0];
+        double endT = coincidence.fTs[0][1];
+        bool startSwapped, oStartSwapped, cancelers;
+        if ((cancelers = startSwapped = startT > endT)) {
+            SkTSwap(startT, endT);
+        }
+        bump_out_close_span(&startT, &endT);
+        SkASSERT(!approximately_negative(endT - startT));
+        double oStartT = coincidence.fTs[1][0];
+        double oEndT = coincidence.fTs[1][1];
+        if ((oStartSwapped = oStartT > oEndT)) {
+            SkTSwap(oStartT, oEndT);
+            cancelers ^= true;
+        }
+        bump_out_close_span(&oStartT, &oEndT);
+        SkASSERT(!approximately_negative(oEndT - oStartT));
+        const SkPoint& startPt = coincidence.fPts[0][startSwapped];
+        if (cancelers) {
+            // make sure startT and endT have t entries
+            if (startT > 0 || oEndT < 1
+                    || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
+                thisOne.addTPair(startT, &other, oEndT, true, startPt,
+                        coincidence.fPts[1][startSwapped]);
+            }
+            const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
+            if (oStartT > 0 || endT < 1
+                    || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
+                other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
+                        coincidence.fPts[0][oStartSwapped]);
+            }
+        } else {
+            if (startT > 0 || oStartT > 0
+                    || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
+                thisOne.addTPair(startT, &other, oStartT, true, startPt,
+                        coincidence.fPts[1][startSwapped]);
+            }
+            const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
+            if (endT < 1 || oEndT < 1
+                    || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
+                other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
+                        coincidence.fPts[0][!oStartSwapped]);
+            }
+        }
+    #if DEBUG_CONCIDENT
+        thisOne.debugShowTs("+");
+        other.debugShowTs("o");
+    #endif
+    }
+    // if there are multiple pairs of coincidence that share an edge, see if the opposite
+    // are also coincident
+    for (int index = 0; index < count - 1; ++index) {
+        const SkCoincidence& coincidence = fCoincidences[index];
+        int thisIndex = coincidence.fSegments[0];
+        SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        for (int idx2 = 1; idx2 < count; ++idx2) {
+            const SkCoincidence& innerCoin = fCoincidences[idx2];
+            int innerThisIndex = innerCoin.fSegments[0];
+            if (thisIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
+            }
+            if (this == otherContour && otherIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
+            }
+            SkOpContour* innerOtherContour = innerCoin.fOther;
+            innerThisIndex = innerCoin.fSegments[1];
+            if (this == innerOtherContour && thisIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
+            }
+            if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
+            }
+        }
+    }
+}
+
+bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+        const SkIntersections& ts, int ptIndex, bool swap) {
+    SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
+    SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
+    if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
+        // FIXME: one could imagine a case where it would be incorrect to ignore this
+        // suppose two self-intersecting cubics overlap to form a partial coincidence --
+        // although it isn't clear why the regular coincidence could wouldn't pick this up
+        // this is exceptional enough to ignore for now
+        return false;
+    }
+    SkCoincidence& coincidence = fPartialCoincidences.push_back();
+    coincidence.fOther = other;
+    coincidence.fSegments[0] = index;
+    coincidence.fSegments[1] = otherIndex;
+    coincidence.fTs[swap][0] = ts[0][ptIndex];
+    coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
+    coincidence.fTs[!swap][0] = ts[1][ptIndex];
+    coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
+    coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
+    coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
+    coincidence.fNearly[0] = 0;
+    coincidence.fNearly[1] = 0;
+    return true;
+}
+
+void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
+        SkCoincidence* coincidence) {
+    for (int idx2 = 0; idx2 < 2; ++idx2) {
+        if (coincidence->fPts[0][idx2] == aligned.fOldPt
+                && coincidence->fTs[swap][idx2] == aligned.fOldT) {
+            SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
+            coincidence->fPts[0][idx2] = aligned.fPt;
+            SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
+            coincidence->fTs[swap][idx2] = aligned.fT;
+        }
+    }
+}
+
+void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
+        SkTArray<SkCoincidence, true>* coincidences) {
+    int count = coincidences->count();
+    for (int index = 0; index < count; ++index) {
+        SkCoincidence& coincidence = (*coincidences)[index];
+        int thisIndex = coincidence.fSegments[0];
+        const SkOpSegment* thisOne = &fSegments[thisIndex];
+        const SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        const SkOpSegment* other = &otherContour->fSegments[otherIndex];
+        if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
+            align(aligned, false, &coincidence);
+        } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
+            align(aligned, true, &coincidence);
+        }
+    }
+}
+
+void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex, 
+        bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
+    int zeroPt;
+    if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
+        alignPt(segmentIndex, point, zeroPt);
+    }
+    if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
+        other->alignPt(otherIndex, point, zeroPt);
+    }
+}
+
+void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
+    const SkOpSegment& segment = fSegments[index];
+    if (0 == zeroPt) {     
+        *point = segment.pts()[0];
+    } else {
+        *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+    }
+}
+
+int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
+    double tVal = (*ts)[swap][tIndex];
+    if (tVal != 0 && precisely_zero(tVal)) {
+        ts->set(swap, tIndex, 0);
+        return 0;
+    } 
+     if (tVal != 1 && precisely_equal(tVal, 1)) {
+        ts->set(swap, tIndex, 1);
+        return 1;
+    }
+    return -1;
+}
+
+bool SkOpContour::calcAngles() {
+    int segmentCount = fSegments.count();
+    for (int test = 0; test < segmentCount; ++test) {
+        if (!fSegments[test].calcAngles()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkOpContour::calcCoincidentWinding() {
+    int count = fCoincidences.count();
+#if DEBUG_CONCIDENT
+    if (count > 0) {
+        SkDebugf("%s count=%d\n", __FUNCTION__, count);
+    }
+#endif
+    for (int index = 0; index < count; ++index) {
+        SkCoincidence& coincidence = fCoincidences[index];
+        if (!calcCommonCoincidentWinding(coincidence)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void SkOpContour::calcPartialCoincidentWinding() {
+    int count = fPartialCoincidences.count();
+#if DEBUG_CONCIDENT
+    if (count > 0) {
+        SkDebugf("%s count=%d\n", __FUNCTION__, count);
+    }
+#endif
+    for (int index = 0; index < count; ++index) {
+        SkCoincidence& coincidence = fPartialCoincidences[index];
+        calcCommonCoincidentWinding(coincidence);
+    }
+    // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
+    // are also coincident
+    for (int index = 0; index < count - 1; ++index) {
+        const SkCoincidence& coincidence = fPartialCoincidences[index];
+        int thisIndex = coincidence.fSegments[0];
+        SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        for (int idx2 = 1; idx2 < count; ++idx2) {
+            const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
+            int innerThisIndex = innerCoin.fSegments[0];
+            if (thisIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
+            }
+            if (this == otherContour && otherIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
+            }
+            SkOpContour* innerOtherContour = innerCoin.fOther;
+            innerThisIndex = innerCoin.fSegments[1];
+            if (this == innerOtherContour && thisIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
+            }
+            if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+                checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
+            }
+        }
+    }
+}
+
+void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
+        const SkCoincidence& twoCoin, int twoIdx, bool partial) {
+    SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
+    SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
+    // look for common overlap
+    double min = SK_ScalarMax;
+    double max = SK_ScalarMin;
+    double min1 = oneCoin.fTs[!oneIdx][0];
+    double max1 = oneCoin.fTs[!oneIdx][1];
+    double min2 = twoCoin.fTs[!twoIdx][0];
+    double max2 = twoCoin.fTs[!twoIdx][1];
+    bool cancelers = (min1 < max1) != (min2 < max2);
+    if (min1 > max1) {
+        SkTSwap(min1, max1);
+    }
+    if (min2 > max2) {
+        SkTSwap(min2, max2);
+    }
+    if (between(min1, min2, max1)) {
+        min = min2;
+    }
+    if (between(min1, max2, max1)) {
+        max = max2;
+    }
+    if (between(min2, min1, max2)) {
+        min = SkTMin(min, min1);
+    }
+    if (between(min2, max1, max2)) {
+        max = SkTMax(max, max1);
+    }
+    if (min >= max) {
+        return;  // no overlap
+    }
+    // look to see if opposite are different segments
+    int seg1Index = oneCoin.fSegments[oneIdx];
+    int seg2Index = twoCoin.fSegments[twoIdx];
+    if (seg1Index == seg2Index) {
+        return;
+    }
+    SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
+    SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
+    SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
+    SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
+    // find opposite t value ranges corresponding to reference min/max range
+    const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
+    const int refSegIndex = oneCoin.fSegments[!oneIdx];
+    const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
+    int seg1Start = segment1->findOtherT(min, refSegment);
+    int seg1End = segment1->findOtherT(max, refSegment);
+    int seg2Start = segment2->findOtherT(min, refSegment);
+    int seg2End = segment2->findOtherT(max, refSegment);
+    // if the opposite pairs already contain min/max, we're done
+    if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
+        return;
+    }
+    double loEnd = SkTMin(min1, min2);
+    double hiEnd = SkTMax(max1, max2);
+    // insert the missing coincident point(s)
+    double missingT1 = -1;
+    double otherT1 = -1;
+    if (seg1Start < 0) {
+        if (seg2Start < 0) {
+            return;
+        }
+        missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+                segment2, seg1End);
+        if (missingT1 < 0) {
+            return;
+        }
+        const SkOpSpan* missingSpan = &segment2->span(seg2Start);
+        otherT1 = missingSpan->fT;
+    } else if (seg2Start < 0) {
+        SkASSERT(seg1Start >= 0);
+        missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+                segment1, seg2End);
+        if (missingT1 < 0) {
+            return;
+        }
+        const SkOpSpan* missingSpan = &segment1->span(seg1Start);
+        otherT1 = missingSpan->fT;
+    }
+    SkPoint missingPt1;
+    SkOpSegment* addTo1 = NULL;
+    SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
+    int minTIndex = refSegment->findExactT(min, addOther1);
+    SkASSERT(minTIndex >= 0);
+    if (missingT1 >= 0) {
+        missingPt1 = refSegment->span(minTIndex).fPt;
+        addTo1 = seg1Start < 0 ? segment1 : segment2;
+    }
+    double missingT2 = -1;
+    double otherT2 = -1;
+    if (seg1End < 0) {
+        if (seg2End < 0) {
+            return;
+        }
+        missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+                segment2, seg1Start);
+        if (missingT2 < 0) {
+            return;
+        }
+        const SkOpSpan* missingSpan = &segment2->span(seg2End);
+        otherT2 = missingSpan->fT;
+    } else if (seg2End < 0) {
+        SkASSERT(seg1End >= 0);
+        missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+                segment1, seg2Start);
+        if (missingT2 < 0) {
+            return;
+        }
+        const SkOpSpan* missingSpan = &segment1->span(seg1End);
+        otherT2 = missingSpan->fT;
+    }
+    SkPoint missingPt2;
+    SkOpSegment* addTo2 = NULL;
+    SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
+    int maxTIndex = refSegment->findExactT(max, addOther2);
+    SkASSERT(maxTIndex >= 0);
+    if (missingT2 >= 0) {
+        missingPt2 = refSegment->span(maxTIndex).fPt;
+        addTo2 = seg1End < 0 ? segment1 : segment2;
+    }
+    if (missingT1 >= 0) {
+        addTo1->pinT(missingPt1, &missingT1);
+        addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
+    } else {
+        SkASSERT(minTIndex >= 0);
+        missingPt1 = refSegment->span(minTIndex).fPt;
+    }
+    if (missingT2 >= 0) {
+        addTo2->pinT(missingPt2, &missingT2);
+        addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
+    } else {
+        SkASSERT(minTIndex >= 0);
+        missingPt2 = refSegment->span(maxTIndex).fPt;
+    }
+    if (!partial) {
+        return;
+    }
+    if (cancelers) {
+        if (missingT1 >= 0) {
+            if (addTo1->reversePoints(missingPt1, missingPt2)) {
+                SkTSwap(missingPt1, missingPt2);
+            }
+            addTo1->addTCancel(missingPt1, missingPt2, addOther1);
+        } else {
+            if (addTo2->reversePoints(missingPt1, missingPt2)) {
+                SkTSwap(missingPt1, missingPt2);
+            }
+            addTo2->addTCancel(missingPt1, missingPt2, addOther2);
+        }
+    } else if (missingT1 >= 0) {
+        SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
+                addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
+    } else {
+        SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
+                addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
+    }
+}
+
+void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
+    int count = coincidences.count();
+#if DEBUG_CONCIDENT
+    if (count > 0) {
+        SkDebugf("%s count=%d\n", __FUNCTION__, count);
+    }
+#endif
+    // look for a lineup where the partial implies another adjoining coincidence
+    for (int index = 0; index < count; ++index) {
+        const SkCoincidence& coincidence = coincidences[index];
+        int thisIndex = coincidence.fSegments[0];
+        SkOpSegment& thisOne = fSegments[thisIndex];
+        if (thisOne.done()) {
+            continue;
+        }
+        SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        SkOpSegment& other = otherContour->fSegments[otherIndex];
+        if (other.done()) {
+            continue;
+        }
+        double startT = coincidence.fTs[0][0];
+        double endT = coincidence.fTs[0][1];
+        if (startT == endT) {  // this can happen in very large compares
+            continue;
+        }
+        double oStartT = coincidence.fTs[1][0];
+        double oEndT = coincidence.fTs[1][1];
+        if (oStartT == oEndT) {
+            continue;
+        }
+        bool swapStart = startT > endT;
+        bool swapOther = oStartT > oEndT;
+        const SkPoint* startPt = &coincidence.fPts[0][0];
+        const SkPoint* endPt = &coincidence.fPts[0][1];
+        if (swapStart) {
+            SkTSwap(startT, endT);
+            SkTSwap(oStartT, oEndT);
+            SkTSwap(startPt, endPt);
+        }
+        bool cancel = swapOther != swapStart;
+        int step = swapStart ? -1 : 1;
+        int oStep = swapOther ? -1 : 1;
+        double oMatchStart = cancel ? oEndT : oStartT;
+        if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
+            bool added = false;
+            if (oMatchStart != 0) {
+                const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
+                added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
+            }
+            if (!cancel && startT != 0 && !added) {
+                (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
+            }
+        }
+        double oMatchEnd = cancel ? oStartT : oEndT;
+        if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
+            bool added = false;
+            if (cancel && endT != 1 && !added) {
+                (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
+            }
+        }
+    }
+}
+
+bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
+    if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
+        return true;
+    }
+    int thisIndex = coincidence.fSegments[0];
+    SkOpSegment& thisOne = fSegments[thisIndex];
+    if (thisOne.done()) {
+        return true;
+    }
+    SkOpContour* otherContour = coincidence.fOther;
+    int otherIndex = coincidence.fSegments[1];
+    SkOpSegment& other = otherContour->fSegments[otherIndex];
+    if (other.done()) {
+        return true;
+    }
+    double startT = coincidence.fTs[0][0];
+    double endT = coincidence.fTs[0][1];
+    const SkPoint* startPt = &coincidence.fPts[0][0];
+    const SkPoint* endPt = &coincidence.fPts[0][1];
+    bool cancelers;
+    if ((cancelers = startT > endT)) {
+        SkTSwap<double>(startT, endT);
+        SkTSwap<const SkPoint*>(startPt, endPt);
+    }
+    bump_out_close_span(&startT, &endT);
+    SkASSERT(!approximately_negative(endT - startT));
+    double oStartT = coincidence.fTs[1][0];
+    double oEndT = coincidence.fTs[1][1];
+    if (oStartT > oEndT) {
+        SkTSwap<double>(oStartT, oEndT);
+        cancelers ^= true;
+    }
+    bump_out_close_span(&oStartT, &oEndT);
+    SkASSERT(!approximately_negative(oEndT - oStartT));
+    bool success = true;
+    if (cancelers) {
+        thisOne.addTCancel(*startPt, *endPt, &other);
+    } else {
+        success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
+    }
+#if DEBUG_CONCIDENT
+    thisOne.debugShowTs("p");
+    other.debugShowTs("o");
+#endif
+    return success;
+}
+
+void SkOpContour::resolveNearCoincidence() {
+    int count = fCoincidences.count();
+    for (int index = 0; index < count; ++index) {
+        SkCoincidence& coincidence = fCoincidences[index];
+        if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
+            continue;
+        }
+        int thisIndex = coincidence.fSegments[0];
+        SkOpSegment& thisOne = fSegments[thisIndex];
+        SkOpContour* otherContour = coincidence.fOther;
+        int otherIndex = coincidence.fSegments[1];
+        SkOpSegment& other = otherContour->fSegments[otherIndex];
+        if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+            // OPTIMIZATION: remove from coincidence array
+            continue;
+        }
+    #if DEBUG_CONCIDENT
+        thisOne.debugShowTs("-");
+        other.debugShowTs("o");
+    #endif
+        double startT = coincidence.fTs[0][0];
+        double endT = coincidence.fTs[0][1];
+        bool cancelers;
+        if ((cancelers = startT > endT)) {
+            SkTSwap<double>(startT, endT);
+        }
+        if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
+            if (endT <= 1 - FLT_EPSILON) {
+                endT += FLT_EPSILON;
+                SkASSERT(endT <= 1);
+            } else {
+                startT -= FLT_EPSILON;
+                SkASSERT(startT >= 0);
+            }
+        }
+        SkASSERT(!approximately_negative(endT - startT));
+        double oStartT = coincidence.fTs[1][0];
+        double oEndT = coincidence.fTs[1][1];
+        if (oStartT > oEndT) {
+            SkTSwap<double>(oStartT, oEndT);
+            cancelers ^= true;
+        }
+        SkASSERT(!approximately_negative(oEndT - oStartT));
+        if (cancelers) {
+            thisOne.blindCancel(coincidence, &other);
+        } else {
+            thisOne.blindCoincident(coincidence, &other);
+        }
+    }
+}
+
+void SkOpContour::sortAngles() {
+    int segmentCount = fSegments.count();
+    for (int test = 0; test < segmentCount; ++test) {
+        fSegments[test].sortAngles();
+    }
+}
+
+void SkOpContour::sortSegments() {
+    int segmentCount = fSegments.count();
+    fSortedSegments.push_back_n(segmentCount);
+    for (int test = 0; test < segmentCount; ++test) {
+        fSortedSegments[test] = &fSegments[test];
+    }
+    SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+    fFirstSorted = 0;
+}
+
 void SkOpContour::toPath(SkPathWriter* path) const {
-    const SkPoint& pt = fHead.pts()[0];
+    int segmentCount = fSegments.count();
+    const SkPoint& pt = fSegments.front().pts()[0];
     path->deferredMove(pt);
-    const SkOpSegment* segment = &fHead;
-    do {
-        segment->addCurveTo(segment->head(), segment->tail(), path, true);
-    } while ((segment = segment->next()));
+    for (int test = 0; test < segmentCount; ++test) {
+        fSegments[test].addCurveTo(0, 1, path, true);
+    }
     path->close();
 }
 
@@ -99,14 +706,57 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
     }
 }
 
-SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
-    SkOpSegment* segment = &fHead;
-    do {
-        if (segment->done()) {
+SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
+    int segmentCount = fSegments.count();
+    for (int test = 0; test < segmentCount; ++test) {
+        SkOpSegment* testSegment = &fSegments[test];
+        if (testSegment->done()) {
             continue;
         }
-        segment->undoneSpan(startPtr, endPtr);
-        return segment;
-    } while ((segment = segment->next()));
+        testSegment->undoneSpan(start, end);
+        return testSegment;
+    }
     return NULL;
 }
+
+#if DEBUG_SHOW_WINDING
+int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
+    int count = fSegments.count();
+    int sum = 0;
+    for (int index = 0; index < count; ++index) {
+        sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
+    }
+//      SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
+    return sum;
+}
+
+void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
+//     int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
+//    int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
+    int ofInterest = 1 << 5 | 1 << 8;
+    int total = 0;
+    int index;
+    for (index = 0; index < contourList.count(); ++index) {
+        total += contourList[index]->segments().count();
+    }
+    int sum = 0;
+    for (index = 0; index < contourList.count(); ++index) {
+        sum += contourList[index]->debugShowWindingValues(total, ofInterest);
+    }
+//       SkDebugf("%s total=%d\n", __FUNCTION__, sum);
+}
+#endif
+
+void SkOpContour::setBounds() {
+    int count = fSegments.count();
+    if (count == 0) {
+        SkDebugf("%s empty contour\n", __FUNCTION__);
+        SkASSERT(0);
+        // FIXME: delete empty contour?
+        return;
+    }
+    fBounds = fSegments.front().bounds();
+    for (int index = 1; index < count; ++index) {
+        fBounds.add(fSegments[index].bounds());
+    }
+}
index be1f59f4bbe2a5fa73f51730c01b646285fe3eea..7a1cc09247a49bb614ce1ebbf7ba7f7711368529 100644 (file)
@@ -8,16 +8,31 @@
 #define SkOpContour_DEFINED
 
 #include "SkOpSegment.h"
-#include "SkTDArray.h"
-#include "SkTSort.h"
+#include "SkTArray.h"
 
-class SkChunkAlloc;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+#include "SkThread.h"
+#endif
+
+class SkIntersections;
+class SkOpContour;
 class SkPathWriter;
 
+struct SkCoincidence {
+    SkOpContour* fOther;
+    int fSegments[2];
+    double fTs[2][2];
+    SkPoint fPts[2][2];
+    int fNearly[2];
+};
+
 class SkOpContour {
 public:
     SkOpContour() {
         reset();
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+        fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
+#endif
     }
 
     bool operator<(const SkOpContour& rh) const {
@@ -26,255 +41,211 @@ public:
                 : fBounds.fTop < rh.fBounds.fTop;
     }
 
-    void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
-        appendSegment(allocator).addCubic(pts, this);
-    }
-
-    void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
-
-    void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
-        appendSegment(allocator).addLine(pts, this);
-    }
-
-    void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
-        appendSegment(allocator).addQuad(pts, this);
-    }
-
-    void align() {
-        SkASSERT(fCount > 0);
-        SkOpSegment* segment = &fHead;
-        do {
-            segment->align();
-        } while ((segment = segment->next()));
-    }
-
-    SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
-        SkOpSegment* result = fCount++
-                ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
-        result->setPrev(fTail);
-        if (fTail) {
-            fTail->setNext(result);
-        }
-        fTail = result;
-        return *result;
-    }
+    bool addCoincident(int index, SkOpContour* other, int otherIndex,
+                       const SkIntersections& ts, bool swap);
+    void addCoincidentPoints();
 
-    SkOpContour* appendContour(SkChunkAlloc* allocator) {
-        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
-        
-        SkOpContour* prev = this;
-        SkOpContour* next;
-        while ((next = prev->next())) {
-            prev = next;
+    void addCross(const SkOpContour* crosser) {
+#ifdef DEBUG_CROSS
+        for (int index = 0; index < fCrosses.count(); ++index) {
+            SkASSERT(fCrosses[index] != crosser);
         }
-        prev->setNext(contour);
-        return contour;
-    }
-    
-    const SkPathOpsBounds& bounds() const {
-        return fBounds;
+#endif
+        fCrosses.push_back(crosser);
     }
 
-    void calcAngles(SkChunkAlloc* allocator) {
-        SkASSERT(fCount > 0);
-        SkOpSegment* segment = &fHead;
-        do {
-            segment->calcAngles(allocator);
-        } while ((segment = segment->next()));
+    void addCubic(const SkPoint pts[4]) {
+        fSegments.push_back().addCubic(pts, fOperand, fXor);
+        fContainsCurves = fContainsCubics = true;
     }
 
-    void complete() {
-        setBounds();
+    int addLine(const SkPoint pts[2]) {
+        fSegments.push_back().addLine(pts, fOperand, fXor);
+        return fSegments.count();
     }
 
-    int count() const {
-        return fCount;
+    void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
+        fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
     }
 
-    int debugID() const {
-        return PATH_OPS_DEBUG_RELEASE(fID, -1);
-    }
+    bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+                       const SkIntersections& ts, int ptIndex, bool swap);
 
-    int debugIndent() const {
-        return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
+    int addQuad(const SkPoint pts[3]) {
+        fSegments.push_back().addQuad(pts, fOperand, fXor);
+        fContainsCurves = true;
+        return fSegments.count();
     }
 
-#if DEBUG_ACTIVE_SPANS
-    void debugShowActiveSpans() {
-        SkOpSegment* segment = &fHead;
-        do {
-            segment->debugShowActiveSpans();
-        } while ((segment = segment->next()));
+    int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
+        setContainsIntercepts();
+        return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
     }
-#endif
 
-    const SkOpAngle* debugAngle(int id) const {
-        return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
+    int addSelfT(int segIndex, const SkPoint& pt, double newT) {
+        setContainsIntercepts();
+        return fSegments[segIndex].addSelfT(pt, newT);
     }
 
-    SkOpContour* debugContour(int id) {
-        return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
-    }
+    void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
+    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
+            SkTArray<SkCoincidence, true>* coincidences);
 
-    const SkOpPtT* debugPtT(int id) const {
-        return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
+    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
+        alignCoincidence(aligned, &fCoincidences);
+        alignCoincidence(aligned, &fPartialCoincidences);
     }
 
-    const SkOpSegment* debugSegment(int id) const {
-        return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
+    void alignMultiples(SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment& segment = fSegments[sIndex];
+            if (segment.hasMultiples()) {
+                segment.alignMultiples(aligned);
+            }
+        }
     }
 
-    const SkOpSpanBase* debugSpan(int id) const {
-        return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
-    }
+    void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
+                  bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
 
-    SkOpGlobalState* globalState() const {
-        return fState; 
+    const SkPathOpsBounds& bounds() const {
+        return fBounds;
     }
 
-    void debugValidate() const {
-#if DEBUG_VALIDATE
-        const SkOpSegment* segment = &fHead;
-        const SkOpSegment* prior = NULL;
-        do {
-            segment->debugValidate();
-            SkASSERT(segment->prev() == prior);
-            prior = segment;
-        } while ((segment = segment->next()));
-        SkASSERT(prior == fTail);
-#endif
-    }
+    bool calcAngles();
+    bool calcCoincidentWinding();
+    void calcPartialCoincidentWinding();
 
-    bool done() const {
-        return fDone;
+    void checkDuplicates() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment& segment = fSegments[sIndex];
+            if (segment.count() > 2) {
+                segment.checkDuplicates();
+            }
+        }
     }
 
-    void dump();
-    void dumpAll();
-    void dumpAngles() const;
-    void dumpPt(int ) const;
-    void dumpPts() const;
-    void dumpPtsX() const;
-    void dumpSegment(int ) const;
-    void dumpSegments(SkPathOp op) const;
-    void dumpSpan(int ) const;
-    void dumpSpans() const;
-
-    const SkPoint& end() const {
-        return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
+    bool checkEnds() {
+        if (!fContainsCurves) {
+            return true;
+        }
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment* segment = &fSegments[sIndex];
+            if (segment->verb() == SkPath::kLine_Verb) {
+                continue;
+            }
+            if (segment->done()) {
+                continue;   // likely coincident, nothing to do
+            }
+            if (!segment->checkEnds()) {
+                return false;
+            }
+        }
+        return true;
     }
 
-    SkOpSegment* first() {
-        SkASSERT(fCount > 0);
-        return &fHead;
+    void checkMultiples() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment& segment = fSegments[sIndex];
+            if (segment.count() > 2) {
+                segment.checkMultiples();
+                fMultiples |= segment.hasMultiples();
+            }
+        }
     }
 
-    const SkOpSegment* first() const {
-        SkASSERT(fCount > 0);
-        return &fHead;
+    void checkSmall() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment& segment = fSegments[sIndex];
+            // OPTIMIZATION : skip segments that are done?
+            if (segment.hasSmall()) {
+                segment.checkSmall();
+            }
+        }
     }
 
-    void indentDump() {
-        PATH_OPS_DEBUG_CODE(fIndent += 2);
+    // if same point has different T values, choose a common T
+    void checkTiny() {
+        int segmentCount = fSegments.count();
+        if (segmentCount <= 2) {
+            return;
+        }
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            SkOpSegment& segment = fSegments[sIndex];
+            if (segment.hasTiny()) {
+                segment.checkTiny();
+            }
+        }
     }
 
-    void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
-        fState = globalState;
-        fOperand = operand;
-        fXor = isXor;
+    void complete() {
+        setBounds();
+        fContainsIntercepts = false;
     }
 
-    bool isXor() const {
-        return fXor;
+    bool containsCubics() const {
+        return fContainsCubics;
     }
 
-    void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
-        SkASSERT(fCount > 0);
-        SkOpSegment* segment = &fHead;
-        do {
-            if (fState->angleCoincidence()) {
-                segment->checkAngleCoin(coincidences, allocator);
-            } else {
-                segment->missingCoincidence(coincidences, allocator);
+    bool crosses(const SkOpContour* crosser) const {
+        for (int index = 0; index < fCrosses.count(); ++index) {
+            if (fCrosses[index] == crosser) {
+                return true;
             }
-        } while ((segment = segment->next()));
+        }
+        return false;
     }
 
-    bool moveNearby() {
-        SkASSERT(fCount > 0);
-        SkOpSegment* segment = &fHead;
-        do {
-            if (!segment->moveNearby()) {
-                return false;
-            }
-        } while ((segment = segment->next()));
-        return true;
+    bool done() const {
+        return fDone;
     }
 
-    SkOpContour* next() {
-        return fNext;
+    const SkPoint& end() const {
+        const SkOpSegment& segment = fSegments.back();
+        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
     }
 
-    const SkOpContour* next() const {
-        return fNext;
+    void fixOtherTIndex() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            fSegments[sIndex].fixOtherTIndex();
+        }
     }
 
-    SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
-
-    bool operand() const {
-        return fOperand;
+    bool hasMultiples() const {
+        return fMultiples;
     }
 
-    bool oppXor() const {
-        return fOppXor;
+    void joinCoincidence() {
+        joinCoincidence(fCoincidences, false);
+        joinCoincidence(fPartialCoincidences, true);
     }
 
-    void outdentDump() {
-        PATH_OPS_DEBUG_CODE(fIndent -= 2);
-    }
+    SkOpSegment* nonVerticalSegment(int* start, int* end);
 
-    void remove(SkOpContour* contour) {
-        if (contour == this) {
-            SkASSERT(fCount == 0);
-            return;
-        }
-        SkASSERT(contour->fNext == NULL);
-        SkOpContour* prev = this;
-        SkOpContour* next;
-        while ((next = prev->next()) != contour) {
-            SkASSERT(next);
-            prev = next;
-        }
-        SkASSERT(prev);
-        prev->setNext(NULL);
+    bool operand() const {
+        return fOperand;
     }
 
     void reset() {
-        fTail = NULL;
-        fNext = NULL;
-        fCount = 0;
-        fDone = false;
-        SkDEBUGCODE(fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin));
-        SkDEBUGCODE(fFirstSorted = -1);
-        PATH_OPS_DEBUG_CODE(fIndent = 0);
-    }
-
-    void setBounds() {
-        SkASSERT(fCount > 0);
-        const SkOpSegment* segment = &fHead;
-        fBounds = segment->bounds();
-        while ((segment = segment->next())) {
-            fBounds.add(segment->bounds());
-        }
+        fSegments.reset();
+        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
     }
 
-    void setGlobalState(SkOpGlobalState* state) {
-        fState = state;
+    void resolveNearCoincidence();
+
+    SkTArray<SkOpSegment>& segments() {
+        return fSegments;
     }
 
-    void setNext(SkOpContour* contour) {
-        SkASSERT(!fNext == !!contour);
-        fNext = contour;
+    void setContainsIntercepts() {
+        fContainsIntercepts = true;
     }
 
     void setOperand(bool isOp) {
@@ -283,68 +254,107 @@ public:
 
     void setOppXor(bool isOppXor) {
         fOppXor = isOppXor;
+        int segmentCount = fSegments.count();
+        for (int test = 0; test < segmentCount; ++test) {
+            fSegments[test].setOppXor(isOppXor);
+        }
     }
 
     void setXor(bool isXor) {
         fXor = isXor;
     }
 
-    SkPath::Verb simplifyCubic(SkPoint pts[4]);
-
-    void sortAngles() {
-        SkASSERT(fCount > 0);
-        SkOpSegment* segment = &fHead;
-        do {
-            segment->sortAngles();
-        } while ((segment = segment->next()));
-    }
-
-    void sortSegments() {
-        SkOpSegment* segment = &fHead;
-        do {
-            *fSortedSegments.append() = segment;
-        } while ((segment = segment->next()));
-        SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
-        fFirstSorted = 0;
-    }
+    void sortAngles();
+    void sortSegments();
 
     const SkPoint& start() const {
-        return fHead.pts()[0];
+        return fSegments.front().pts()[0];
     }
 
+    void toPath(SkPathWriter* path) const;
+
     void toPartialBackward(SkPathWriter* path) const {
-        const SkOpSegment* segment = fTail;
-        do {
-            segment->addCurveTo(segment->tail(), segment->head(), path, true);
-        } while ((segment = segment->prev()));
+        int segmentCount = fSegments.count();
+        for (int test = segmentCount - 1; test >= 0; --test) {
+            fSegments[test].addCurveTo(1, 0, path, true);
+        }
     }
 
     void toPartialForward(SkPathWriter* path) const {
-        const SkOpSegment* segment = &fHead;
-        do {
-            segment->addCurveTo(segment->head(), segment->tail(), path, true);
-        } while ((segment = segment->next()));
+        int segmentCount = fSegments.count();
+        for (int test = 0; test < segmentCount; ++test) {
+            fSegments[test].addCurveTo(0, 1, path, true);
+        }
     }
 
-    void toPath(SkPathWriter* path) const;
     void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
-    SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
+    SkOpSegment* undoneSegment(int* start, int* end);
+
+    int updateSegment(int index, const SkPoint* pts) {
+        SkOpSegment& segment = fSegments[index];
+        segment.updatePts(pts);
+        return SkPathOpsVerbToPoints(segment.verb()) + 1;
+    }
+
+#if DEBUG_TEST
+    SkTArray<SkOpSegment>& debugSegments() {
+        return fSegments;
+    }
+#endif
+
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+    void debugShowActiveSpans() {
+        for (int index = 0; index < fSegments.count(); ++index) {
+            fSegments[index].debugShowActiveSpans();
+        }
+    }
+#endif
+
+#if DEBUG_SHOW_WINDING
+    int debugShowWindingValues(int totalSegments, int ofInterest);
+    static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
+#endif
+
+    // available to test routines only
+    void dump() const;
+    void dumpAngles() const;
+    void dumpCoincidence(const SkCoincidence& ) const;
+    void dumpCoincidences() const;
+    void dumpPt(int ) const;
+    void dumpPts() const;
+    void dumpSpan(int ) const;
+    void dumpSpans() const;
 
 private:
-    SkOpGlobalState* fState;
-    SkOpSegment fHead;
-    SkOpSegment* fTail;
-    SkOpContour* fNext;
-    SkTDArray<SkOpSegment*> fSortedSegments;  // set by find top segment
-    SkPathOpsBounds fBounds;
-    int fCount;
+    void alignPt(int index, SkPoint* point, int zeroPt) const;
+    int alignT(bool swap, int tIndex, SkIntersections* ts) const;
+    bool calcCommonCoincidentWinding(const SkCoincidence& );
+    void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
+                             const SkCoincidence& twoCoin, int twoIdx, bool partial);
+    void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
+    void setBounds();
+
+    SkTArray<SkOpSegment> fSegments;
+    SkTArray<SkOpSegment*, true> fSortedSegments;
     int fFirstSorted;
-    bool fDone;  // set by find top segment
+    SkTArray<SkCoincidence, true> fCoincidences;
+    SkTArray<SkCoincidence, true> fPartialCoincidences;
+    SkTArray<const SkOpContour*, true> fCrosses;
+    SkPathOpsBounds fBounds;
+    bool fContainsIntercepts;  // FIXME: is this used by anybody?
+    bool fContainsCubics;
+    bool fContainsCurves;
+    bool fDone;
+    bool fMultiples;  // set if some segment has multiple identical intersections with other curves
     bool fOperand;  // true for the second argument to a binary operator
-    bool fXor;  // set if original path had even-odd fill
-    bool fOppXor;  // set if opposite path had even-odd fill
-    PATH_OPS_DEBUG_CODE(int fID);
-    PATH_OPS_DEBUG_CODE(int fIndent);
+    bool fXor;
+    bool fOppXor;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+    int debugID() const { return fID; }
+    int fID;
+#else
+    int debugID() const { return -1; }
+#endif
 };
 
 #endif
index bd21d729b6ec1467c0c2b3fa2b9b445e2e1542fe..803a5f4739e238a5426cc7c4fec513cca1faf879 100644 (file)
@@ -9,7 +9,7 @@
 #include "SkReduceOrder.h"
 
 void SkOpEdgeBuilder::init() {
-    fCurrentContour = fContoursHead;
+    fCurrentContour = NULL;
     fOperand = false;
     fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
@@ -19,43 +19,32 @@ void SkOpEdgeBuilder::init() {
 
 void SkOpEdgeBuilder::addOperand(const SkPath& path) {
     SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
-    fPathVerbs.pop();
+    fPathVerbs.pop_back();
     fPath = &path;
     fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
     preFetch();
 }
 
-int SkOpEdgeBuilder::count() const {
-    SkOpContour* contour = fContoursHead;
-    int count = 0;
-    while (contour) {
-        count += contour->count() > 0;
-        contour = contour->next();
-    }
-    return count;
-}
-
-bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
-    fOperand = false;
-    if (fUnparseable || !walk(allocator)) {
+bool SkOpEdgeBuilder::finish() {
+    if (fUnparseable || !walk()) {
         return false;
     }
     complete();
-    if (fCurrentContour && !fCurrentContour->count()) {
-        fContoursHead->remove(fCurrentContour);
+    if (fCurrentContour && !fCurrentContour->segments().count()) {
+        fContours.pop_back();
     }
     return true;
 }
 
 void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
     if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
-        *fPathVerbs.append() = SkPath::kLine_Verb;
-        *fPathPts.append() = curveStart;
+        fPathVerbs.push_back(SkPath::kLine_Verb);
+        fPathPts.push_back_n(1, &curveStart);
     } else {
         fPathPts[fPathPts.count() - 1] = curveStart;
     }
-    *fPathVerbs.append() = SkPath::kClose_Verb;
+    fPathVerbs.push_back(SkPath::kClose_Verb);
 }
 
 // very tiny points cause numerical instability : don't allow them
@@ -68,6 +57,7 @@ static void force_small_to_zero(SkPoint* pt) {
     }
 }
 
+
 int SkOpEdgeBuilder::preFetch() {
     if (!fPath->isFinite()) {
         fUnparseable = true;
@@ -88,18 +78,18 @@ int SkOpEdgeBuilder::preFetch() {
                 if (!fAllowOpenContours && lastCurve) {
                     closeContour(curve[0], curveStart);
                 }
-                *fPathVerbs.append() = verb;
+                fPathVerbs.push_back(verb);
                 force_small_to_zero(&pts[0]);
-                *fPathPts.append() = pts[0];
+                fPathPts.push_back(pts[0]);
                 curveStart = curve[0] = pts[0];
                 lastCurve = false;
                 continue;
             case SkPath::kLine_Verb:
                 force_small_to_zero(&pts[1]);
                 if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
-                    uint8_t lastVerb = fPathVerbs.top();
+                    uint8_t lastVerb = fPathVerbs.back();
                     if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
-                        fPathPts.top() = pts[1];
+                        fPathPts.back() = pts[1];
                     }
                     continue;  // skip degenerate points
                 }
@@ -119,9 +109,9 @@ int SkOpEdgeBuilder::preFetch() {
                             quadderTol);
                     const int nQuads = quadder.countQuads();
                     for (int i = 0; i < nQuads; ++i) {
-                       *fPathVerbs.append() = SkPath::kQuad_Verb;
+                       fPathVerbs.push_back(SkPath::kQuad_Verb);
                     }
-                    fPathPts.append(nQuads * 2, &quadPts[1]);
+                    fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
                     curve[0] = pts[2];
                     lastCurve = true;
                 }
@@ -145,16 +135,16 @@ int SkOpEdgeBuilder::preFetch() {
             case SkPath::kDone_Verb:
                 continue;
         }
-        *fPathVerbs.append() = verb;
+        fPathVerbs.push_back(verb);
         int ptCount = SkPathOpsVerbToPoints(verb);
-        fPathPts.append(ptCount, &pts[1]);
+        fPathPts.push_back_n(ptCount, &pts[1]);
         curve[0] = pts[ptCount];
         lastCurve = true;
     } while (verb != SkPath::kDone_Verb);
     if (!fAllowOpenContours && lastCurve) {
         closeContour(curve[0], curveStart);
     }
-    *fPathVerbs.append() = SkPath::kDone_Verb;
+    fPathVerbs.push_back(SkPath::kDone_Verb);
     return fPathVerbs.count() - 1;
 }
 
@@ -163,10 +153,10 @@ bool SkOpEdgeBuilder::close() {
     return true;
 }
 
-bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
+bool SkOpEdgeBuilder::walk() {
     uint8_t* verbPtr = fPathVerbs.begin();
     uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
-    SkPoint* pointsPtr = fPathPts.begin() - 1;
+    const SkPoint* pointsPtr = fPathPts.begin() - 1;
     SkPath::Verb verb;
     while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
         if (verbPtr == endOfFirstHalf) {
@@ -175,7 +165,7 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
         verbPtr++;
         switch (verb) {
             case SkPath::kMove_Verb:
-                if (fCurrentContour && fCurrentContour->count()) {
+                if (fCurrentContour) {
                     if (fAllowOpenContours) {
                         complete();
                     } else if (!close()) {
@@ -183,44 +173,21 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
                     }
                 }
                 if (!fCurrentContour) {
-                    fCurrentContour = fContoursHead->appendContour(allocator);
+                    fCurrentContour = fContours.push_back_n(1);
+                    fCurrentContour->setOperand(fOperand);
+                    fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
                 }
-                fCurrentContour->init(fGlobalState, fOperand,
-                    fXorMask[fOperand] == kEvenOdd_PathOpsMask);
                 pointsPtr += 1;
                 continue;
             case SkPath::kLine_Verb:
-                fCurrentContour->addLine(pointsPtr, fAllocator);
+                fCurrentContour->addLine(pointsPtr);
                 break;
             case SkPath::kQuad_Verb:
-                fCurrentContour->addQuad(pointsPtr, fAllocator);
+                fCurrentContour->addQuad(pointsPtr);
+                break;
+            case SkPath::kCubic_Verb:
+                fCurrentContour->addCubic(pointsPtr);
                 break;
-            case SkPath::kCubic_Verb: {
-                // split self-intersecting cubics in two before proceeding
-                // if the cubic is convex, it doesn't self intersect.
-                SkScalar loopT;
-                if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) {
-                    SkPoint cubicPair[7]; 
-                    SkChopCubicAt(pointsPtr, cubicPair, loopT);
-                    SkPoint cStorage[2][4];
-                    SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]);
-                    SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]);
-                    if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) {
-                        SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0];
-                        SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1];
-                        for (size_t index = 0; index < SK_ARRAY_COUNT(curve1); ++index) {
-                            force_small_to_zero(&curve1[index]);
-                            force_small_to_zero(&curve2[index]);
-                        }
-                        fCurrentContour->addCurve(v1, curve1, fAllocator);
-                        fCurrentContour->addCurve(v2, curve2, fAllocator);
-                    } else {
-                        fCurrentContour->addCubic(pointsPtr, fAllocator);
-                    }
-                } else {
-                    fCurrentContour->addCubic(pointsPtr, fAllocator);
-                }
-                } break;
             case SkPath::kClose_Verb:
                 SkASSERT(fCurrentContour);
                 if (!close()) {
@@ -231,11 +198,10 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
                 SkDEBUGFAIL("bad verb");
                 return false;
         }
-        SkASSERT(fCurrentContour);
-        fCurrentContour->debugValidate();
         pointsPtr += SkPathOpsVerbToPoints(verb);
+        SkASSERT(fCurrentContour);
     }
-   if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
+   if (fCurrentContour && !fAllowOpenContours && !close()) {
        return false;
    }
    return true;
index 3ecc915833137b173547b18dee07ee0171ea30f4..fd0744572d9bc256df276bc5feb1acc136b61a9d 100644 (file)
@@ -9,25 +9,20 @@
 
 #include "SkOpContour.h"
 #include "SkPathWriter.h"
+#include "SkTArray.h"
 
 class SkOpEdgeBuilder {
 public:
-    SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
-            SkOpGlobalState* globalState)
-        : fAllocator(allocator)  // FIXME: replace with const, tune this
-        , fGlobalState(globalState)
-        , fPath(path.nativePath())
-        , fContoursHead(contours2)
+    SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
+        : fPath(path.nativePath())
+        , fContours(contours)
         , fAllowOpenContours(true) {
         init();
     }
 
-    SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
-            SkOpGlobalState* globalState)
-        : fAllocator(allocator)
-        , fGlobalState(globalState)
-        , fPath(&path)
-        , fContoursHead(contours2)
+    SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
+        : fPath(&path)
+        , fContours(contours)
         , fAllowOpenContours(false) {
         init();
     }
@@ -35,19 +30,13 @@ public:
     void addOperand(const SkPath& path);
 
     void complete() {
-        if (fCurrentContour && fCurrentContour->count()) {
+        if (fCurrentContour && fCurrentContour->segments().count()) {
             fCurrentContour->complete();
             fCurrentContour = NULL;
         }
     }
 
-    int count() const;
-    bool finish(SkChunkAlloc* );
-
-    const SkOpContour* head() const {
-        return fContoursHead;
-    }
-
+    bool finish();
     void init();
     bool unparseable() const { return fUnparseable; }
     SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@@ -56,15 +45,13 @@ private:
     void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
     bool close();
     int preFetch();
-    bool walk(SkChunkAlloc* );
+    bool walk();
 
-    SkChunkAlloc* fAllocator;
-    SkOpGlobalState* fGlobalState;
     const SkPath* fPath;
-    SkTDArray<SkPoint> fPathPts;
-    SkTDArray<uint8_t> fPathVerbs;
+    SkTArray<SkPoint, true> fPathPts;
+    SkTArray<uint8_t, true> fPathVerbs;
     SkOpContour* fCurrentContour;
-    SkOpContour* fContoursHead;
+    SkTArray<SkOpContour>& fContours;
     SkPathOpsMask fXorMask[2];
     int fSecondHalf;
     bool fOperand;
index 902f27374438b150739c125ec8e0d24760f442d2..1fb5afa028af33e894a82fe890e8b03af185f52a 100644 (file)
@@ -4,20 +4,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkOpCoincidence.h"
+#include "SkIntersections.h"
 #include "SkOpContour.h"
 #include "SkOpSegment.h"
 #include "SkPathWriter.h"
-
-/*
-After computing raw intersections, post process all segments to:
-- find small collections of points that can be collapsed to a single point
-- find missing intersections to resolve differences caused by different algorithms
-
-Consider segments containing tiny or small intervals. Consider coincident segments
-because coincidence finds intersections through distance measurement that non-coincident
-intersection tests cannot.
- */
+#include "SkTSort.h"
 
 #define F (false)      // discard the edge
 #define T (true)       // keep the edge
@@ -42,125 +33,147 @@ static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
 #undef F
 #undef T
 
-SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr, bool* done, bool* sortable) {
-    if (SkOpAngle* result = activeAngleInner(start, startPtr, endPtr, done, sortable)) {
+enum {
+    kOutsideTrackedTCount = 16,  // FIXME: determine what this should be
+    kMissingSpanCount = 4,  // FIXME: determine what this should be
+};
+
+const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
+        bool* sortable) const {
+    if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
         return result;
     }
-    if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, done, sortable)) {
-        return result;
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    while (--lesser >= 0
+            && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
+        if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
+            return result;
+        }
     }
+    do {
+        if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
+            return result;
+        }
+        if (++index == fTs.count()) {
+            break;
+        }
+        if (fTs[index - 1].fTiny) {
+            referenceT = fTs[index].fT;
+            continue;
+        }
+    } while (precisely_negative(fTs[index].fT - referenceT));
     return NULL;
 }
 
-SkOpAngle* SkOpSegment::activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr, bool* done, bool* sortable) {
-    SkOpSpan* upSpan = start->upCastable();
-    if (upSpan) {
-        if (upSpan->windValue() || upSpan->oppValue()) {
-            SkOpSpanBase* next = upSpan->next();
-            if (!*endPtr) {
-                *startPtr = start;
-                *endPtr = next;
+const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
+        bool* sortable) const {
+    int next = nextExactSpan(index, 1);
+    if (next > 0) {
+        const SkOpSpan& upSpan = fTs[index];
+        if (upSpan.fWindValue || upSpan.fOppValue) {
+            if (*end < 0) {
+                *start = index;
+                *end = next;
             }
-            if (!upSpan->done()) {
-                if (upSpan->windSum() != SK_MinS32) {
-                    return spanToAngle(start, next);
+            if (!upSpan.fDone) {
+                if (upSpan.fWindSum != SK_MinS32) {
+                    return spanToAngle(index, next);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(upSpan->done());
+            SkASSERT(upSpan.fDone);
         }
     }
-    SkOpSpan* downSpan = start->prev();
+    int prev = nextExactSpan(index, -1);
     // edge leading into junction
-    if (downSpan) {
-        if (downSpan->windValue() || downSpan->oppValue()) {
-            if (!*endPtr) {
-                *startPtr = start;
-                *endPtr = downSpan;
-            }
-            if (!downSpan->done()) {
-                if (downSpan->windSum() != SK_MinS32) {
-                    return spanToAngle(start, downSpan);
+    if (prev >= 0) {
+        const SkOpSpan& downSpan = fTs[prev];
+        if (downSpan.fWindValue || downSpan.fOppValue) {
+            if (*end < 0) {
+                *start = index;
+                *end = prev;
+            }
+            if (!downSpan.fDone) {
+                if (downSpan.fWindSum != SK_MinS32) {
+                    return spanToAngle(index, prev);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(downSpan->done());
+            SkASSERT(downSpan.fDone);
         }
     }
     return NULL;
 }
 
-SkOpAngle* SkOpSegment::activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr, bool* done, bool* sortable) {
-    SkOpPtT* oPtT = start->ptT()->next();
-    SkOpSegment* other = oPtT->segment();
-    SkOpSpanBase* oSpan = oPtT->span();
-    return other->activeAngleInner(oSpan, startPtr, endPtr, done, sortable);
+const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
+        bool* sortable) const {
+    const SkOpSpan* span = &fTs[index];
+    SkOpSegment* other = span->fOther;
+    int oIndex = span->fOtherIndex;
+    return other->activeAngleInner(oIndex, start, end, done, sortable);
 }
 
-SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
+SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
     SkASSERT(!done());
     SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
+    int count = fTs.count();
     // see if either end is not done since we want smaller Y of the pair
     bool lastDone = true;
     double lastT = -1;
-    SkOpSpanBase* span = &fHead;
-    do {
-        if (lastDone && (span->final() || span->upCast()->done())) {
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fDone && lastDone) {
+            goto next;
+        }
+        if (approximately_negative(span.fT - lastT)) {
             goto next;
         }
         {
-            const SkPoint& xy = span->pt();
+            const SkPoint& xy = xyAtT(&span);
             if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
                 topPt = xy;
-                if (firstSpan) {
-                    *firstSpan = span;
+                if (firstT) {
+                    *firstT = index;
                 }
             }
             if (fVerb != SkPath::kLine_Verb && !lastDone) {
-                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
-                        span->t());
+                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
                 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
                         && topPt.fX > curveTop.fX)) {
                     topPt = curveTop;
-                    if (firstSpan) {
-                        *firstSpan = span;
+                    if (firstT) {
+                        *firstT = index;
                     }
                 }
             }
-            lastT = span->t();
+            lastT = span.fT;
         }
 next:
-        if (span->final()) {
-            break;
-        }
-        lastDone = span->upCast()->done();
-    } while ((span = span->upCast()->next()));
+        lastDone = span.fDone;
+    }
     return topPt;
 }
 
-bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
-        SkPathOp op) {
-    int sumMiWinding = this->updateWinding(end, start);
-    int sumSuWinding = this->updateOppWinding(end, start);
+bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
+    int sumMiWinding = updateWinding(endIndex, index);
+    int sumSuWinding = updateOppWinding(endIndex, index);
 #if DEBUG_LIMIT_WIND_SUM
     SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
     SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
 #endif
-    if (this->operand()) {
+    if (fOperand) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
-    return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
+    return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
 }
 
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
-        SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
+        int* sumMiWinding, int* sumSuWinding) {
     int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
-    this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
+    setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
             &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
     bool miFrom;
     bool miTo;
@@ -180,31 +193,178 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, Sk
     bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
 #if DEBUG_ACTIVE_OP
     SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
-            __FUNCTION__, debugID(), start->t(), end->t(),
+            __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
             SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
 #endif
     return result;
 }
 
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
-    int sumWinding = updateWinding(end, start);
-    return activeWinding(start, end, &sumWinding);
+bool SkOpSegment::activeWinding(int index, int endIndex) {
+    int sumWinding = updateWinding(endIndex, index);
+    return activeWinding(index, endIndex, &sumWinding);
 }
 
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
+bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
     int maxWinding;
-    setUpWinding(start, end, &maxWinding, sumWinding);
+    setUpWinding(index, endIndex, &maxWinding, sumWinding);
     bool from = maxWinding != 0;
     bool to = *sumWinding  != 0;
     bool result = gUnaryActiveEdge[from][to];
     return result;
 }
 
-void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
-        SkPathWriter* path, bool active) const {
+void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
+        SkOpSegment* other) {
+    int tIndex = -1;
+    int tCount = fTs.count();
+    int oIndex = -1;
+    int oCount = other->fTs.count();
+    do {
+        ++tIndex;
+    } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
+    int tIndexStart = tIndex;
+    do {
+        ++oIndex;
+    } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
+    int oIndexStart = oIndex;
+    const SkPoint* nextPt;
+    do {
+        nextPt = &fTs[++tIndex].fPt;
+        SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
+    } while (startPt == *nextPt);
+    double nextT = fTs[tIndex].fT;
+    const SkPoint* oNextPt;
+    do {
+        oNextPt = &other->fTs[++oIndex].fPt;
+        SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
+    } while (endPt == *oNextPt);
+    double oNextT = other->fTs[oIndex].fT;
+    // at this point, spans before and after are at:
+    //  fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
+    // if tIndexStart == 0, no prior span
+    // if nextT == 1, no following span
+
+    // advance the span with zero winding
+    // if the following span exists (not past the end, non-zero winding)
+    // connect the two edges
+    if (!fTs[tIndexStart].fWindValue) {
+        if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+            SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, fID, other->fID, tIndexStart - 1,
+                    fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
+                    xyAtT(tIndexStart).fY);
+#endif
+            SkPoint copy = fTs[tIndexStart].fPt;  // add t pair may move the point array
+            addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
+        }
+        if (nextT < 1 && fTs[tIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+            SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, fID, other->fID, tIndex,
+                    fTs[tIndex].fT, xyAtT(tIndex).fX,
+                    xyAtT(tIndex).fY);
+#endif
+            SkPoint copy = fTs[tIndex].fPt;  // add t pair may move the point array
+            addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
+        }
+    } else {
+        SkASSERT(!other->fTs[oIndexStart].fWindValue);
+        if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+            SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, fID, other->fID, oIndexStart - 1,
+                    other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
+                    other->xyAtT(oIndexStart).fY);
+            other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
+#endif
+        }
+        if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+            SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, fID, other->fID, oIndex,
+                    other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
+                    other->xyAtT(oIndex).fY);
+            other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
+#endif
+        }
+    }
+}
+
+void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
+        SkOpSegment* other) {
+    // walk this to startPt
+    // walk other to startPt
+    // if either is > 0, add a pointer to the other, copying adjacent winding
+    int tIndex = -1;
+    int oIndex = -1;
+    do {
+        ++tIndex;
+    } while (startPt != fTs[tIndex].fPt);
+    int ttIndex = tIndex;
+    bool checkOtherTMatch = false;
+    do {
+        const SkOpSpan& span = fTs[ttIndex];
+        if (startPt != span.fPt) {
+            break;
+        }
+        if (span.fOther == other && span.fPt == startPt) {
+            checkOtherTMatch = true;
+            break;
+        }
+    } while (++ttIndex < count());
+    do {
+        ++oIndex;
+    } while (startPt != other->fTs[oIndex].fPt);
+    bool skipAdd = false;
+    if (checkOtherTMatch) {
+        int ooIndex = oIndex;
+        do {
+            const SkOpSpan& oSpan = other->fTs[ooIndex];
+            if (startPt != oSpan.fPt) {
+                break;
+            }
+            if (oSpan.fT == fTs[ttIndex].fOtherT) {
+                skipAdd = true;
+                break;
+            }
+        } while (++ooIndex < other->count());
+    }
+    if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
+        addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
+    }
+    SkPoint nextPt = startPt;
+    do {
+        const SkPoint* workPt;
+        do {
+            workPt = &fTs[++tIndex].fPt;
+        } while (nextPt == *workPt);
+        const SkPoint* oWorkPt;
+        do {
+            oWorkPt = &other->fTs[++oIndex].fPt;
+        } while (nextPt == *oWorkPt);
+        nextPt = *workPt;
+        double tStart = fTs[tIndex].fT;
+        double oStart = other->fTs[oIndex].fT;
+        if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
+            break;
+        }
+        if (*workPt == *oWorkPt) {
+            addTPair(tStart, other, oStart, false, nextPt);
+        }
+    } while (endPt != nextPt);
+}
+
+void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
+    init(pts, SkPath::kCubic_Verb, operand, evenOdd);
+    fBounds.setCubicBounds(pts);
+}
+
+void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
     SkPoint edge[4];
     const SkPoint* ePtr;
-    if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
+    int lastT = fTs.count() - 1;
+    if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
         ePtr = fPts;
     } else {
     // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -212,7 +372,7 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
         ePtr = edge;
     }
     if (active) {
-        bool reverse = ePtr == fPts && start != &fHead;
+        bool reverse = ePtr == fPts && start != 0;
         if (reverse) {
             path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
             switch (fVerb) {
@@ -228,6 +388,7 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
                 default:
                     SkASSERT(0);
             }
+   //         return ePtr[0];
        } else {
             path->deferredMoveLine(ePtr[0]);
             switch (fVerb) {
@@ -245,350 +406,1473 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
             }
         }
     }
+  //  return ePtr[SkPathOpsVerbToPoints(fVerb)];
 }
 
-SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
-    SkOpSpanBase* existing = NULL;
-    SkOpSpanBase* test = &fHead;
-    double testT;
+void SkOpSegment::addEndSpan(int endIndex) {
+    SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
+//            && approximately_greater_than_one(span(endIndex).fT)
+    ));
+    int spanCount = fTs.count();
+    int startIndex = endIndex - 1;
+    while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
+        --startIndex;
+        SkASSERT(startIndex > 0);
+        --endIndex;
+    }
+    SkOpAngle& angle = fAngles.push_back();
+    angle.set(this, spanCount - 1, startIndex);
+#if DEBUG_ANGLE
+    debugCheckPointsEqualish(endIndex, spanCount);
+#endif
+    setFromAngle(endIndex, &angle);
+}
+
+void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
+    int spanCount = fTs.count();
     do {
-        if ((testT = test->ptT()->fT) >= t) {
-            if (testT == t) {
-                existing = test;
-            }
-            break;
-        }
-    } while ((test = test->upCast()->next()));
-    SkOpPtT* result;
-    if (existing && existing->contains(opp)) {
-        result = existing->ptT();
-    } else {
-        result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+        fTs[endIndex].fFromAngle = angle;
+    } while (++endIndex < spanCount);
+}
+
+void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
+    init(pts, SkPath::kLine_Verb, operand, evenOdd);
+    fBounds.set(pts, 2);
+}
+
+// add 2 to edge or out of range values to get T extremes
+void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
+    SkOpSpan& span = fTs[index];
+    if (precisely_zero(otherT)) {
+        otherT = 0;
+    } else if (precisely_equal(otherT, 1)) {
+        otherT = 1;
     }
-    SkASSERT(result);
-    return result;
+    span.fOtherT = otherT;
+    span.fOtherIndex = otherIndex;
+}
+
+void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
+    init(pts, SkPath::kQuad_Verb, operand, evenOdd);
+    fBounds.setQuadBounds(pts);
 }
 
-SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
-        SkChunkAlloc* allocator) {
-    SkOpSpan* startSpan = fTail.prev();
-    SkASSERT(startSpan);
-    SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-    *anglePtr = angle;
-    angle->set(&fTail, startSpan);
-    fTail.setFromAngle(angle);
-    SkOpSegment* other = NULL;  // these initializations silence a release build warning
-    SkOpSpan* oStartSpan = NULL;
-    SkOpSpanBase* oEndSpan = NULL;
-    SkOpPtT* ptT = fTail.ptT(), * startPtT = ptT;
-    while ((ptT = ptT->next()) != startPtT) {
-        other = ptT->segment();
-        oStartSpan = ptT->span()->upCastable();
-        if (oStartSpan && oStartSpan->windValue()) {
-            oEndSpan = oStartSpan->next();
+SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+    int spanIndex = count() - 1;
+    int startIndex = nextExactSpan(spanIndex, -1);
+    SkASSERT(startIndex >= 0);
+    SkOpAngle& angle = fAngles.push_back();
+    *anglePtr = &angle;
+    angle.set(this, spanIndex, startIndex);
+    setFromAngle(spanIndex, &angle);
+    SkOpSegment* other;
+    int oStartIndex, oEndIndex;
+    do {
+        const SkOpSpan& span = fTs[spanIndex];
+        SkASSERT(span.fT > 0);
+        other = span.fOther;
+        oStartIndex = span.fOtherIndex;
+        oEndIndex = other->nextExactSpan(oStartIndex, 1);
+        if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
             break;
         }
-        oEndSpan = ptT->span();
-        oStartSpan = oEndSpan->prev();
-        if (oStartSpan && oStartSpan->windValue()) {
+        oEndIndex = oStartIndex;
+        oStartIndex = other->nextExactSpan(oEndIndex, -1);
+        --spanIndex;
+    } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
+    SkOpAngle& oAngle = other->fAngles.push_back();
+    oAngle.set(other, oStartIndex, oEndIndex);
+    other->setToAngle(oEndIndex, &oAngle);
+    *otherPtr = other;
+    return &oAngle;
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+    int endIndex = nextExactSpan(0, 1);
+    SkASSERT(endIndex > 0);
+    SkOpAngle& angle = fAngles.push_back();
+    *anglePtr = &angle;
+    angle.set(this, 0, endIndex);
+    setToAngle(endIndex, &angle);
+    int spanIndex = 0;
+    SkOpSegment* other;
+    int oStartIndex, oEndIndex;
+    do {
+        const SkOpSpan& span = fTs[spanIndex];
+        SkASSERT(span.fT < 1);
+        other = span.fOther;
+        oEndIndex = span.fOtherIndex;
+        oStartIndex = other->nextExactSpan(oEndIndex, -1);
+        if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
             break;
         }
-    }
-    SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-    oAngle->set(oStartSpan, oEndSpan);
-    oStartSpan->setToAngle(oAngle);
+        oStartIndex = oEndIndex;
+        oEndIndex = other->nextExactSpan(oStartIndex, 1);
+        ++spanIndex;
+    } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
+    SkOpAngle& oAngle = other->fAngles.push_back();
+    oAngle.set(other, oEndIndex, oStartIndex);
+    other->setFromAngle(oEndIndex, &oAngle);
     *otherPtr = other;
-    return oAngle;
+    return &oAngle;
 }
 
-SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
+SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
     SkOpSegment* other;
     SkOpAngle* angle, * otherAngle;
     if (step > 0) {
-        otherAngle = addSingletonAngleUp(&other, &angle, allocator);
+        otherAngle = addSingletonAngleUp(&other, &angle);
     } else {
-        otherAngle = addSingletonAngleDown(&other, &angle, allocator);
+        otherAngle = addSingletonAngleDown(&other, &angle);
     }
     angle->insert(otherAngle);
     return angle;
 }
 
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
-        SkChunkAlloc* allocator) {
-    SkOpSpanBase* endSpan = fHead.next();
-    SkASSERT(endSpan);
-    SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-    *anglePtr = angle;
-    angle->set(&fHead, endSpan);
-    fHead.setToAngle(angle);
-    SkOpSegment* other = NULL;  // these initializations silence a release build warning
-    SkOpSpan* oStartSpan = NULL;
-    SkOpSpanBase* oEndSpan = NULL;
-    SkOpPtT* ptT = fHead.ptT(), * startPtT = ptT;
-    while ((ptT = ptT->next()) != startPtT) {
-        other = ptT->segment();
-        oEndSpan = ptT->span();
-        oStartSpan = oEndSpan->prev();
-        if (oStartSpan && oStartSpan->windValue()) {
-            break;
-        }
-        oStartSpan = oEndSpan->upCastable();
-        if (oStartSpan && oStartSpan->windValue()) {
-            oEndSpan = oStartSpan->next();
-            break;
-        }
-    }
-    SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-    oAngle->set(oEndSpan, oStartSpan);
-    oEndSpan->setFromAngle(oAngle);
-    *otherPtr = other;
-    return oAngle;
+void SkOpSegment::addStartSpan(int endIndex) {
+    int index = 0;
+    SkOpAngle& angle = fAngles.push_back();
+    angle.set(this, index, endIndex);
+#if DEBUG_ANGLE
+    debugCheckPointsEqualish(index, endIndex);
+#endif
+    setToAngle(endIndex, &angle);
 }
 
-SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
-    debugValidate();
-    SkPoint pt = this->ptAtT(t);
-    SkOpSpanBase* span = &fHead;
+void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
+    int index = 0;
     do {
-        SkOpPtT* result = span->ptT();
-        if (t == result->fT) {
-            return result;
-        }
-        if (this->match(result, this, t, pt)) {
-            // see if any existing alias matches segment, pt, and t
-            SkOpPtT* loop = result->next();
-            bool duplicatePt = false;
-            while (loop != result) {
-                bool ptMatch = loop->fPt == pt;
-                if (loop->segment() == this && loop->fT == t && ptMatch) {
-                    return result;
-                }
-                duplicatePt |= ptMatch;
-                loop = loop->next();
-            }
-            if (kNoAlias == allowAlias) {
-                return result;
-            }
-            SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
-            alias->init(result->span(), t, pt, duplicatePt);
-            result->insert(alias);
-            result->span()->unaligned();
-            this->debugValidate();
-#if DEBUG_ADD_T
-            SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n",  __FUNCTION__, t,
-                    alias->segment()->debugID(), alias->span()->debugID());
-#endif
-            return alias;
-        }
-        if (t < result->fT) {
-            SkOpSpan* prev = result->span()->prev();
-            SkOpSpan* span = insert(prev, allocator);
-            span->init(this, prev, t, pt);
-            this->debugValidate();
-#if DEBUG_ADD_T
-            SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
-                    span->segment()->debugID(), span->debugID());
-#endif
-            return span->ptT();
-        }
-        SkASSERT(span != &fTail);
-    } while ((span = span->upCast()->next()));
-    SkASSERT(0);
-    return NULL;
+        fTs[index].fToAngle = angle;
+    } while (++index < endIndex);
 }
 
-// choose a solitary t and pt value; remove aliases; align the opposite ends
-void SkOpSegment::align() {
-    debugValidate();
-    SkOpSpanBase* span = &fHead;
-    if (!span->aligned()) {
-        span->alignEnd(0, fPts[0]);
-    }
-    while ((span = span->upCast()->next())) {
-        if (span == &fTail) {
+    // Defer all coincident edge processing until
+    // after normal intersections have been computed
+
+// no need to be tricky; insert in normal T order
+// resolve overlapping ts when considering coincidence later
+
+    // add non-coincident intersection. Resulting edges are sorted in T.
+int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
+    SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
+ #if 0  // this needs an even rougher association to be useful
+    SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
+ #endif
+    const SkPoint& firstPt = fPts[0];
+    const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
+    SkASSERT(newT == 0 || !precisely_zero(newT));
+    SkASSERT(newT == 1 || !precisely_equal(newT, 1));
+    // FIXME: in the pathological case where there is a ton of intercepts,
+    //  binary search?
+    int insertedAt = -1;
+    int tCount = fTs.count();
+    for (int index = 0; index < tCount; ++index) {
+        // OPTIMIZATION: if there are three or more identical Ts, then
+        // the fourth and following could be further insertion-sorted so
+        // that all the edges are clockwise or counterclockwise.
+        // This could later limit segment tests to the two adjacent
+        // neighbors, although it doesn't help with determining which
+        // circular direction to go in.
+        const SkOpSpan& span = fTs[index];
+        if (newT < span.fT) {
+            insertedAt = index;
             break;
         }
-        span->align();
+        if (newT == span.fT) {
+            if (pt == span.fPt) {
+                insertedAt = index;
+                break;
+            }
+            if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
+                insertedAt = index;
+                break;
+            }
+        }
     }
-    if (!span->aligned()) {
-        span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
+    SkOpSpan* span;
+    if (insertedAt >= 0) {
+        span = fTs.insert(insertedAt);
+    } else {
+        insertedAt = tCount;
+        span = fTs.append();
     }
-    debugValidate();
-}
-
-bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
-        const SkOpSpanBase* greater) {
-    if (lesser->t() > greater->t()) {
-        SkTSwap<const SkOpSpanBase*>(lesser, greater);
+    span->fT = newT;
+    span->fOtherT = -1;
+    span->fOther = other;
+    span->fPt = pt;
+#if 0
+    // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
+    SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
+            && approximately_equal(xyAtT(newT).fY, pt.fY));
+#endif
+    span->fFromAngle = NULL;
+    span->fToAngle = NULL;
+    span->fWindSum = SK_MinS32;
+    span->fOppSum = SK_MinS32;
+    span->fWindValue = 1;
+    span->fOppValue = 0;
+    span->fChased = false;
+    span->fCoincident = false;
+    span->fLoop = false;
+    span->fNear = false;
+    span->fMultiple = false;
+    span->fSmall = false;
+    span->fTiny = false;
+    if ((span->fDone = newT == 1)) {
+        ++fDoneSpans;
     }
-    return approximately_between(lesser->t(), testT, greater->t());
+    setSpanFlags(pt, newT, span);
+    return insertedAt;
 }
 
-void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
-    bool activePrior = !fHead.isCanceled();
-    if (activePrior && !fHead.simple()) {
-        addStartSpan(allocator);
-    }
-    SkOpSpan* prior = &fHead;
-    SkOpSpanBase* spanBase = fHead.next();
-    while (spanBase != &fTail) {
-        if (activePrior) {
-            SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-            priorAngle->set(spanBase, prior);
-            spanBase->setFromAngle(priorAngle);
+void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
+    int less = -1;
+// FIXME: note that this relies on spans being a continguous array
+// find range of spans with nearly the same point as this one
+    // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+    while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
+        if (fVerb == SkPath::kCubic_Verb) {
+            double tInterval = newT - span[less].fT;
+            double tMid = newT - tInterval / 2;
+            SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+            if (!midPt.approximatelyEqual(xyAtT(span))) {
+                break;
+            }
         }
-        SkOpSpan* span = spanBase->upCast();
-        bool active = !span->isCanceled();
-        SkOpSpanBase* next = span->next();
-        if (active) {
-            SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-            angle->set(span, next);
-            span->setToAngle(angle);
+        --less;
+    }
+    int more = 1;
+    // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+    while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
+        if (fVerb == SkPath::kCubic_Verb) {
+            double tEndInterval = span[more].fT - newT;
+            double tMid = newT - tEndInterval / 2;
+            SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
+            if (!midEndPt.approximatelyEqual(xyAtT(span))) {
+                break;
+            }
         }
-        activePrior = active;
-        prior = span;
-        spanBase = next;
+        ++more;
+    }
+    ++less;
+    --more;
+    while (more - 1 > less && span[more].fPt == span[more - 1].fPt
+            && span[more].fT == span[more - 1].fT) {
+        --more;
+    }
+    if (less == more) {
+        return;
     }
-    if (activePrior && !fTail.simple()) {
-        addEndSpan(allocator);
+    if (precisely_negative(span[more].fT - span[less].fT)) {
+        return;
     }
+// if the total range of t values is big enough, mark all tiny
+    bool tiny = span[less].fPt == span[more].fPt;
+    int index = less;
+    do {
+        fSmall = span[index].fSmall = true;
+        fTiny |= span[index].fTiny = tiny;
+        if (!span[index].fDone) {
+            span[index].fDone = true;
+            ++fDoneSpans;
+        }
+    } while (++index < more);
+    return;
 }
 
-void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
-    SkOpSpanBase* base = &fHead;
-    SkOpSpan* span;
+void SkOpSegment::resetSpanFlags() {
+    fSmall = fTiny = false;
+    fDoneSpans = 0;
+    int start = 0;
+    int last = this->count() - 1;
     do {
-        SkOpAngle* angle = base->fromAngle();
-        if (angle && angle->fCheckCoincidence) {
-            angle->checkNearCoincidence();
+        SkOpSpan* startSpan = &this->fTs[start];
+        double startT = startSpan->fT;
+        startSpan->fSmall = startSpan->fTiny = false;  // sets range initial
+        bool terminus = startT == 1;
+        if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
+            ++fDoneSpans;
+        }
+        ++start;  // range initial + 1
+        if (terminus) {
+            continue;
+        }
+        const SkPoint& pt = startSpan->fPt;
+        int end = start;  // range initial + 1
+        while (end <= last) {
+            const SkOpSpan& endSpan = this->span(end);
+            if (!AlmostEqualUlps(endSpan.fPt, pt)) {
+                break;
+            }
+            if (fVerb == SkPath::kCubic_Verb) {
+                double tMid = (startSpan->fT + endSpan.fT) / 2;
+                SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
+                if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
+                    break;
+                }
+            }
+            ++end;
         }
-        if (base->final()) {
-             break;
+        if (start == end) {  // end == range final + 1
+            continue;
+        }
+        while (--end >= start) {  // end == range final
+            const SkOpSpan& endSpan = this->span(end);
+            const SkOpSpan& priorSpan = this->span(end - 1);
+            if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
+                break;  // end == range final + 1
+            }
+        }
+        if (end < start) {  // end == range final + 1
+            continue;
+        }
+        int index = start - 1;  // index == range initial
+        start = end;  // start = range final + 1
+        const SkOpSpan& nextSpan = this->span(end);
+        if (precisely_negative(nextSpan.fT - startSpan->fT)) {
+            while (++index < end) {
+                startSpan = &this->fTs[index];
+                startSpan->fSmall = startSpan->fTiny = false;  // sets range initial + 1
+                if ((startSpan->fDone = !startSpan->fWindValue)) {
+                    ++fDoneSpans;
+                }
+            }
+            continue;
         }
-        span = base->upCast();
-        angle = span->toAngle();
-        if (angle && angle->fCheckCoincidence) {
-            angle->checkNearCoincidence();
+        if (!startSpan->fWindValue) {
+            --fDoneSpans;  // added back below
         }
-    } while ((base = span->next()));
+        bool tiny = nextSpan.fPt == startSpan->fPt;
+        do {
+            fSmall = startSpan->fSmall = true;  // sets range initial
+            fTiny |= startSpan->fTiny = tiny;
+            startSpan->fDone = true;
+            ++fDoneSpans;
+            startSpan = &this->fTs[++index];
+        } while (index < end);  // loop through tiny small range end (last)
+    } while (start <= last);
 }
 
-// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
-    SkASSERT(fVerb != SkPath::kLine_Verb);
-    SkPoint edge[4];
-    if (fVerb == SkPath::kCubic_Verb) {
-        double startT = start->t();
-        double endT = end->t();
-        bool flip = startT > endT;
-        SkDCubic cubic;
-        cubic.set(fPts);
-        double inflectionTs[2];
-        int inflections = cubic.findInflections(inflectionTs);
-        for (int index = 0; index < inflections; ++index) {
-            double inflectionT = inflectionTs[index];
-            if (between(startT, inflectionT, endT)) {
-                if (flip) {
-                    if (inflectionT != endT) {
-                        startT = inflectionT;
-                    }
+// set spans from start to end to decrement by one
+// note this walks other backwards
+// FIXME: there's probably an edge case that can be constructed where
+// two span in one segment are separated by float epsilon on one span but
+// not the other, if one segment is very small. For this
+// case the counts asserted below may or may not be enough to separate the
+// spans. Even if the counts work out, what if the spans aren't correctly
+// sorted? It feels better in such a case to match the span's other span
+// pointer since both coincident segments must contain the same spans.
+// FIXME? It seems that decrementing by one will fail for complex paths that
+// have three or more coincident edges. Shouldn't this subtract the difference
+// between the winding values?
+/*                                      |-->                           |-->
+this     0>>>>1>>>>2>>>>3>>>4      0>>>>1>>>>2>>>>3>>>4      0>>>>1>>>>2>>>>3>>>4
+other         2<<<<1<<<<0               2<<<<1<<<<0               2<<<<1<<<<0
+              ^         ^                 <--|                           <--|
+           startPt    endPt        test/oTest first pos      test/oTest final pos
+*/
+void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    while (startPt != fTs[index].fPt) {
+        SkASSERT(index < fTs.count());
+        ++index;
+    }
+    while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
+        --index;
+    }
+    bool oFoundEnd = false;
+    int oIndex = other->fTs.count();
+    while (startPt != other->fTs[--oIndex].fPt) {  // look for startPt match
+        SkASSERT(oIndex > 0);
+    }
+    double oStartT = other->fTs[oIndex].fT;
+    // look for first point beyond match
+    while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
+        if (!oIndex) {
+            return;  // tiny spans may move in the wrong direction
+        }
+    }
+    SkOpSpan* test = &fTs[index];
+    SkOpSpan* oTest = &other->fTs[oIndex];
+    SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
+    SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+    bool decrement, track, bigger;
+    int originalWindValue;
+    const SkPoint* testPt;
+    const SkPoint* oTestPt;
+    do {
+        SkASSERT(test->fT < 1);
+        SkASSERT(oTest->fT < 1);
+        decrement = test->fWindValue && oTest->fWindValue;
+        track = test->fWindValue || oTest->fWindValue;
+        bigger = test->fWindValue >= oTest->fWindValue;
+        testPt = &test->fPt;
+        double testT = test->fT;
+        oTestPt = &oTest->fPt;
+        double oTestT = oTest->fT;
+        do {
+            if (decrement) {
+                if (binary && bigger) {
+                    test->fOppValue--;
                 } else {
-                    if (inflectionT != startT) {
-                        endT = inflectionT;
-                    }
+                    decrementSpan(test);
                 }
+            } else if (track) {
+                TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
+            }
+            SkASSERT(index < fTs.count() - 1);
+            test = &fTs[++index];
+        } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
+        originalWindValue = oTest->fWindValue;
+        do {
+            SkASSERT(oTest->fT < 1);
+            SkASSERT(originalWindValue == oTest->fWindValue);
+            if (decrement) {
+                if (binary && !bigger) {
+                    oTest->fOppValue--;
+                } else {
+                    other->decrementSpan(oTest);
+                }
+            } else if (track) {
+                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+            }
+            if (!oIndex) {
+                break;
+            }
+            oFoundEnd |= endPt == oTest->fPt;
+            oTest = &other->fTs[--oIndex];
+        } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
+    } while (endPt != test->fPt && test->fT < 1);
+    // FIXME: determine if canceled edges need outside ts added
+    if (!oFoundEnd) {
+        for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
+            SkOpSpan* oTst2 = &other->fTs[oIdx2];            
+            if (originalWindValue != oTst2->fWindValue) {
+                goto skipAdvanceOtherCancel;
+            }
+            if (!oTst2->fWindValue) {
+                goto skipAdvanceOtherCancel;
+            }
+            if (endPt == other->fTs[oIdx2].fPt) {
+                break;
             }
         }
-        SkDCubic part = cubic.subDivide(startT, endT);
-        for (int index = 0; index < 4; ++index) {
-            edge[index] = part[index].asSkPoint();
-        }
-    } else {
-    subDivide(start, end, edge);
+        oFoundEnd = endPt == oTest->fPt;
+        do {
+            SkASSERT(originalWindValue == oTest->fWindValue);
+            if (decrement) {
+                if (binary && !bigger) {
+                    oTest->fOppValue--;
+                } else {
+                    other->decrementSpan(oTest);
+                }
+            } else if (track) {
+                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+            }
+            if (!oIndex) {
+                break;
+            }
+            oTest = &other->fTs[--oIndex];
+            oFoundEnd |= endPt == oTest->fPt;
+        } while (!oFoundEnd || endPt == oTest->fPt);
     }
-    bool sumSet = false;
-    int points = SkPathOpsVerbToPoints(fVerb);
-    double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
-    if (!sumSet) {
-        for (int idx = 0; idx < points; ++idx){
-            sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+skipAdvanceOtherCancel:
+    int outCount = outsidePts.count();
+    if (!done() && outCount) {
+        addCancelOutsides(outsidePts[0], outsidePts[1], other);
+        if (outCount > 2) {
+            addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
         }
     }
-    if (fVerb == SkPath::kCubic_Verb) {
-        SkDCubic cubic;
-        cubic.set(edge);
-        *swap = sum > 0 && !cubic.monotonicInY();
-    } else {
-        SkDQuad quad;
-        quad.set(edge);
-        *swap = sum > 0 && !quad.monotonicInY();
+    if (!other->done() && oOutsidePts.count()) {
+        other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
     }
-    return sum <= 0;
+    setCoincidentRange(startPt, endPt, other);
+    other->setCoincidentRange(startPt, endPt, this);
 }
 
-void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-        SkOpAngle::IncludeType includeType) {
-    const SkOpSegment* baseSegment = baseAngle->segment();
-    int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
-    int sumSuWinding;
-    bool binary = includeType >= SkOpAngle::kBinarySingle;
-    if (binary) {
-        sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
-        if (baseSegment->operand()) {
-            SkTSwap<int>(sumMiWinding, sumSuWinding);
-        }
+int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
+    // if the tail nearly intersects itself but not quite, the caller records this separately
+    int result = addT(this, pt, newT);
+    SkOpSpan* span = &fTs[result];
+    fLoop = span->fLoop = true;
+    return result;
+}
+
+// find the starting or ending span with an existing loop of angles
+// FIXME? replicate for all identical starting/ending spans?
+// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
+// FIXME? assert that only one other span has a valid windValue or oppValue
+void SkOpSegment::addSimpleAngle(int index) {
+    SkOpSpan* span = &fTs[index];
+    int idx;
+    int start, end;
+    if (span->fT == 0) {
+        idx = 0;
+        span = &fTs[0];
+        do {
+            if (span->fToAngle) {
+                SkASSERT(span->fToAngle->loopCount() == 2);
+                SkASSERT(!span->fFromAngle);
+                span->fFromAngle = span->fToAngle->next();
+                return;
+            }
+            span = &fTs[++idx];
+        } while (span->fT == 0);
+        SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
+        addStartSpan(idx);
+        start = 0;
+        end = idx;
+    } else {
+        idx = count() - 1;
+        span = &fTs[idx];
+        do {
+            if (span->fFromAngle) {
+                SkASSERT(span->fFromAngle->loopCount() == 2);
+                SkASSERT(!span->fToAngle);
+                span->fToAngle = span->fFromAngle->next();
+                return;
+            }
+            span = &fTs[--idx];
+        } while (span->fT == 1);
+        SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
+        addEndSpan(++idx);
+        start = idx;
+        end = count();
     }
-    SkOpSegment* nextSegment = nextAngle->segment();
-    int maxWinding, sumWinding;
-    SkOpSpanBase* last;
-    if (binary) {
-        int oppMaxWinding, oppSumWinding;
-        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
-                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
-                nextAngle);
+    SkOpSegment* other;
+    SkOpSpan* oSpan;
+    index = start;
+    do {
+        span = &fTs[index];
+        other = span->fOther;
+        int oFrom = span->fOtherIndex;
+        oSpan = &other->fTs[oFrom];
+        if (oSpan->fT < 1 && oSpan->fWindValue) {
+            break;
+        }
+        if (oSpan->fT == 0) {
+            continue;
+        }
+        oFrom = other->nextExactSpan(oFrom, -1);
+        SkOpSpan* oFromSpan = &other->fTs[oFrom];
+        SkASSERT(oFromSpan->fT < 1);
+        if (oFromSpan->fWindValue) {
+            break;
+        }
+    } while (++index < end);
+    SkOpAngle* angle, * oAngle;
+    if (span->fT == 0) {
+        SkASSERT(span->fOtherIndex - 1 >= 0);
+        SkASSERT(span->fOtherT == 1);
+        SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
+        SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
+        SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
+        other->addEndSpan(span->fOtherIndex);
+        angle = span->fToAngle;
+        oAngle = oSpan->fFromAngle;
     } else {
-        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
-                &maxWinding, &sumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+        SkASSERT(span->fOtherIndex + 1 < other->count());
+        SkASSERT(span->fOtherT == 0);
+        SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
+                || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
+                && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
+        int oIndex = 1;
+        do {
+            const SkOpSpan& osSpan = other->span(oIndex);
+            if (osSpan.fFromAngle || osSpan.fT > 0) {
+                break;
+            }
+            ++oIndex;
+            SkASSERT(oIndex < other->count());
+        } while (true);
+        other->addStartSpan(oIndex);
+        angle = span->fFromAngle;
+        oAngle = oSpan->fToAngle;
     }
-    nextAngle->setLastMarked(last);
+    angle->insert(oAngle);
 }
 
-void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-        SkOpAngle::IncludeType includeType) {
-    const SkOpSegment* baseSegment = baseAngle->segment();
-    int sumMiWinding = baseSegment->updateWinding(baseAngle);
-    int sumSuWinding;
-    bool binary = includeType >= SkOpAngle::kBinarySingle;
-    if (binary) {
-        sumSuWinding = baseSegment->updateOppWinding(baseAngle);
-        if (baseSegment->operand()) {
-            SkTSwap<int>(sumMiWinding, sumSuWinding);
+void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+    debugValidate();
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        SkOpSpan& span = fTs[index];
+        if (!span.fMultiple) {
+            continue;
+        }
+        int end = nextExactSpan(index, 1);
+        SkASSERT(end > index + 1);
+        const SkPoint& thisPt = span.fPt;
+        while (index < end - 1) {
+            SkOpSegment* other1 = span.fOther;
+            int oCnt = other1->count();
+            for (int idx2 = index + 1; idx2 < end; ++idx2) {
+                SkOpSpan& span2 = fTs[idx2];
+                SkOpSegment* other2 = span2.fOther;
+                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+                    SkOpSpan& oSpan = other1->fTs[oIdx];
+                    if (oSpan.fOther != other2) {
+                        continue;
+                    }
+                    if (oSpan.fPt == thisPt) {
+                        goto skipExactMatches;
+                    }
+                }
+                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+                    SkOpSpan& oSpan = other1->fTs[oIdx];
+                    if (oSpan.fOther != other2) {
+                        continue;
+                    }
+                    if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
+                        SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
+                        if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
+                                || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
+                            return;
+                        }
+                        if (!way_roughly_equal(span.fOtherT, oSpan.fT)
+                                || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
+                                || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
+                                || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
+                            return;
+                        }
+                        alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
+                                other2, &oSpan, alignedArray);
+                        alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT, 
+                                other1, &oSpan2, alignedArray);
+                        break;
+                    }
+                }
+        skipExactMatches:
+                ;
+            }
+            ++index;
         }
     }
-    SkOpSegment* nextSegment = nextAngle->segment();
-    int maxWinding, sumWinding;
-    SkOpSpanBase* last;
+    debugValidate();
+}
+
+void SkOpSegment::alignRange(int lower, int upper,
+        const SkOpSegment* other, int oLower, int oUpper) {
+    for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
+        const SkOpSpan& oSpan = other->span(oIndex);
+        const SkOpSegment* oOther = oSpan.fOther;
+        if (oOther == this) {
+            continue;
+        }
+        SkOpSpan* matchSpan;
+        int matchIndex;
+        const SkOpSpan* refSpan;
+        for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+            const SkOpSpan& iSpan = this->span(iIndex);
+            const SkOpSegment* iOther = iSpan.fOther;
+            if (iOther == other) {
+                continue;
+            }
+            if (iOther == oOther) {
+                goto nextI;
+            }
+        }
+        {
+            // oSpan does not have a match in this
+            int iCount = this->count();
+            const SkOpSpan* iMatch = NULL;
+            double iMatchTDiff;
+            matchIndex = -1;
+            for (int iIndex = 0; iIndex < iCount; ++iIndex) {
+                const SkOpSpan& iSpan = this->span(iIndex);
+                const SkOpSegment* iOther = iSpan.fOther;
+                if (iOther != oOther) {
+                    continue;
+                }
+                double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
+                if (!iMatch || testTDiff < iMatchTDiff) {
+                    matchIndex = iIndex;
+                    iMatch = &iSpan;
+                    iMatchTDiff = testTDiff;
+                }
+            }
+            if (matchIndex < 0) {
+                continue;  // the entry is missing, & will be picked up later (FIXME: fix it here?)
+            }
+            matchSpan = &this->fTs[matchIndex];
+            refSpan = &this->span(lower);
+            if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
+                goto nextI;
+            }
+            if (matchIndex != lower - 1 && matchIndex != upper + 1) {
+                // the consecutive spans need to be rearranged to get the missing one close 
+                continue;  // FIXME: more work to do
+            }
+        }
+        {
+            this->fixOtherTIndex();
+            SkScalar newT;
+            if (matchSpan->fT != 0 && matchSpan->fT != 1) {
+                newT = matchSpan->fT = refSpan->fT;
+                matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
+            } else {  // leave span at the start or end there and adjust the neighbors
+                newT = matchSpan->fT;
+                for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+                    matchSpan = &this->fTs[iIndex];
+                    matchSpan->fT = newT;
+                    matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
+                }
+            }
+            this->resetSpanFlags();  // fix up small / tiny / done
+            // align ts of other ranges with adjacent spans that match the aligned points
+            lower = SkTMin(lower, matchIndex);
+            while (lower > 0) {
+                const SkOpSpan& span = this->span(lower - 1);
+                if (span.fT != newT) {
+                    break;
+                }
+                --lower;
+            }
+            upper = SkTMax(upper, matchIndex);
+            int last = this->count() - 1;
+            while (upper < last) {
+                const SkOpSpan& span = this->span(upper + 1);
+                if (span.fT != newT) {
+                    break;
+                }
+                ++upper;
+            }
+            for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+                const SkOpSpan& span = this->span(iIndex);
+                SkOpSegment* aOther = span.fOther;
+                int aLower = span.fOtherIndex;
+                SkScalar aT = span.fOtherT;
+                bool aResetFlags = false;
+                while (aLower > 0) {
+                    SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
+                    for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+                        if (aSpan->fPt == this->fTs[iIndex].fPt) {
+                            goto matchFound;
+                        }
+                    }
+                    break;
+            matchFound:
+                    --aLower;
+                }
+                int aUpper = span.fOtherIndex;
+                int aLast = aOther->count() - 1;
+                while (aUpper < aLast) {
+                    SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
+                    for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+                        if (aSpan->fPt == this->fTs[iIndex].fPt) {
+                            goto matchFound2;
+                        }
+                    }
+                    break;
+            matchFound2:
+                    ++aUpper;
+                }
+                if (aOther->fTs[aLower].fT == 0) {
+                    aT = 0;
+                } else if (aOther->fTs[aUpper].fT == 1) {
+                    aT = 1;
+                }
+                bool aFixed = false;
+                for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
+                    SkOpSpan* aSpan = &aOther->fTs[aIndex];
+                    if (aSpan->fT == aT) {
+                        continue;
+                    }
+                    SkASSERT(way_roughly_equal(aSpan->fT, aT));
+                    if (!aFixed) {
+                        aOther->fixOtherTIndex();
+                        aFixed = true;
+                    }
+                    aSpan->fT = aT;
+                    aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
+                    aResetFlags = true;
+                }
+                if (aResetFlags) {
+                    aOther->resetSpanFlags();
+                }
+            }
+        }
+nextI: ;
+    }
+}
+
+void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
+        double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
+        SkTDArray<AlignedSpan>* alignedArray) {
+    AlignedSpan* aligned = alignedArray->append();
+    aligned->fOldPt = oSpan->fPt;
+    aligned->fPt = newPt;
+    aligned->fOldT = oSpan->fT;
+    aligned->fT = newT;
+    aligned->fSegment = this;  // OPTIMIZE: may be unused, can remove
+    aligned->fOther1 = other;
+    aligned->fOther2 = other2;
+    SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
+    oSpan->fPt = newPt;
+//    SkASSERT(way_roughly_equal(oSpan->fT, newT));
+    oSpan->fT = newT;
+//    SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
+    oSpan->fOtherT = otherT;
+}
+
+bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
+    bool aligned = false;
+    SkOpSpan* span = &fTs[index];
+    SkOpSegment* other = span->fOther;
+    int oIndex = span->fOtherIndex;
+    SkOpSpan* oSpan = &other->fTs[oIndex];
+    if (span->fT != thisT) {
+        span->fT = thisT;
+        oSpan->fOtherT = thisT;
+        aligned = true;
+    }
+    if (span->fPt != thisPt) {
+        span->fPt = thisPt;
+        oSpan->fPt = thisPt;
+        aligned = true;
+    }
+    double oT = oSpan->fT;
+    if (oT == 0) {
+        return aligned;
+    }
+    int oStart = other->nextSpan(oIndex, -1) + 1;
+    oSpan = &other->fTs[oStart];
+    int otherIndex = oStart;
+    if (oT == 1) {
+        if (aligned) {
+            while (oSpan->fPt == thisPt && oSpan->fT != 1) {
+                oSpan->fTiny = true;
+                ++oSpan;
+            }
+        }
+        return aligned;
+    }
+    oT = oSpan->fT;
+    int oEnd = other->nextSpan(oIndex, 1);
+    bool oAligned = false;
+    if (oSpan->fPt != thisPt) {
+        oAligned |= other->alignSpan(oStart, oT, thisPt);
+    }
+    while (++otherIndex < oEnd) {
+        SkOpSpan* oNextSpan = &other->fTs[otherIndex];
+        if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
+            oAligned |= other->alignSpan(otherIndex, oT, thisPt);
+        }
+    }
+    if (oAligned) {
+        other->alignSpanState(oStart, oEnd);
+    }
+    return aligned;
+}
+
+void SkOpSegment::alignSpanState(int start, int end) {
+    SkOpSpan* lastSpan = &fTs[--end];
+    bool allSmall = lastSpan->fSmall;
+    bool allTiny = lastSpan->fTiny;
+    bool allDone = lastSpan->fDone;
+    SkDEBUGCODE(int winding = lastSpan->fWindValue);
+    SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
+    int index = start;
+    while (index < end) {
+        SkOpSpan* span = &fTs[index];
+        span->fSmall = allSmall;
+        span->fTiny = allTiny;
+        if (span->fDone != allDone) {
+            span->fDone = allDone;
+            fDoneSpans += allDone ? 1 : -1;
+        }
+        SkASSERT(span->fWindValue == winding);
+        SkASSERT(span->fOppValue == oppWinding);
+        ++index;
+    }
+}
+
+void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    int last = this->count();
+    do {
+        SkOpSpan& span = this->fTs[--last];
+        if (span.fT != 1 && !span.fSmall) {
+            break;
+        }
+        span.fCoincident = true;
+    } while (true);
+    int oIndex = other->count();
+    do {
+        SkOpSpan& oSpan = other->fTs[--oIndex];
+        if (oSpan.fT != 1 && !oSpan.fSmall) {
+            break;
+        }
+        oSpan.fCoincident = true;
+    } while (true);
+    do {
+        SkOpSpan* test = &this->fTs[index];
+        int baseWind = test->fWindValue;
+        int baseOpp = test->fOppValue;
+        int endIndex = index;
+        while (++endIndex <= last) {
+            SkOpSpan* endSpan = &this->fTs[endIndex];
+            SkASSERT(endSpan->fT < 1);
+            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+                break;
+            }
+            endSpan->fCoincident = true;
+        }
+        SkOpSpan* oTest = &other->fTs[oIndex];
+        int oBaseWind = oTest->fWindValue;
+        int oBaseOpp = oTest->fOppValue;
+        int oStartIndex = oIndex;
+        while (--oStartIndex >= 0) {
+            SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
+            if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
+                break;
+            }
+            oStartSpan->fCoincident = true;
+        }
+        bool decrement = baseWind && oBaseWind;
+        bool bigger = baseWind >= oBaseWind;
+        do {
+            SkASSERT(test->fT < 1);
+            if (decrement) {
+                if (binary && bigger) {
+                    test->fOppValue--;
+                } else {
+                    decrementSpan(test);
+                }
+            }
+            test->fCoincident = true;
+            test = &fTs[++index];
+        } while (index < endIndex);
+        do {
+            SkASSERT(oTest->fT < 1);
+            if (decrement) {
+                if (binary && !bigger) {
+                    oTest->fOppValue--;
+                } else {
+                    other->decrementSpan(oTest);
+                }
+            }
+            oTest->fCoincident = true;
+            oTest = &other->fTs[--oIndex];
+        } while (oIndex > oStartIndex);
+    } while (index <= last && oIndex >= 0);
+    SkASSERT(index > last);
+    SkASSERT(oIndex < 0);
+}
+
+void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    int last = this->count();
+    do {
+        SkOpSpan& span = this->fTs[--last];
+        if (span.fT != 1 && !span.fSmall) {
+            break;
+        }
+        span.fCoincident = true;
+    } while (true);
+    int oIndex = 0;
+    int oLast = other->count();
+    do {
+        SkOpSpan& oSpan = other->fTs[--oLast];
+        if (oSpan.fT != 1 && !oSpan.fSmall) {
+            break;
+        }
+        oSpan.fCoincident = true;
+    } while (true);
+    do {
+        SkOpSpan* test = &this->fTs[index];
+        int baseWind = test->fWindValue;
+        int baseOpp = test->fOppValue;
+        int endIndex = index;
+        SkOpSpan* endSpan;
+        while (++endIndex <= last) {
+            endSpan = &this->fTs[endIndex];
+            SkASSERT(endSpan->fT < 1);
+            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+                break;
+            }
+            endSpan->fCoincident = true;
+        }
+        SkOpSpan* oTest = &other->fTs[oIndex];
+        int oBaseWind = oTest->fWindValue;
+        int oBaseOpp = oTest->fOppValue;
+        int oEndIndex = oIndex;
+        SkOpSpan* oEndSpan;
+        while (++oEndIndex <= oLast) {
+            oEndSpan = &this->fTs[oEndIndex];
+            SkASSERT(oEndSpan->fT < 1);
+            if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
+                break;
+            }
+            oEndSpan->fCoincident = true;
+        }
+        // consolidate the winding count even if done
+        if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
+            if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+                bumpCoincidentBlind(binary, index, endIndex);
+                other->bumpCoincidentOBlind(oIndex, oEndIndex);
+            } else {
+                other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
+                bumpCoincidentOBlind(index, endIndex);
+            }
+        }
+        index = endIndex;
+        oIndex = oEndIndex;
+    } while (index <= last && oIndex <= oLast);
+    SkASSERT(index > last);
+    SkASSERT(oIndex > oLast);
+}
+
+void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
+    const SkOpSpan& oTest = fTs[index];
+    int oWindValue = oTest.fWindValue;
+    int oOppValue = oTest.fOppValue;
     if (binary) {
-        int oppMaxWinding, oppSumWinding;
-        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
-                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
-                nextAngle);
+        SkTSwap<int>(oWindValue, oOppValue);
+    }
+    do {
+        (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
+    } while (++index < endIndex);
+}
+
+bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
+        SkTArray<SkPoint, true>* outsideTs) {
+    int index = *indexPtr;
+    int oWindValue = oTest.fWindValue;
+    int oOppValue = oTest.fOppValue;
+    if (binary) {
+        SkTSwap<int>(oWindValue, oOppValue);
+    }
+    SkOpSpan* const test = &fTs[index];
+    SkOpSpan* end = test;
+    const SkPoint& oStartPt = oTest.fPt;
+    do {
+        if (end->fDone && !end->fTiny && !end->fSmall) {  // extremely large paths trigger this
+            return false;
+        }
+        if (bumpSpan(end, oWindValue, oOppValue)) {
+            TrackOutside(outsideTs, oStartPt);
+        }
+        end = &fTs[++index];
+    } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
+    *indexPtr = index;
+    return true;
+}
+
+void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
+    do {
+        zeroSpan(&fTs[index]);
+    } while (++index < endIndex);
+}
+
+// because of the order in which coincidences are resolved, this and other
+// may not have the same intermediate points. Compute the corresponding
+// intermediate T values (using this as the master, other as the follower)
+// and walk other conditionally -- hoping that it catches up in the end
+bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
+        SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
+    int oIndex = *oIndexPtr;
+    SkOpSpan* const oTest = &fTs[oIndex];
+    SkOpSpan* oEnd = oTest;
+    const SkPoint& oStartPt = oTest->fPt;
+    double oStartT = oTest->fT;
+#if 0  // FIXME : figure out what disabling this breaks
+    const SkPoint& startPt = test.fPt;
+    // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
+    if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
+        TrackOutside(oOutsidePts, startPt);
+    }
+#endif
+    bool foundEnd = false;
+    while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
+        foundEnd |= oEndPt == oEnd->fPt;
+        zeroSpan(oEnd);
+        oEnd = &fTs[++oIndex];
+    }
+    *oIndexPtr = oIndex;
+    return foundEnd;
+}
+
+// FIXME: need to test this case:
+// contourA has two segments that are coincident
+// contourB has two segments that are coincident in the same place
+// each ends up with +2/0 pairs for winding count
+// since logic below doesn't transfer count (only increments/decrements) can this be
+// resolved to +4/0 ?
+
+// set spans from start to end to increment the greater by one and decrement
+// the lesser
+bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+        SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    while (startPt != fTs[index].fPt) {
+        SkASSERT(index < fTs.count());
+        ++index;
+    }
+    double startT = fTs[index].fT;
+    while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
+        --index;
+    }
+    int oIndex = 0;
+    while (startPt != other->fTs[oIndex].fPt) {
+        SkASSERT(oIndex < other->fTs.count());
+        ++oIndex;
+    }
+    double oStartT = other->fTs[oIndex].fT;
+    while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
+        --oIndex;
+    }
+    SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
+    SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+    SkOpSpan* test = &fTs[index];
+    const SkPoint* testPt = &test->fPt;
+    double testT = test->fT;
+    SkOpSpan* oTest = &other->fTs[oIndex];
+    const SkPoint* oTestPt = &oTest->fPt;
+    // paths with extreme data will fail this test and eject out of pathops altogether later on
+    // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+    do {
+        SkASSERT(test->fT < 1);
+        if (oTest->fT == 1) {
+            // paths with extreme data may be so mismatched that we fail here
+            return false;
+        }
+
+        // consolidate the winding count even if done
+        bool foundEnd = false;
+        if ((test->fWindValue == 0 && test->fOppValue == 0)
+                || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
+            SkDEBUGCODE(int firstWind = test->fWindValue);
+            SkDEBUGCODE(int firstOpp = test->fOppValue);
+            do {
+                SkASSERT(firstWind == fTs[index].fWindValue);
+                SkASSERT(firstOpp == fTs[index].fOppValue);
+                ++index;
+                SkASSERT(index < fTs.count());
+            } while (*testPt == fTs[index].fPt);
+            SkDEBUGCODE(firstWind = oTest->fWindValue);
+            SkDEBUGCODE(firstOpp = oTest->fOppValue);
+            do {
+                SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
+                SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
+                ++oIndex;
+                SkASSERT(oIndex < other->fTs.count());
+            } while (*oTestPt == other->fTs[oIndex].fPt);
+        } else {
+            if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+                if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
+                    return false;
+                }
+                foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
+            } else {
+                if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+                    return false;
+                }
+                foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
+            }
+        }
+        test = &fTs[index];
+        testPt = &test->fPt;
+        testT = test->fT;
+        oTest = &other->fTs[oIndex];
+        oTestPt = &oTest->fPt;
+        if (endPt == *testPt || precisely_equal(endT, testT)) {
+            break;
+        }
+        if (0 && foundEnd) {  // FIXME: this is likely needed but wait until a test case triggers it
+            break;
+        }
+//        SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+    } while (endPt != *oTestPt);
+    // in rare cases, one may have ended before the other
+    if (endPt != *testPt && !precisely_equal(endT, testT)) {
+        int lastWind = test[-1].fWindValue;
+        int lastOpp = test[-1].fOppValue;
+        bool zero = lastWind == 0 && lastOpp == 0;
+        do {
+            if (test->fWindValue || test->fOppValue) {
+                test->fWindValue = lastWind;
+                test->fOppValue = lastOpp;
+                if (zero) {
+                    SkASSERT(!test->fDone);
+                    test->fDone = true;
+                    ++fDoneSpans;
+                }
+            }
+            test = &fTs[++index];
+            testPt = &test->fPt;
+        } while (endPt != *testPt);
+    }
+    if (endPt != *oTestPt) {
+        // look ahead to see if zeroing more spans will allows us to catch up
+        int oPeekIndex = oIndex;
+        bool success = true;
+        SkOpSpan* oPeek;
+        int oCount = other->count();
+        do {
+            oPeek = &other->fTs[oPeekIndex];
+            if (++oPeekIndex == oCount) {
+                success = false;
+                break;
+            }
+        } while (endPt != oPeek->fPt);
+        if (success) {
+            // make sure the matching point completes the coincidence span
+            success = false;
+            do {
+                if (oPeek->fOther == this) {
+                    success = true;
+                    break;
+                }
+                if (++oPeekIndex == oCount) {
+                    break;
+                }
+                oPeek = &other->fTs[oPeekIndex];
+            } while (endPt == oPeek->fPt);
+        }
+        if (success) {
+            do {
+                if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+                    if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
+                        break;
+                    }
+                } else {
+                    if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+                        return false;
+                    }
+                }
+                oTest = &other->fTs[oIndex];
+                oTestPt = &oTest->fPt;
+            } while (endPt != *oTestPt);
+        }
+    }
+    int outCount = outsidePts.count();
+    if (!done() && outCount) {
+        addCoinOutsides(outsidePts[0], endPt, other);
+    }
+    if (!other->done() && oOutsidePts.count()) {
+        other->addCoinOutsides(oOutsidePts[0], endPt, this);
+    }
+    setCoincidentRange(startPt, endPt, other);
+    other->setCoincidentRange(startPt, endPt, this);
+    return true;
+}
+
+// FIXME: this doesn't prevent the same span from being added twice
+// fix in caller, SkASSERT here?
+// FIXME: this may erroneously reject adds for cubic loops
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+        const SkPoint& pt, const SkPoint& pt2) {
+    int tCount = fTs.count();
+    for (int tIndex = 0; tIndex < tCount; ++tIndex) {
+        const SkOpSpan& span = fTs[tIndex];
+        if (!approximately_negative(span.fT - t)) {
+            break;
+        }
+        if (span.fOther == other) {
+            bool tsMatch = approximately_equal(span.fT, t);
+            bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
+            // FIXME: add cubic loop detecting logic here
+            // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
+            // or if a new bit is added ala fOtherLoop
+            if (tsMatch || otherTsMatch) {
+#if DEBUG_ADD_T_PAIR
+                SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
+                        __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+                return NULL;
+            }
+        }
+    }
+    int oCount = other->count();
+    for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+        const SkOpSpan& oSpan = other->span(oIndex);
+        if (!approximately_negative(oSpan.fT - otherT)) {
+            break;
+        }
+        if (oSpan.fOther == this) {
+            bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
+            bool tsMatch = approximately_equal(oSpan.fOtherT, t);
+            if (otherTsMatch || tsMatch) {
+#if DEBUG_ADD_T_PAIR
+                SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
+                        __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+                return NULL;
+            }
+        }
+    }
+#if DEBUG_ADD_T_PAIR
+    SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
+            __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+    SkASSERT(other != this);
+    int insertedAt = addT(other, pt, t);
+    int otherInsertedAt = other->addT(this, pt2, otherT);
+    this->addOtherT(insertedAt, otherT, otherInsertedAt);
+    other->addOtherT(otherInsertedAt, t, insertedAt);
+    this->matchWindingValue(insertedAt, t, borrowWind);
+    other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
+    SkOpSpan& span = this->fTs[insertedAt];
+    if (pt != pt2) {
+        span.fNear = true;
+        SkOpSpan& oSpan = other->fTs[otherInsertedAt];
+        oSpan.fNear = true;
+    }
+    // if the newly inserted spans match a neighbor on one but not the other, make them agree
+    int lower = this->nextExactSpan(insertedAt, -1) + 1;
+    int upper = this->nextExactSpan(insertedAt, 1) - 1;
+    if (upper < 0) {
+        upper = this->count() - 1;
+    }
+    int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
+    int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
+    if (oUpper < 0) {
+        oUpper = other->count() - 1;
+    }
+    if (lower == upper && oLower == oUpper) {
+        return &span;
+    }
+#if DEBUG_CONCIDENT
+    SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
+            debugID(), lower, upper, other->debugID(), oLower, oUpper);
+#endif
+    // find the nearby spans in one range missing in the other
+    this->alignRange(lower, upper, other, oLower, oUpper);
+    other->alignRange(oLower, oUpper, this, lower, upper);
+    return &span;
+}
+
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+                           const SkPoint& pt) {
+    return addTPair(t, other, otherT, borrowWind, pt, pt);
+}
+
+bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
+    const SkPoint midPt = ptAtT(midT);
+    SkPathOpsBounds bounds;
+    bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
+    bounds.sort();
+    return bounds.almostContains(midPt);
+}
+
+bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
+    if (lesser > greater) {
+        SkTSwap<int>(lesser, greater);
+    }
+    return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
+}
+
+// in extreme cases (like the buffer overflow test) return false to abort
+// for now, if one t value represents two different points, then the values are too extreme
+// to generate meaningful results
+bool SkOpSegment::calcAngles() {
+    int spanCount = fTs.count();
+    if (spanCount <= 2) {
+        return spanCount == 2;
+    }
+    int index = 1;
+    const SkOpSpan* firstSpan = &fTs[index];
+    int activePrior = checkSetAngle(0);
+    const SkOpSpan* span = &fTs[0];
+    if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
+        index = findStartSpan(0);  // curve start intersects
+        if (fTs[index].fT == 0) {
+            return false;
+        }
+        SkASSERT(index > 0);
+        if (activePrior >= 0) {
+            addStartSpan(index);
+        }
+    }
+    bool addEnd;
+    int endIndex = spanCount - 1;
+    span = &fTs[endIndex - 1];
+    if ((addEnd = span->fT == 1 || span->fTiny)) {  // if curve end intersects
+        endIndex = findEndSpan(endIndex);
+        SkASSERT(endIndex > 0);
     } else {
-        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
-                &maxWinding, &sumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+        addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
     }
-    nextAngle->setLastMarked(last);
+    SkASSERT(endIndex >= index);
+    int prior = 0;
+    while (index < endIndex) {
+        const SkOpSpan& fromSpan = fTs[index];  // for each intermediate intersection
+        const SkOpSpan* lastSpan;
+        span = &fromSpan;
+        int start = index;
+        do {
+            lastSpan = span;
+            span = &fTs[++index];
+            SkASSERT(index < spanCount);
+            if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
+                break;
+            }
+            if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
+                return false;
+            }
+        } while (true);
+        SkOpAngle* angle = NULL;
+        SkOpAngle* priorAngle;
+        if (activePrior >= 0) {
+            int pActive = firstActive(prior);
+            SkASSERT(pActive < start);
+            priorAngle = &fAngles.push_back();
+            priorAngle->set(this, start, pActive);
+        }
+        int active = checkSetAngle(start);
+        if (active >= 0) {
+            SkASSERT(active < index);
+            angle = &fAngles.push_back();
+            angle->set(this, active, index);
+        }
+    #if DEBUG_ANGLE
+        debugCheckPointsEqualish(start, index);
+    #endif
+        prior = start;
+        do {
+            const SkOpSpan* startSpan = &fTs[start - 1];
+            if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
+                    || startSpan->fToAngle) {
+                break;
+            }
+            --start;
+        } while (start > 0);
+        do {
+            if (activePrior >= 0) {
+                SkASSERT(fTs[start].fFromAngle == NULL);
+                fTs[start].fFromAngle = priorAngle;
+            }
+            if (active >= 0) {
+                SkASSERT(fTs[start].fToAngle == NULL);
+                fTs[start].fToAngle = angle;
+            }
+        } while (++start < index);
+        activePrior = active;
+    }
+    if (addEnd && activePrior >= 0) {
+        addEndSpan(endIndex);
+    }
+    return true;
+}
+
+int SkOpSegment::checkSetAngle(int tIndex) const {
+    const SkOpSpan* span = &fTs[tIndex];
+    while (span->fTiny /* || span->fSmall */) {
+        span = &fTs[++tIndex];
+    }
+    return isCanceled(tIndex) ? -1 : tIndex;
 }
 
 // at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
-        SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
     SkASSERT(includeType != SkOpAngle::kUnaryXor);
-    SkOpAngle* firstAngle = this->spanToAngle(end, start);
+    SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
     if (NULL == firstAngle || NULL == firstAngle->next()) {
         return SK_NaN32;
     }
@@ -614,7 +1898,7 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
             baseAngle = NULL;
             continue;
         }
-        int testWinding = angle->starter()->windSum();
+        int testWinding = angle->segment()->windSum(angle);
         if (SK_MinS32 != testWinding) {
             baseAngle = angle;
             tryReverse = true;
@@ -622,10 +1906,10 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
         }
         if (baseAngle) {
             ComputeOneSum(baseAngle, angle, includeType);
-            baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+            baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
         }
     } while (next != firstAngle);
-    if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
+    if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
         firstAngle = baseAngle;
         tryReverse = true;
     }
@@ -641,145 +1925,1101 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
                 baseAngle = NULL;
                 continue;
             }
-            int testWinding = angle->starter()->windSum();
+            int testWinding = angle->segment()->windSum(angle);
             if (SK_MinS32 != testWinding) {
                 baseAngle = angle;
                 continue;
             }
             if (baseAngle) {
                 ComputeOneSumReverse(baseAngle, angle, includeType);
-                baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+                baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
             }
         } while (prior != firstAngle);
     }
-    return start->starter(end)->windSum();
+    int minIndex = SkMin32(startIndex, endIndex);
+    return windSum(minIndex);
 }
 
-SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
-        SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
-    SkScalar bottom = fBounds.fBottom;
-    *vertical = false;
+void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+        SkOpAngle::IncludeType includeType) {
+    const SkOpSegment* baseSegment = baseAngle->segment();
+    int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
+    int sumSuWinding;
+    bool binary = includeType >= SkOpAngle::kBinarySingle;
+    if (binary) {
+        sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
+        if (baseSegment->operand()) {
+            SkTSwap<int>(sumMiWinding, sumSuWinding);
+        }
+    }
+    SkOpSegment* nextSegment = nextAngle->segment();
+    int maxWinding, sumWinding;
+    SkOpSpan* last;
+    if (binary) {
+        int oppMaxWinding, oppSumWinding;
+        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+                nextAngle);
+    } else {
+        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+                &maxWinding, &sumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+    }
+    nextAngle->setLastMarked(last);
+}
+
+void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+        SkOpAngle::IncludeType includeType) {
+    const SkOpSegment* baseSegment = baseAngle->segment();
+    int sumMiWinding = baseSegment->updateWinding(baseAngle);
+    int sumSuWinding;
+    bool binary = includeType >= SkOpAngle::kBinarySingle;
+    if (binary) {
+        sumSuWinding = baseSegment->updateOppWinding(baseAngle);
+        if (baseSegment->operand()) {
+            SkTSwap<int>(sumMiWinding, sumSuWinding);
+        }
+    }
+    SkOpSegment* nextSegment = nextAngle->segment();
+    int maxWinding, sumWinding;
+    SkOpSpan* last;
+    if (binary) {
+        int oppMaxWinding, oppSumWinding;
+        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+                nextAngle);
+    } else {
+        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+                &maxWinding, &sumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+    }
+    nextAngle->setLastMarked(last);
+}
+
+bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
+    int step = index < endIndex ? 1 : -1;
+    do {
+        const SkOpSpan& span = this->span(index);
+        if (span.fPt == pt) {
+            const SkOpSpan& endSpan = this->span(endIndex);
+            return span.fT == endSpan.fT && pt != endSpan.fPt;
+        }
+        index += step;
+    } while (index != endIndex);
+    return false;
+}
+
+bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (t < span.fT) {
+            return false;
+        }
+        if (t == span.fT) {
+            if (other != span.fOther) {
+                continue;
+            }
+            if (other->fVerb != SkPath::kCubic_Verb) {
+                return true;
+            }
+            if (!other->fLoop) {
+                return true;
+            }
+            double otherMidT = (otherT + span.fOtherT) / 2;
+            SkPoint otherPt = other->ptAtT(otherMidT);
+            return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
+        }
+    }
+    return false;
+}
+
+int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
+                              bool* hitSomething, double mid, bool opp, bool current) const {
+    SkScalar bottom = fBounds.fBottom;
+    int bestTIndex = -1;
     if (bottom <= *bestY) {
-        return NULL;
+        return bestTIndex;
     }
     SkScalar top = fBounds.fTop;
     if (top >= basePt.fY) {
-        return NULL;
+        return bestTIndex;
     }
     if (fBounds.fLeft > basePt.fX) {
-        return NULL;
+        return bestTIndex;
     }
     if (fBounds.fRight < basePt.fX) {
-        return NULL;
+        return bestTIndex;
     }
     if (fBounds.fLeft == fBounds.fRight) {
         // if vertical, and directly above test point, wait for another one
-        *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
-        return NULL;
+        return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
+    }
+    // intersect ray starting at basePt with edge
+    SkIntersections intersections;
+    // OPTIMIZE: use specialty function that intersects ray with curve,
+    // returning t values only for curve (we don't care about t on ray)
+    intersections.allowNear(false);
+    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
+            (fPts, top, bottom, basePt.fX, false);
+    if (pts == 0 || (current && pts == 1)) {
+        return bestTIndex;
+    }
+    if (current) {
+        SkASSERT(pts > 1);
+        int closestIdx = 0;
+        double closest = fabs(intersections[0][0] - mid);
+        for (int idx = 1; idx < pts; ++idx) {
+            double test = fabs(intersections[0][idx] - mid);
+            if (closest > test) {
+                closestIdx = idx;
+                closest = test;
+            }
+        }
+        intersections.quickRemoveOne(closestIdx, --pts);
+    }
+    double bestT = -1;
+    for (int index = 0; index < pts; ++index) {
+        double foundT = intersections[0][index];
+        if (approximately_less_than_zero(foundT)
+                    || approximately_greater_than_one(foundT)) {
+            continue;
+        }
+        SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
+        if (approximately_negative(testY - *bestY)
+                || approximately_negative(basePt.fY - testY)) {
+            continue;
+        }
+        if (pts > 1 && fVerb == SkPath::kLine_Verb) {
+            return SK_MinS32;  // if the intersection is edge on, wait for another one
+        }
+        if (fVerb > SkPath::kLine_Verb) {
+            SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
+            if (approximately_zero(dx)) {
+                return SK_MinS32;  // hit vertical, wait for another one
+            }
+        }
+        *bestY = testY;
+        bestT = foundT;
+    }
+    if (bestT < 0) {
+        return bestTIndex;
+    }
+    SkASSERT(bestT >= 0);
+    SkASSERT(bestT <= 1);
+    int start;
+    int end = 0;
+    do {
+        start = end;
+        end = nextSpan(start, 1);
+    } while (fTs[end].fT < bestT);
+    // FIXME: see next candidate for a better pattern to find the next start/end pair
+    while (start + 1 < end && fTs[start].fDone) {
+        ++start;
+    }
+    if (!isCanceled(start)) {
+        *hitT = bestT;
+        bestTIndex = start;
+        *hitSomething = true;
+    }
+    return bestTIndex;
+}
+
+bool SkOpSegment::decrementSpan(SkOpSpan* span) {
+    SkASSERT(span->fWindValue > 0);
+    if (--(span->fWindValue) == 0) {
+        if (!span->fOppValue && !span->fDone) {
+            span->fDone = true;
+            ++fDoneSpans;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
+    SkASSERT(!span->fDone || span->fTiny || span->fSmall);
+    span->fWindValue += windDelta;
+    SkASSERT(span->fWindValue >= 0);
+    span->fOppValue += oppDelta;
+    SkASSERT(span->fOppValue >= 0);
+    if (fXor) {
+        span->fWindValue &= 1;
+    }
+    if (fOppXor) {
+        span->fOppValue &= 1;
+    }
+    if (!span->fWindValue && !span->fOppValue) {
+        if (!span->fDone) {
+            span->fDone = true;
+            ++fDoneSpans;
+        }
+        return true;
+    }
+    return false;
+}
+
+const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
+    const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
+    const SkOpSpan* beginSpan = fTs.begin();
+    const SkPoint& testPt = thisSpan.fPt;
+    while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
+        --firstSpan;
+    }
+    return *firstSpan;
+}
+
+const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
+    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be small
+    const SkOpSpan* lastSpan = &thisSpan;  // find the end
+    const SkPoint& testPt = thisSpan.fPt;
+    while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
+        ++lastSpan;
+    }
+    return *lastSpan;
+}
+
+// with a loop, the comparison is move involved
+// scan backwards and forwards to count all matching points
+// (verify that there are twp scans marked as loops)
+// compare that against 2 matching scans for loop plus other results
+bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
+    const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
+    const SkOpSpan& lastSpan = this->lastSpan(thisSpan);  // find the end
+    double firstLoopT = -1, lastLoopT = -1;
+    const SkOpSpan* testSpan = &firstSpan - 1;
+    while (++testSpan <= &lastSpan) {
+        if (testSpan->fLoop) {
+            firstLoopT = testSpan->fT;
+            break;
+        }
+    }
+    testSpan = &lastSpan + 1;
+    while (--testSpan >= &firstSpan) {
+        if (testSpan->fLoop) {
+            lastLoopT = testSpan->fT;
+            break;
+        }
+    }
+    SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
+    if (firstLoopT == -1) {
+        return false;
+    }
+    SkASSERT(firstLoopT < lastLoopT);
+    testSpan = &firstSpan - 1;
+    smallCounts[0] = smallCounts[1] = 0;
+    while (++testSpan <= &lastSpan) {
+        SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
+                approximately_equal(testSpan->fT, lastLoopT) == 1);
+        smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
+    }
+    return true;
+}
+
+double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+        double hiEnd, const SkOpSegment* other, int thisStart) {
+    if (max >= hiEnd) {
+        return -1;
+    }
+    int end = findOtherT(hiEnd, ref);
+    if (end < 0) {
+        return -1;
+    }
+    double tHi = span(end).fT;
+    double tLo, refLo;
+    if (thisStart >= 0) {
+        tLo = span(thisStart).fT;
+        refLo = min;
+    } else {
+        int start1 = findOtherT(loEnd, ref);
+        SkASSERT(start1 >= 0);
+        tLo = span(start1).fT;
+        refLo = loEnd;
+    }
+    double missingT = (max - refLo) / (hiEnd - refLo);
+    missingT = tLo + missingT * (tHi - tLo);
+    return missingT;
+}
+
+double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+        double hiEnd, const SkOpSegment* other, int thisEnd) {
+    if (min <= loEnd) {
+        return -1;
+    }
+    int start = findOtherT(loEnd, ref);
+    if (start < 0) {
+        return -1;
+    }
+    double tLo = span(start).fT;
+    double tHi, refHi;
+    if (thisEnd >= 0) {
+        tHi = span(thisEnd).fT;
+        refHi = max;
+    } else {
+        int end1 = findOtherT(hiEnd, ref);
+        if (end1 < 0) {
+            return -1;
+        }
+        tHi = span(end1).fT;
+        refHi = hiEnd;
+    }
+    double missingT = (min - loEnd) / (refHi - loEnd);
+    missingT = tLo + missingT * (tHi - tLo);
+    return missingT;
+}
+
+// see if spans with two or more intersections have the same number on the other end
+void SkOpSegment::checkDuplicates() {
+    debugValidate();
+    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+    int index;
+    int endIndex = 0;
+    bool endFound;
+    do {
+        index = endIndex;
+        endIndex = nextExactSpan(index, 1);
+        if ((endFound = endIndex < 0)) {
+            endIndex = count();
+        }
+        int dupCount = endIndex - index;
+        if (dupCount < 2) {
+            continue;
+        }
+        do {
+            const SkOpSpan* thisSpan = &fTs[index];
+            if (thisSpan->fNear) {
+                continue;
+            }
+            SkOpSegment* other = thisSpan->fOther;
+            int oIndex = thisSpan->fOtherIndex;
+            int oStart = other->nextExactSpan(oIndex, -1) + 1;
+            int oEnd = other->nextExactSpan(oIndex, 1);
+            if (oEnd < 0) {
+                oEnd = other->count();
+            }
+            int oCount = oEnd - oStart;
+            // force the other to match its t and this pt if not on an end point
+            if (oCount != dupCount) {
+                MissingSpan& missing = missingSpans.push_back();
+                missing.fOther = NULL;
+                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+                missing.fPt = thisSpan->fPt;
+                const SkOpSpan& oSpan = other->span(oIndex);
+                if (oCount > dupCount) {
+                    missing.fSegment = this;
+                    missing.fT = thisSpan->fT;
+                    other->checkLinks(&oSpan, &missingSpans);
+                } else {
+                    missing.fSegment = other;
+                    missing.fT = oSpan.fT;
+                    checkLinks(thisSpan, &missingSpans);
+                }
+                if (!missingSpans.back().fOther) {
+                    missingSpans.pop_back();
+                }
+            }
+        } while (++index < endIndex);
+    } while (!endFound);
+    int missingCount = missingSpans.count();
+    if (missingCount == 0) {
+        return;
+    }
+    SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
+    for (index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        SkOpSegment* missingOther = missing.fOther;
+        if (missing.fSegment == missing.fOther) {
+            continue;
+        }
+#if 0  // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
+       // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
+        if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
+#if DEBUG_DUPLICATES
+            SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
+                    missing.fT, missing.fOther->fID, missing.fOtherT);
+#endif
+            continue;
+        }
+        if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
+#if DEBUG_DUPLICATES
+            SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
+                    missing.fOtherT, missing.fSegment->fID, missing.fT);
+#endif
+            continue;
+        }
+#endif
+        // skip if adding would insert point into an existing coincindent span
+        if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
+                && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
+            continue;
+        }
+        // skip if the created coincident spans are small
+        if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
+                && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
+            continue;
+        }
+        const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
+                missing.fOtherT, false, missing.fPt);
+        if (added && added->fSmall) {
+            missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
+        }
+    }
+    for (index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        missing.fSegment->fixOtherTIndex();
+        missing.fOther->fixOtherTIndex();
+    }
+    for (index = 0; index < missingCoincidence.count(); ++index) {
+        MissingSpan& missing = missingCoincidence[index];
+        missing.fSegment->fixOtherTIndex();
+    }
+    debugValidate();
+}
+
+// look to see if the curve end intersects an intermediary that intersects the other
+bool SkOpSegment::checkEnds() {
+    debugValidate();
+    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+    int count = fTs.count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        double otherT = span.fOtherT;
+        if (otherT != 0 && otherT != 1) { // only check ends
+            continue;
+        }
+        const SkOpSegment* other = span.fOther;
+        // peek start/last describe the range of spans that match the other t of this span
+        int peekStart = span.fOtherIndex;
+        while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
+            ;
+        int otherCount = other->fTs.count();
+        int peekLast = span.fOtherIndex;
+        while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
+            ;
+        if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
+            continue;
+        }
+        // t start/last describe the range of spans that match the t of this span
+        double t = span.fT;
+        double tBottom = -1;
+        int tStart = -1;
+        int tLast = count;
+        bool lastSmall = false;
+        double afterT = t;
+        for (int inner = 0; inner < count; ++inner) {
+            double innerT = fTs[inner].fT;
+            if (innerT <= t && innerT > tBottom) {
+                if (innerT < t || !lastSmall) {
+                    tStart = inner - 1;
+                }
+                tBottom = innerT;
+            }
+            if (innerT > afterT) {
+                if (t == afterT && lastSmall) {
+                    afterT = innerT;
+                } else {
+                    tLast = inner;
+                    break;
+                }
+            }
+            lastSmall = innerT <= t ? fTs[inner].fSmall : false;
+        }
+        for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
+            if (peekIndex == span.fOtherIndex) {  // skip the other span pointed to by this span
+                continue;
+            }
+            const SkOpSpan& peekSpan = other->fTs[peekIndex];
+            SkOpSegment* match = peekSpan.fOther;
+            if (match->done()) {
+                continue;  // if the edge has already been eaten (likely coincidence), ignore it
+            }
+            const double matchT = peekSpan.fOtherT;
+            // see if any of the spans match the other spans
+            for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
+                const SkOpSpan& tSpan = fTs[tIndex];
+                if (tSpan.fOther == match) {
+                    if (tSpan.fOtherT == matchT) {
+                        goto nextPeekIndex;
+                    }
+                    double midT = (tSpan.fOtherT + matchT) / 2;
+                    if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
+                        goto nextPeekIndex;
+                    }
+                }
+            }
+            if (missingSpans.count() > 0) {
+                const MissingSpan& lastMissing = missingSpans.back();
+                if (lastMissing.fT == t
+                        && lastMissing.fOther == match
+                        && lastMissing.fOtherT == matchT) {
+                    SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
+                    continue;
+                }
+            }
+            if (this == match) {
+                return false; // extremely large paths can trigger this
+            }
+#if DEBUG_CHECK_ALIGN
+            SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
+                    __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
+#endif
+            // this segment is missing a entry that the other contains
+            // remember so we can add the missing one and recompute the indices
+            {
+                MissingSpan& missing = missingSpans.push_back();
+                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+                missing.fT = t;
+                SkASSERT(this != match);
+                missing.fOther = match;
+                missing.fOtherT = matchT;
+                missing.fPt = peekSpan.fPt;
+            }
+            break;
+nextPeekIndex:
+            ;
+        }
+    }
+    if (missingSpans.count() == 0) {
+        debugValidate();
+        return true;
+    }
+    debugValidate();
+    int missingCount = missingSpans.count();
+    for (int index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        if (this != missing.fOther) {
+            addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
+        }
+    }
+    fixOtherTIndex();
+    // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
+    // avoid this
+    for (int index = 0; index < missingCount; ++index)  {
+        missingSpans[index].fOther->fixOtherTIndex();
+    }
+    debugValidate();
+    return true;
+}
+
+void SkOpSegment::checkLinks(const SkOpSpan* base,
+        SkTArray<MissingSpan, true>* missingSpans) const {
+    const SkOpSpan* first = fTs.begin();
+    const SkOpSpan* last = fTs.end() - 1;
+    SkASSERT(base >= first && last >= base);
+    const SkOpSegment* other = base->fOther;
+    const SkOpSpan* oFirst = other->fTs.begin();
+    const SkOpSpan* oLast = other->fTs.end() - 1;
+    const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
+    const SkOpSpan* test = base;
+    const SkOpSpan* missing = NULL;
+    while (test > first && (--test)->fPt == base->fPt) {
+        if (this == test->fOther) {
+            continue;
+        }
+        CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
+    }
+    test = base;
+    while (test < last && (++test)->fPt == base->fPt) {
+        SkASSERT(this != test->fOther || test->fLoop);
+        CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
+    }
+}
+
+// see if spans with two or more intersections all agree on common t and point values
+void SkOpSegment::checkMultiples() {
+    debugValidate();
+    int index;
+    int end = 0;
+    while (fTs[++end].fT == 0)
+        ;
+    while (fTs[end].fT < 1) {
+        int start = index = end;
+        end = nextExactSpan(index, 1);
+        if (end <= index) {
+            return;  // buffer overflow example triggers this
+        }
+        if (index + 1 == end) {
+            continue;
+        }
+        // force the duplicates to agree on t and pt if not on the end
+        SkOpSpan& span = fTs[index];
+        double thisT = span.fT;
+        const SkPoint& thisPt = span.fPt;
+        span.fMultiple = true;
+        bool aligned = false;
+        while (++index < end) {
+            aligned |= alignSpan(index, thisT, thisPt);
+        }
+        if (aligned) {
+            alignSpanState(start, end);
+        }
+        fMultiples = true;
+    }
+    debugValidate();
+}
+
+void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
+        const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
+        SkTArray<MissingSpan, true>* missingSpans) {
+    SkASSERT(oSpan->fPt == test->fPt);
+    const SkOpSpan* oTest = oSpan;
+    while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
+        if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
+            return;
+        }
+    }
+    oTest = oSpan;
+    while (oTest < oLast && (++oTest)->fPt == test->fPt) {
+        if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
+            return;
+        }
+    }
+    if (*missingPtr) {
+        missingSpans->push_back();
+    }
+    MissingSpan& lastMissing = missingSpans->back();
+    if (*missingPtr) {
+        lastMissing = missingSpans->end()[-2];
+    }
+    *missingPtr = test;
+    lastMissing.fOther = test->fOther;
+    lastMissing.fOtherT = test->fOtherT;
+}
+
+bool SkOpSegment::checkSmall(int index) const {
+    if (fTs[index].fSmall) {
+        return true;
+    }
+    double tBase = fTs[index].fT;
+    while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
+        ;
+    return fTs[index].fSmall;
+}
+
+// a pair of curves may turn into coincident lines -- small may be a hint that that happened
+// if a cubic contains a loop, the counts must be adjusted
+void SkOpSegment::checkSmall() {
+    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+    const SkOpSpan* beginSpan = fTs.begin();
+    const SkOpSpan* thisSpan = beginSpan - 1;
+    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be small
+    while (++thisSpan < endSpan) {
+        if (!thisSpan->fSmall) {
+            continue;
+        }
+        if (!thisSpan->fWindValue) {
+            continue;
+        }
+        const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
+        const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
+        const SkOpSpan* nextSpan = &firstSpan + 1;
+        ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
+        SkASSERT(1 <= smallCount && smallCount < count());
+        if (smallCount <= 1 && !nextSpan->fSmall) {
+            SkASSERT(1 == smallCount);
+            checkSmallCoincidence(firstSpan, NULL);
+            continue;
+        }
+        // at this point, check for missing computed intersections
+        const SkPoint& testPt = firstSpan.fPt;
+        thisSpan = &firstSpan - 1;
+        SkOpSegment* other = NULL;
+        while (++thisSpan <= &lastSpan) {
+            other = thisSpan->fOther;
+            if (other != this) {
+                break;
+            }
+        }
+        SkASSERT(other != this);
+        int oIndex = thisSpan->fOtherIndex;
+        const SkOpSpan& oSpan = other->span(oIndex);
+        const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
+        const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
+        ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
+        if (fLoop) {
+            int smallCounts[2];
+            SkASSERT(!other->fLoop);  // FIXME: we need more complicated logic for pair of loops
+            if (calcLoopSpanCount(*thisSpan, smallCounts)) {
+                if (smallCounts[0] && oCount != smallCounts[0]) {
+                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
+                }
+                if (smallCounts[1] && oCount != smallCounts[1]) {
+                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
+                }
+                goto nextSmallCheck;
+            }
+        }
+        if (other->fLoop) {
+            int otherCounts[2];
+            if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
+                if (otherCounts[0] && otherCounts[0] != smallCount) {
+                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
+                }
+                if (otherCounts[1] && otherCounts[1] != smallCount) {
+                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
+                }
+                goto nextSmallCheck;
+            }
+        }
+        if (oCount != smallCount) {  // check if number of pts in this match other
+            MissingSpan& missing = missingSpans.push_back();
+            missing.fOther = NULL;
+            SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+            missing.fPt = testPt;
+            const SkOpSpan& oSpan = other->span(oIndex);
+            if (oCount > smallCount) {
+                missing.fSegment = this;
+                missing.fT = thisSpan->fT;
+                other->checkLinks(&oSpan, &missingSpans);
+            } else {
+                missing.fSegment = other;
+                missing.fT = oSpan.fT;
+                checkLinks(thisSpan, &missingSpans);
+            }
+            if (!missingSpans.back().fOther || missing.fSegment->done()) {
+                missingSpans.pop_back();
+            }
+        }
+nextSmallCheck:
+        thisSpan = &lastSpan;
+    }
+    int missingCount = missingSpans.count();
+    for (int index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        SkOpSegment* missingOther = missing.fOther;
+        // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
+        if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
+                missing.fPt)) {
+            continue;
+        }
+        int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
+        const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
+        if (otherSpan.fSmall) {
+            const SkOpSpan* nextSpan = &otherSpan;
+            if (nextSpan->fPt == missing.fPt) {
+                continue;
+            }
+            do {
+                ++nextSpan;
+            } while (nextSpan->fSmall);
+            if (nextSpan->fT == 1) {
+                continue;
+            }
+            SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
+                    nextSpan->fT, missingOther));
+        } else if (otherSpan.fT > 0) {
+            const SkOpSpan* priorSpan = &otherSpan;
+            do {
+                --priorSpan;
+            } while (priorSpan->fT == otherSpan.fT);
+            if (priorSpan->fSmall) {
+                missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
+            }
+        }
+    }
+    // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
+    // avoid this
+    for (int index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        missing.fSegment->fixOtherTIndex();
+        missing.fOther->fixOtherTIndex();
+    }
+    debugValidate();
+}
+
+void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
+        SkTArray<MissingSpan, true>* checkMultiple) {
+    SkASSERT(span.fSmall);
+    if (0 && !span.fWindValue) {
+        return;
+    }
+    SkASSERT(&span < fTs.end() - 1);
+    const SkOpSpan* next = &span + 1;
+    SkASSERT(!next->fSmall || checkMultiple);
+    if (checkMultiple) {
+        while (next->fSmall) {
+            ++next;
+            SkASSERT(next < fTs.end());
+        }
+    }
+    SkOpSegment* other = span.fOther;
+    while (other != next->fOther) {
+        if (!checkMultiple) {
+            return;
+        }
+        const SkOpSpan* test = next + 1;
+        if (test == fTs.end()) {
+            return;
+        }
+        if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
+            return;
+        }
+        next = test;
+    }
+    SkASSERT(span.fT < next->fT);
+    int oStartIndex = other->findExactT(span.fOtherT, this);
+    int oEndIndex = other->findExactT(next->fOtherT, this);
+    // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
+    if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
+        SkPoint mid = ptAtT((span.fT + next->fT) / 2);
+        const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
+        const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
+        SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
+        if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
+            return;
+        }
+    }
+    // FIXME: again, be overly conservative to avoid breaking existing tests
+    const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
+            : other->fTs[oEndIndex];
+    if (checkMultiple && !oSpan.fSmall) {
+        return;
+    }
+//    SkASSERT(oSpan.fSmall);
+    if (oStartIndex < oEndIndex) {
+        SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
+    } else {
+        addTCancel(span.fPt, next->fPt, other);
     }
-    // intersect ray starting at basePt with edge
-    SkIntersections intersections;
-    // OPTIMIZE: use specialty function that intersects ray with curve,
-    // returning t values only for curve (we don't care about t on ray)
-    intersections.allowNear(false);
-    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
-            (fPts, top, bottom, basePt.fX, false);
-    if (pts == 0 || (current && pts == 1)) {
-        return NULL;
+    if (!checkMultiple) {
+        return;
     }
-    if (current) {
-        SkASSERT(pts > 1);
-        int closestIdx = 0;
-        double closest = fabs(intersections[0][0] - mid);
-        for (int idx = 1; idx < pts; ++idx) {
-            double test = fabs(intersections[0][idx] - mid);
-            if (closest > test) {
-                closestIdx = idx;
-                closest = test;
+    // check to see if either segment is coincident with a third segment -- if it is, and if
+    // the opposite segment is not already coincident with the third, make it so
+    // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
+    if (span.fWindValue != 1 || span.fOppValue != 0) {
+//        start here;
+        // iterate through the spans, looking for the third coincident case
+        // if we find one, we need to return state to the caller so that the indices can be fixed
+        // this also suggests that all of this function is fragile since it relies on a valid index
+    }
+    // probably should make this a common function rather than copy/paste code
+    if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
+        const SkOpSpan* oTest = &oSpan;
+        while (--oTest >= other->fTs.begin()) {
+            if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
+                break;
+            }
+            SkOpSegment* testOther = oTest->fOther;
+            SkASSERT(testOther != this);
+            // look in both directions to see if there is a coincident span
+            const SkOpSpan* tTest = testOther->fTs.begin();
+            for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
+                if (tTest->fPt != span.fPt) {
+                    ++tTest;
+                    continue;
+                }
+                if (testOther->verb() != SkPath::kLine_Verb
+                        || other->verb() != SkPath::kLine_Verb) {
+                    SkPoint mid = ptAtT((span.fT + next->fT) / 2);
+                    SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
+                    if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
+                        continue;
+                    }
+                }
+#if DEBUG_CONCIDENT
+                SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
+                        oTest->fOtherT, tTest->fT);
+#endif
+                if (tTest->fT < oTest->fOtherT) {
+                    SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
+                } else {
+                    addTCancel(span.fPt, next->fPt, testOther);
+                }
+                MissingSpan missing;
+                missing.fSegment = testOther;
+                checkMultiple->push_back(missing);
+                break;
             }
         }
-        intersections.quickRemoveOne(closestIdx, --pts);
+        oTest = &oSpan;
+        while (++oTest < other->fTs.end()) {
+            if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
+                break;
+            }
+
+        }
     }
-    double bestT = -1;
-    for (int index = 0; index < pts; ++index) {
-        double foundT = intersections[0][index];
-        if (approximately_less_than_zero(foundT)
-                    || approximately_greater_than_one(foundT)) {
+}
+
+// if pair of spans on either side of tiny have the same end point and mid point, mark
+// them as parallel
+void SkOpSegment::checkTiny() {
+    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+    SkOpSpan* thisSpan = fTs.begin() - 1;
+    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be tiny
+    while (++thisSpan < endSpan) {
+        if (!thisSpan->fTiny) {
             continue;
         }
-        SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
-        if (approximately_negative(testY - *bestY)
-                || approximately_negative(basePt.fY - testY)) {
+        SkOpSpan* nextSpan = thisSpan + 1;
+        double thisT = thisSpan->fT;
+        double nextT = nextSpan->fT;
+        if (thisT == nextT) {
             continue;
         }
-        if (pts > 1 && fVerb == SkPath::kLine_Verb) {
-            *vertical = true;
-            return NULL;  // if the intersection is edge on, wait for another one
-        }
-        if (fVerb > SkPath::kLine_Verb) {
-            SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
-            if (approximately_zero(dx)) {
-                *vertical = true;
-                return NULL;  // hit vertical, wait for another one
+        SkASSERT(thisT < nextT);
+        SkASSERT(thisSpan->fPt == nextSpan->fPt);
+        SkOpSegment* thisOther = thisSpan->fOther;
+        SkOpSegment* nextOther = nextSpan->fOther;
+        int oIndex = thisSpan->fOtherIndex;
+        for (int oStep = -1; oStep <= 1; oStep += 2) {
+            int oEnd = thisOther->nextExactSpan(oIndex, oStep);
+            if (oEnd < 0) {
+                continue;
+            }
+            const SkOpSpan& oSpan = thisOther->span(oEnd);
+            int nIndex = nextSpan->fOtherIndex;
+            for (int nStep = -1; nStep <= 1; nStep += 2) {
+                int nEnd = nextOther->nextExactSpan(nIndex, nStep);
+                if (nEnd < 0) {
+                    continue;
+                }
+                const SkOpSpan& nSpan = nextOther->span(nEnd);
+                if (oSpan.fPt != nSpan.fPt) {
+                    continue;
+                }
+                double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
+                const SkPoint& oPt = thisOther->ptAtT(oMidT);
+                double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
+                const SkPoint& nPt = nextOther->ptAtT(nMidT);
+                if (!AlmostEqualUlps(oPt, nPt)) {
+                    continue;
+                }
+#if DEBUG_CHECK_TINY
+                SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
+                    thisOther->fID, nextOther->fID);
+#endif
+                // this segment is missing a entry that the other contains
+                // remember so we can add the missing one and recompute the indices
+                MissingSpan& missing = missingSpans.push_back();
+                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+                missing.fSegment = thisOther;
+                missing.fT = thisSpan->fOtherT;
+                SkASSERT(this != nextOther);
+                missing.fOther = nextOther;
+                missing.fOtherT = nextSpan->fOtherT;
+                missing.fPt = thisSpan->fPt;
             }
         }
-        *bestY = testY;
-        bestT = foundT;
     }
-    if (bestT < 0) {
-        return NULL;
+    int missingCount = missingSpans.count();
+    if (!missingCount) {
+        return;
     }
-    SkASSERT(bestT >= 0);
-    SkASSERT(bestT < 1);
-    SkOpSpanBase* testTSpanBase = &this->fHead;
-    SkOpSpanBase* nextTSpan;
-    double endT = 0;
-    do {
-        nextTSpan = testTSpanBase->upCast()->next();
-        endT = nextTSpan->t();
-        if (endT >= bestT) {
-            break;
+    for (int index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        if (missing.fSegment != missing.fOther) {
+            missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
+                    missing.fPt);
         }
-        testTSpanBase = nextTSpan;
-    } while (testTSpanBase);
-    SkOpSpan* bestTSpan = NULL;
-    SkOpSpan* testTSpan = testTSpanBase->upCast();
-    if (!testTSpan->isCanceled()) {
-        *hitT = bestT;
-        bestTSpan = testTSpan;
-        *hitSomething = true;
     }
-    return bestTSpan;
-}
-
-void SkOpSegment::detach(const SkOpSpan* span) {
-    if (span->done()) {
-        --this->fDoneCount;
+    // OPTIMIZE: consolidate to avoid multiple calls to fix index
+    for (int index = 0; index < missingCount; ++index)  {
+        MissingSpan& missing = missingSpans[index];
+        missing.fSegment->fixOtherTIndex();
+        missing.fOther->fixOtherTIndex();
     }
-    --this->fCount;
 }
 
-double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
-    SkDPoint testPt = this->dPtAtT(t);
-    SkDLine testPerp = {{ testPt, testPt }};
-    SkDVector slope = this->dSlopeAtT(t);
-    testPerp[1].fX += slope.fY;
-    testPerp[1].fY -= slope.fX;
-    SkIntersections i;
-    SkOpSegment* oppSegment = oppAngle->segment();
-    int oppPtCount = SkPathOpsVerbToPoints(oppSegment->verb());
-    (*CurveIntersectRay[oppPtCount])(oppSegment->pts(), testPerp, &i);
-    double closestDistSq = SK_ScalarInfinity;
-    for (int index = 0; index < i.used(); ++index) {
-        if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
+bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = this->span(index);
+        if (span.fOther != other) {
+            continue;
+        }
+        if (span.fPt == pt) {
+            continue;
+        }
+        if (!AlmostEqualUlps(span.fPt, pt)) {
             continue;
         }
-        double testDistSq = testPt.distanceSquared(i.pt(index));
-        if (closestDistSq > testDistSq) {
-            closestDistSq = testDistSq;
+        if (fVerb != SkPath::kCubic_Verb) {
+            return true;
         }
+        double tInterval = t - span.fT;
+        double tMid = t - tInterval / 2;
+        SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+        return midPt.approximatelyEqual(xyAtT(t));
     }
-    return closestDistSq;
+    return false;
+}
+
+bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
+        int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
+    SkASSERT(span->fT == 0 || span->fT == 1);
+    SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
+    const SkOpSpan* otherSpan = &other->span(oEnd);
+    double refT = otherSpan->fT;
+    const SkPoint& refPt = otherSpan->fPt;
+    const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
+    do {
+        const SkOpSegment* match = span->fOther;
+        if (match == otherSpan->fOther) {
+            // find start of respective spans and see if both have winding
+            int startIndex, endIndex;
+            if (span->fOtherT == 1) {
+                endIndex = span->fOtherIndex;
+                startIndex = match->nextExactSpan(endIndex, -1);
+            } else {
+                startIndex = span->fOtherIndex;
+                endIndex = match->nextExactSpan(startIndex, 1);
+            }
+            const SkOpSpan& startSpan = match->span(startIndex);
+            if (startSpan.fWindValue != 0) {
+                // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
+                // to other segment.
+                const SkOpSpan& endSpan = match->span(endIndex);
+                SkDLine ray;
+                SkVector dxdy;
+                if (span->fOtherT == 1) {
+                    ray.fPts[0].set(startSpan.fPt);
+                    dxdy = match->dxdy(startIndex);
+                } else {
+                    ray.fPts[0].set(endSpan.fPt);
+                    dxdy = match->dxdy(endIndex);
+                }
+                ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
+                ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
+                SkIntersections i;
+                int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
+                for (int index = 0; index < roots; ++index) {
+                    if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
+                        double matchMidT = (match->span(startIndex).fT
+                                + match->span(endIndex).fT) / 2;
+                        SkPoint matchMidPt = match->ptAtT(matchMidT);
+                        double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
+                        SkPoint otherMidPt = other->ptAtT(otherMidT);
+                        if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
+                            *startPt = startSpan.fPt;
+                            *endPt = endSpan.fPt;
+                            *endT = endSpan.fT;
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        if (otherSpan == lastSpan) {
+            break;
+        }
+        otherSpan += step;
+    } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
+    return false;
+}
+
+int SkOpSegment::findEndSpan(int endIndex) const {
+    const SkOpSpan* span = &fTs[--endIndex];
+    const SkPoint& lastPt = span->fPt;
+    double endT = span->fT;
+    do {
+        span = &fTs[--endIndex];
+    } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
+    return endIndex + 1;
 }
 
 /*
@@ -789,57 +3029,71 @@ double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
  The Opp variable name part designates that the value is for the Opposite operator.
  Opposite values result from combining coincident spans.
  */
-SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
-        SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
-    SkOpSpanBase* start = *nextStart;
-    SkOpSpanBase* end = *nextEnd;
-    SkASSERT(start != end);
-    int step = start->step(end);
-    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
-    if (other) {
+SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+                                     bool* unsortable, SkPathOp op, const int xorMiMask,
+                                     const int xorSuMask) {
+    const int startIndex = *nextStart;
+    const int endIndex = *nextEnd;
+    SkASSERT(startIndex != endIndex);
+    SkDEBUGCODE(const int count = fTs.count());
+    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+    int step = SkSign32(endIndex - startIndex);
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other) 
+    {
     // mark the smaller of startIndex, endIndex done, and all adjacent
     // spans with the same T value (but not 'other' spans)
 #if DEBUG_WINDING
         SkDebugf("%s simple\n", __FUNCTION__);
 #endif
-        SkOpSpan* startSpan = start->starter(end);
-        if (startSpan->done()) {
+        int min = SkMin32(startIndex, endIndex);
+        if (fTs[min].fDone) {
+            return NULL;
+        }
+        markDoneBinary(min);
+        double startT = other->fTs[*nextStart].fT;
+        *nextEnd = *nextStart;
+        do {
+            *nextEnd += step;
+        } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+        SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+        if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+            *unsortable = true;
             return NULL;
         }
-        markDone(startSpan);
-        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
-    SkASSERT(endNear == end);  // is this ever not end?
-    SkASSERT(endNear);
-    SkASSERT(start != endNear);
-    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+    const int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
+    SkASSERT(startIndex - endIndex != 0);
+    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
-    int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
+
+    int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDone(start->starter(end));
+        markDoneBinary(SkMin32(startIndex, endIndex));
         return NULL;
     }
-    SkOpAngle* angle = this->spanToAngle(end, start);
+    SkOpAngle* angle = spanToAngle(end, startIndex);
     if (angle->unorderable()) {
         *unsortable = true;
-        markDone(start->starter(end));
+        markDoneBinary(SkMin32(startIndex, endIndex));
         return NULL;
     }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumMiWinding = updateWinding(end, start);
+    int sumMiWinding = updateWinding(endIndex, startIndex);
     if (sumMiWinding == SK_MinS32) {
         *unsortable = true;
-        markDone(start->starter(end));
+        markDoneBinary(SkMin32(startIndex, endIndex));
         return NULL;
     }
-    int sumSuWinding = updateOppWinding(end, start);
+    int sumSuWinding = updateOppWinding(endIndex, startIndex);
     if (operand()) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
@@ -856,6 +3110,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
         if (activeAngle) {
             ++activeCount;
             if (!foundAngle || (foundDone && activeCount & 1)) {
+                if (nextSegment->isTiny(nextAngle)) {
+                    *unsortable = true;
+                    markDoneBinary(SkMin32(startIndex, endIndex));
+                    return NULL;
+                }
                 foundAngle = nextAngle;
                 foundDone = nextSegment->done(nextAngle);
             }
@@ -863,24 +3122,30 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
         if (nextSegment->done()) {
             continue;
         }
+        if (nextSegment->isTiny(nextAngle)) {
+            continue;
+        }
         if (!activeAngle) {
-            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpanBase* last = nextAngle->lastMarked();
+        SkOpSpan* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
-                    last->segment()->debugID(), last->debugID());
-            if (!last->final()) {
-                SkDebugf(" windSum=%d", last->upCast()->windSum());
-            }
-            SkDebugf("\n");
+            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+                    last->fSmall);
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-    start->segment()->markDone(start->starter(end));
+#if DEBUG_ANGLE
+    if (foundAngle) {
+        foundAngle->debugSameAs(foundAngle);
+    }
+#endif
+
+    markDoneBinary(SkMin32(startIndex, endIndex));
     if (!foundAngle) {
         return NULL;
     }
@@ -894,55 +3159,62 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
     return nextSegment;
 }
 
-SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
-        SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
-    SkOpSpanBase* start = *nextStart;
-    SkOpSpanBase* end = *nextEnd;
-    SkASSERT(start != end);
-    int step = start->step(end);
-    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
-    if (other) {
+SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
+                                          int* nextEnd, bool* unsortable) {
+    const int startIndex = *nextStart;
+    const int endIndex = *nextEnd;
+    SkASSERT(startIndex != endIndex);
+    SkDEBUGCODE(const int count = fTs.count());
+    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+    int step = SkSign32(endIndex - startIndex);
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other) 
+    {
     // mark the smaller of startIndex, endIndex done, and all adjacent
     // spans with the same T value (but not 'other' spans)
 #if DEBUG_WINDING
         SkDebugf("%s simple\n", __FUNCTION__);
 #endif
-        SkOpSpan* startSpan = start->starter(end);
-        if (startSpan->done()) {
+        int min = SkMin32(startIndex, endIndex);
+        if (fTs[min].fDone) {
+            return NULL;
+        }
+        markDoneUnary(min);
+        double startT = other->fTs[*nextStart].fT;
+        *nextEnd = *nextStart;
+        do {
+            *nextEnd += step;
+        } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+        SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+        if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+            *unsortable = true;
             return NULL;
         }
-        markDone(startSpan);
-        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
-    SkASSERT(endNear == end);  // is this ever not end?
-    SkASSERT(endNear);
-    SkASSERT(start != endNear);
-    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+    const int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
+    SkASSERT(startIndex - endIndex != 0);
+    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
-    int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
+
+    int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDone(start->starter(end));
-        return NULL;
-    }
-    SkOpAngle* angle = this->spanToAngle(end, start);
-    if (angle->unorderable()) {
-        *unsortable = true;
-        markDone(start->starter(end));
+        markDoneUnary(SkMin32(startIndex, endIndex));
         return NULL;
     }
+    SkOpAngle* angle = spanToAngle(end, startIndex);
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumWinding = updateWinding(end, start);
+    int sumWinding = updateWinding(endIndex, startIndex);
     SkOpAngle* nextAngle = angle->next();
     const SkOpAngle* foundAngle = NULL;
     bool foundDone = false;
-    // iterate through the angle, and compute everyone's winding
     SkOpSegment* nextSegment;
     int activeCount = 0;
     do {
@@ -952,6 +3224,11 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
         if (activeAngle) {
             ++activeCount;
             if (!foundAngle || (foundDone && activeCount & 1)) {
+                if (nextSegment->isTiny(nextAngle)) {
+                    *unsortable = true;
+                    markDoneUnary(SkMin32(startIndex, endIndex));
+                    return NULL;
+                }
                 foundAngle = nextAngle;
                 foundDone = nextSegment->done(nextAngle);
             }
@@ -959,24 +3236,24 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
         if (nextSegment->done()) {
             continue;
         }
+        if (nextSegment->isTiny(nextAngle)) {
+            continue;
+        }
         if (!activeAngle) {
-            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
+            nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpanBase* last = nextAngle->lastMarked();
+        SkOpSpan* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
-                    last->segment()->debugID(), last->debugID());
-            if (!last->final()) {
-                SkDebugf(" windSum=%d", last->upCast()->windSum());
-            }
-            SkDebugf("\n");
+            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+                    last->fSmall);
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-    start->segment()->markDone(start->starter(end));
+    markDoneUnary(SkMin32(startIndex, endIndex));
     if (!foundAngle) {
         return NULL;
     }
@@ -990,39 +3267,57 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
     return nextSegment;
 }
 
-SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
-        bool* unsortable) {
-    SkOpSpanBase* start = *nextStart;
-    SkOpSpanBase* end = *nextEnd;
-    SkASSERT(start != end);
-    int step = start->step(end);
-    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
-    if (other) {
-    // mark the smaller of startIndex, endIndex done, and all adjacent
-    // spans with the same T value (but not 'other' spans)
+SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
+    const int startIndex = *nextStart;
+    const int endIndex = *nextEnd;
+    SkASSERT(startIndex != endIndex);
+    SkDEBUGCODE(int count = fTs.count());
+    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+    int step = SkSign32(endIndex - startIndex);
+// Detect cases where all the ends canceled out (e.g.,
+// there is no angle) and therefore there's only one valid connection 
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other)
+    {
 #if DEBUG_WINDING
         SkDebugf("%s simple\n", __FUNCTION__);
 #endif
-        SkOpSpan* startSpan = start->starter(end);
-        if (startSpan->done()) {
+        int min = SkMin32(startIndex, endIndex);
+        if (fTs[min].fDone) {
             return NULL;
         }
-        markDone(startSpan);
-        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+        markDone(min, 1);
+        double startT = other->fTs[*nextStart].fT;
+        // FIXME: I don't know why the logic here is difference from the winding case
+        SkDEBUGCODE(bool firstLoop = true;)
+        if ((approximately_less_than_zero(startT) && step < 0)
+                || (approximately_greater_than_one(startT) && step > 0)) {
+            step = -step;
+            SkDEBUGCODE(firstLoop = false;)
+        }
+        do {
+            *nextEnd = *nextStart;
+            do {
+                *nextEnd += step;
+            } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+            if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
+                break;
+            }
+            SkASSERT(firstLoop);
+            SkDEBUGCODE(firstLoop = false;)
+            step = -step;
+        } while (true);
+        SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
         return other;
     }
-    SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
-            : (*nextStart)->prev());
-    SkASSERT(endNear == end);  // is this ever not end?
-    SkASSERT(endNear);
-    SkASSERT(start != endNear);
-    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
-    SkOpAngle* angle = this->spanToAngle(end, start);
-    if (angle->unorderable()) {
-        *unsortable = true;
-        markDone(start->starter(end));
-        return NULL;
-    }
+    SkASSERT(startIndex - endIndex != 0);
+    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+    // parallel block above with presorted version
+    int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
+    SkOpAngle* angle = spanToAngle(end, startIndex);
+    SkASSERT(angle);
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
@@ -1030,13 +3325,16 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
     SkOpAngle* nextAngle = angle->next();
     const SkOpAngle* foundAngle = NULL;
     bool foundDone = false;
-    // iterate through the angle, and compute everyone's winding
     SkOpSegment* nextSegment;
     int activeCount = 0;
     do {
         nextSegment = nextAngle->segment();
         ++activeCount;
         if (!foundAngle || (foundDone && activeCount & 1)) {
+            if (nextSegment->isTiny(nextAngle)) {
+                *unsortable = true;
+                return NULL;
+            }
             foundAngle = nextAngle;
             if (!(foundDone = nextSegment->done(nextAngle))) {
                 break;
@@ -1044,7 +3342,7 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
         }
         nextAngle = nextAngle->next();
     } while (nextAngle != angle);
-    start->segment()->markDone(start->starter(end));
+    markDone(SkMin32(startIndex, endIndex), 1);
     if (!foundAngle) {
         return NULL;
     }
@@ -1058,39 +3356,105 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
     return nextSegment;
 }
 
-SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
-        bool* unsortable, SkChunkAlloc* allocator) {
+int SkOpSegment::findStartSpan(int startIndex) const {
+    int index = startIndex;
+    const SkOpSpan* span = &fTs[index];
+    const SkPoint& firstPt = span->fPt;
+    double firstT = span->fT;
+    const SkOpSpan* prior;
+    do {
+        prior = span;
+        span = &fTs[++index];
+    } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
+            && (span->fT == firstT || prior->fTiny));
+    return index;
+}
+
+int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fT == t && span.fOther == match) {
+            return index;
+        }
+    }
+    SkASSERT(0);
+    return -1;
+}
+
+
+
+int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fOtherT == t && span.fOther == match) {
+            return index;
+        }
+    }
+    return -1;
+}
+
+int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
+    int count = this->count();
+    // prefer exact matches over approximate matches
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fT == t && span.fOther == match) {
+            return index;
+        }
+    }
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
+            return index;
+        }
+    }
+    // Usually, the pair of ts are an exact match. It's possible that the t values have
+    // been adjusted to make multiple intersections align. In this rare case, look for a
+    // matching point / match pair instead.
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fPt == pt && span.fOther == match) {
+            return index;
+        }
+    }
+    SkASSERT(0);
+    return -1;
+}
+
+SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
+        bool firstPass) {
     // iterate through T intersections and return topmost
     // topmost tangent from y-min to first pt is closer to horizontal
     SkASSERT(!done());
-    SkOpSpanBase* firstT = NULL;
-    (void) this->activeLeftTop(&firstT);
-    if (!firstT) {
+    int firstT = -1;
+    /* SkPoint topPt = */ activeLeftTop(&firstT);
+    if (firstT < 0) {
         *unsortable = !firstPass;
-        firstT = &fHead;
-        while (firstT->upCast()->done()) {
-            firstT = firstT->upCast()->next();
+        firstT = 0;
+        while (fTs[firstT].fDone) {
+            SkASSERT(firstT < fTs.count());
+            ++firstT;
         }
-        *startPtr = firstT;
-        *endPtr = firstT->upCast()->next();
+        *tIndexPtr = firstT;
+        *endIndexPtr = nextExactSpan(firstT, 1);
         return this;
     }
     // sort the edges to find the leftmost
     int step = 1;
-    SkOpSpanBase* end;
-    if (firstT->final() || firstT->upCast()->done()) {
+    int end;
+    if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
         step = -1;
-        end = firstT->prev();
-        SkASSERT(end);
-    } else {
-        end = firstT->upCast()->next();
+        end = nextSpan(firstT, step);
+        SkASSERT(end != -1);
     }
     // if the topmost T is not on end, or is three-way or more, find left
     // look for left-ness from tLeft to firstT (matching y of other)
-    SkASSERT(firstT != end);
+    SkASSERT(firstT - end != 0);
     SkOpAngle* markAngle = spanToAngle(firstT, end);
     if (!markAngle) {
-        markAngle = addSingletonAngles(step, allocator);
+        markAngle = addSingletonAngles(step);
     }
     markAngle->markStops();
     const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -1103,7 +3467,7 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
     const SkOpAngle* angle = baseAngle;
     do {
         if (!angle->unorderable()) {
-            const SkOpSegment* next = angle->segment();
+            SkOpSegment* next = angle->segment();
             SkPathOpsBounds bounds;
             next->subDivideBounds(angle->end(), angle->start(), &bounds);
             bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -1131,10 +3495,9 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
         *unsortable = angle->unorderable();
         if (firstPass || !*unsortable) {
             leftSegment = angle->segment();
-            *startPtr = angle->end();
-            *endPtr = angle->start();
-            const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
-            if (!firstSpan->done()) {
+            *tIndexPtr = angle->end();
+            *endIndexPtr = angle->start();
+            if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
                 break;
             }
         }
@@ -1145,52 +3508,157 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
         return NULL;
     }
     if (leftSegment->verb() >= SkPath::kQuad_Verb) {
-        SkOpSpanBase* start = *startPtr;
-        SkOpSpanBase* end = *endPtr;
+        const int tIndex = *tIndexPtr;
+        const int endIndex = *endIndexPtr;
         bool swap;
-        if (!leftSegment->clockwise(start, end, &swap)) {
+        if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
     #if DEBUG_SWAP_TOP
-            SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
+            SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
                     __FUNCTION__,
-                    swap, leftSegment->debugInflections(start, end),
-                    leftSegment->monotonicInY(start, end));
+                    swap, leftSegment->debugInflections(tIndex, endIndex),
+                    leftSegment->serpentine(tIndex, endIndex),
+                    leftSegment->controlsContainedByEnds(tIndex, endIndex),
+                    leftSegment->monotonicInY(tIndex, endIndex));
     #endif
             if (swap) {
     // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
     // sorted but merely the first not already processed (i.e., not done)
-                SkTSwap(*startPtr, *endPtr);
+                SkTSwap(*tIndexPtr, *endIndexPtr);
             }
         }
     }
+    SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
     return leftSegment;
 }
 
-SkOpGlobalState* SkOpSegment::globalState() const {
-    return contour()->globalState(); 
+int SkOpSegment::firstActive(int tIndex) const {
+    while (fTs[tIndex].fTiny) {
+        SkASSERT(!isCanceled(tIndex));
+        ++tIndex;
+    }
+    return tIndex;
+}
+
+// FIXME: not crazy about this
+// when the intersections are performed, the other index is into an
+// incomplete array. As the array grows, the indices become incorrect
+// while the following fixes the indices up again, it isn't smart about
+// skipping segments whose indices are already correct
+// assuming we leave the code that wrote the index in the first place
+// FIXME: if called after remove, this needs to correct tiny
+void SkOpSegment::fixOtherTIndex() {
+    int iCount = fTs.count();
+    for (int i = 0; i < iCount; ++i) {
+        SkOpSpan& iSpan = fTs[i];
+        double oT = iSpan.fOtherT;
+        SkOpSegment* other = iSpan.fOther;
+        int oCount = other->fTs.count();
+        SkDEBUGCODE(iSpan.fOtherIndex = -1);
+        for (int o = 0; o < oCount; ++o) {
+            SkOpSpan& oSpan = other->fTs[o];
+            if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
+                iSpan.fOtherIndex = o;
+                oSpan.fOtherIndex = i;
+                break;
+            }
+        }
+        SkASSERT(iSpan.fOtherIndex >= 0);
+    }
+}
+
+bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
+    int foundEnds = 0;
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = this->span(index);
+        if (span.fCoincident) {
+            foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
+        }
+    }
+    SkASSERT(foundEnds != 7);
+    return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6;  // two bits set
+}
+
+bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+        int oppSumWinding, const SkOpAngle* angle) const {
+    SkASSERT(angle->segment() == this);
+    if (UseInnerWinding(maxWinding, sumWinding)) {
+        maxWinding = sumWinding;
+    }
+    if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
+        oppMaxWinding = oppSumWinding;
+    }
+    return inconsistentWinding(angle, maxWinding, oppMaxWinding);
+}
+
+bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
+        int oppWinding) const {
+    int index = angle->start();
+    int endIndex = angle->end();
+    int min = SkMin32(index, endIndex);
+    int step = SkSign32(endIndex - index);
+    if (inconsistentWinding(min, winding, oppWinding)) {
+        return true;
+    }
+    const SkOpSegment* other = this;
+    while ((other = other->nextChase(&index, &step, &min, NULL))) {
+        if (other->fTs[min].fWindSum != SK_MinS32) {
+            break;
+        }
+        if (fOperand == other->fOperand) {
+            if (other->inconsistentWinding(min, winding, oppWinding)) {
+                return true;
+            }
+        } else {
+            if (other->inconsistentWinding(min, oppWinding, winding)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
+    SkASSERT(winding || oppWinding);
+    double referenceT = this->span(index).fT;
+    int lesser = index;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
+            return true;
+        }
+    }
+    do {
+        if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
+            return true;
+        }
+   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    return false;
+}
+
+bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
+        int oppWinding) const {
+    const SkOpSpan& span = this->span(tIndex);
+    if (span.fDone && !span.fSmall) {
+        return false;
+    }
+    return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
+            || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
 }
 
-void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
-    fContour = contour;
-    fNext = NULL;
+void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
+    fDoneSpans = 0;
+    fOperand = operand;
+    fXor = evenOdd;
     fPts = pts;
     fVerb = verb;
-    fCount = 0;
-    fDoneCount = 0;
-    fVisited = false;
-    SkOpSpan* zeroSpan = &fHead;
-    zeroSpan->init(this, NULL, 0, fPts[0]);
-    SkOpSpanBase* oneSpan = &fTail;
-    zeroSpan->setNext(oneSpan);
-    oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
-    PATH_OPS_DEBUG_CODE(fID = globalState()->nextSegmentID());
-}
-
-void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
-        SkOpAngle::IncludeType angleIncludeType) {
-    int local = SkOpSegment::SpanSign(start, end);
+    fLoop = fMultiples = fSmall = fTiny = false;
+}
+
+void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
+    int local = spanSign(start, end);
     SkDEBUGCODE(bool success);
     if (angleIncludeType == SkOpAngle::kBinarySingle) {
-        int oppLocal = SkOpSegment::OppSign(start, end);
+        int oppLocal = oppSign(start, end);
         SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
     // OPTIMIZATION: the reverse mark and chase could skip the first marking
         SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
@@ -1211,13 +3679,12 @@ If there was a winding, then it may or may not need adjusting. If the span the w
 from has the same x direction as this span, the winding should change. If the dx is opposite, then
 the same winding is shared by both.
 */
-bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
-        int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
-    SkASSERT(this == start->segment());
+bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
+                              int oppWind, SkScalar hitOppDx) {
     SkASSERT(hitDx || !winding);
     SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
-//    SkASSERT(dx);
-    int windVal = start->starter(end)->windValue();
+    SkASSERT(dx);
+    int windVal = windValue(SkMin32(start, end));
 #if DEBUG_WINDING_AT_T
     SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
             hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
@@ -1226,9 +3693,9 @@ bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHi
     if (abs(winding) < abs(sideWind)) {
         winding = sideWind;
     }
-    SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
+    SkDEBUGCODE(int oppLocal = oppSign(start, end));
     SkASSERT(hitOppDx || !oppWind || !oppLocal);
-    int oppWindVal = start->starter(end)->oppValue();
+    int oppWindVal = oppValue(SkMin32(start, end));
     if (!oppWind) {
         oppWind = dx < 0 ? oppWindVal : -oppWindVal;
     } else if (hitOppDx * dx >= 0) {
@@ -1247,57 +3714,149 @@ bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHi
     return success;
 }
 
-bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
-    SkDPoint cPt = this->dPtAtT(t);
-    int pts = SkPathOpsVerbToPoints(this->verb());
-    SkDVector dxdy = (*CurveDSlopeAtT[pts])(this->pts(), t);
-    SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
-    SkIntersections i;
-    int oppPts = SkPathOpsVerbToPoints(opp->verb());
-    (*CurveIntersectRay[oppPts])(opp->pts(), perp, &i);
-    int used = i.used();
-    for (int index = 0; index < used; ++index) {
-        if (cPt.roughlyEqual(i.pt(index))) {
-            return true;
+bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
+    if (!baseAngle->inLoop()) {
+        return false;
+    }
+    int index = *indexPtr;
+    SkOpAngle* from = fTs[index].fFromAngle;
+    SkOpAngle* to = fTs[index].fToAngle;
+    while (++index < spanCount) {
+        SkOpAngle* nextFrom = fTs[index].fFromAngle;
+        SkOpAngle* nextTo = fTs[index].fToAngle;
+        if (from != nextFrom || to != nextTo) {
+            break;
+        }
+    }
+    *indexPtr = index;
+    return true;
+}
+
+// OPTIMIZE: successive calls could start were the last leaves off
+// or calls could specialize to walk forwards or backwards
+bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
+    int tCount = fTs.count();
+    for (int index = 0; index < tCount; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (approximately_zero(startT - span.fT) && pt == span.fPt) {
+            return false;
         }
     }
+    return true;
+}
+
+
+SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
+    return nextChase(end, step, NULL, NULL);
+}
+
+bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
+    int start = angle->start();
+    int end = angle->end();
+    const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
+    return mSpan.fTiny;
+}
+
+bool SkOpSegment::isTiny(int index) const {
+    return fTs[index].fTiny;
+}
+
+// look pair of active edges going away from coincident edge
+// one of them should be the continuation of other
+// if both are active, look to see if they both the connect to another coincident pair
+// if at least one is a line, then make the pair coincident
+// if neither is a line, test for coincidence
+bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
+        int step, bool cancel) {
+    int otherTIndex = other->findT(otherT, otherPt, this);
+    int next = other->nextExactSpan(otherTIndex, step);
+    int otherMin = SkMin32(otherTIndex, next);
+    int otherWind = other->span(otherMin).fWindValue;
+    if (otherWind == 0) {
+        return false;
+    }
+    if (next < 0) {
+        return false;  // can happen if t values were adjusted but coincident ts were not
+    }
+    int tIndex = 0;
+    do {
+        SkOpSpan* test = &fTs[tIndex];
+        SkASSERT(test->fT == 0);
+        if (test->fOther == other || test->fOtherT != 1) {
+            continue;
+        }
+        SkPoint startPt, endPt;
+        double endT;
+        if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
+            SkOpSegment* match = test->fOther;
+            if (cancel) {
+                match->addTCancel(startPt, endPt, other);
+            } else {
+                if (!match->addTCoincident(startPt, endPt, endT, other)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } while (fTs[++tIndex].fT == 0);
     return false;
 }
 
-bool SkOpSegment::isXor() const {
-    return fContour->isXor();
+// this span is excluded by the winding rule -- chase the ends
+// as long as they are unambiguous to mark connections as done
+// and give them the same winding value
+
+SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
+    int step = SkSign32(endIndex - index);
+    int min = SkMin32(index, endIndex);
+    markDoneBinary(min);
+    SkOpSpan* last = NULL;
+    SkOpSegment* other = this;
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
+        if (other->done()) {
+            SkASSERT(!last);
+            break;
+        }
+        other->markDoneBinary(min);
+    }
+    return last;
 }
 
-SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
-    int step = start->step(end);
-    SkOpSpan* minSpan = start->starter(end);
-    markDone(minSpan);
-    SkOpSpanBase* last = NULL;
+SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
+    int step = SkSign32(endIndex - index);
+    int min = SkMin32(index, endIndex);
+    markDoneUnary(min);
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->done()) {
             SkASSERT(!last);
             break;
         }
-        other->markDone(minSpan);
+        other->markDoneUnary(min);
     }
     return last;
 }
 
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
-        SkOpSpanBase** lastPtr) {
-    SkOpSpan* spanStart = start->starter(end);
-    int step = start->step(end);
-    bool success = markWinding(spanStart, winding);
-    SkOpSpanBase* last = NULL;
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
+    int index = angle->start();
+    int endIndex = angle->end();
+    return markAndChaseWinding(index, endIndex, winding, lastPtr);
+}
+
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
+    int min = SkMin32(index, endIndex);
+    int step = SkSign32(endIndex - index);
+    bool success = markWinding(min, winding);
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
-        if (spanStart->windSum() != SK_MinS32) {
-            SkASSERT(spanStart->windSum() == winding);
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
+        if (other->fTs[min].fWindSum != SK_MinS32) {
+            SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
             SkASSERT(!last);
             break;
         }
-        (void) other->markWinding(spanStart, winding);
+        (void) other->markWinding(min, winding);
     }
     if (lastPtr) {
         *lastPtr = last;
@@ -1305,32 +3864,37 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, in
     return success;
 }
 
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
-        int winding, int oppWinding, SkOpSpanBase** lastPtr) {
-    SkOpSpan* spanStart = start->starter(end);
-    int step = start->step(end);
-    bool success = markWinding(spanStart, winding, oppWinding);
-    SkOpSpanBase* last = NULL;
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+        SkOpSpan** lastPtr) {
+    int min = SkMin32(index, endIndex);
+    int step = SkSign32(endIndex - index);
+    bool success = markWinding(min, winding, oppWinding);
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
-        if (spanStart->windSum() != SK_MinS32) {
-            if (this->operand() == other->operand()) {
-                SkASSERT(spanStart->windSum() == winding);
-                if (spanStart->oppSum() != oppWinding) {
-                    this->globalState()->setWindingFailed();
-                    return false;
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
+        if (other->fTs[min].fWindSum != SK_MinS32) {
+#ifdef SK_DEBUG
+            if (!other->fTs[min].fLoop) {
+                if (fOperand == other->fOperand) {
+// FIXME: this is probably a bug -- rects4 asserts here
+//                    SkASSERT(other->fTs[min].fWindSum == winding);
+// FIXME: this is probably a bug -- rects3 asserts here
+//                    SkASSERT(other->fTs[min].fOppSum == oppWinding);
+                } else {
+// FIXME: this is probably a bug -- issue414409b asserts here
+//                    SkASSERT(other->fTs[min].fWindSum == oppWinding);
+// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
+//                    SkASSERT(other->fTs[min].fOppSum == winding);
                 }
-            } else {
-                SkASSERT(spanStart->windSum() == oppWinding);
-                SkASSERT(spanStart->oppSum() == winding);
             }
             SkASSERT(!last);
+#endif
             break;
         }
-        if (this->operand() == other->operand()) {
-            (void) other->markWinding(spanStart, winding, oppWinding);
+        if (fOperand == other->fOperand) {
+            (void) other->markWinding(min, winding, oppWinding);
         } else {
-            (void) other->markWinding(spanStart, oppWinding, winding);
+            (void) other->markWinding(min, oppWinding, winding);
         }
     }
     if (lastPtr) {
@@ -1339,29 +3903,33 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
     return success;
 }
 
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
+        SkOpSpan** lastPtr) {
+    int start = angle->start();
+    int end = angle->end();
+    return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
+}
+
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
     }
-    SkOpSpanBase* last;
-    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
+    SkOpSpan* last;
+    SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
-                last->segment()->debugID(), last->debugID());
-        if (!last->final()) {
-            SkDebugf(" windSum=");
-            SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
-        }
-        SkDebugf("\n");
+        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+        SkPathOpsDebug::WindingPrintf(last->fWindSum);
+        SkDebugf(" small=%d\n", last->fSmall);
     }
 #endif
     return last;
 }
 
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
-                                   int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+                                 int oppSumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
@@ -1369,161 +3937,440 @@ SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxW
     if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
         oppMaxWinding = oppSumWinding;
     }
-    SkOpSpanBase* last = NULL;
+    SkOpSpan* last;
     // caller doesn't require that this marks anything
-    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
+    (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
-                last->segment()->debugID(), last->debugID());
-        if (!last->final()) {
-            SkDebugf(" windSum=");
-            SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
-        }
-        SkDebugf(" \n");
+        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+        SkPathOpsDebug::WindingPrintf(last->fWindSum);
+        SkDebugf(" small=%d\n", last->fSmall);
     }
 #endif
     return last;
 }
 
-void SkOpSegment::markDone(SkOpSpan* span) {
-    SkASSERT(this == span->segment());
-    if (span->done()) {
-        return;
+// FIXME: this should also mark spans with equal (x,y)
+// This may be called when the segment is already marked done. While this
+// wastes time, it shouldn't do any more than spin through the T spans.
+// OPTIMIZATION: abort on first done found (assuming that this code is
+// always called to mark segments done).
+void SkOpSegment::markDone(int index, int winding) {
+  //  SkASSERT(!done());
+    SkASSERT(winding);
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        markOneDone(__FUNCTION__, lesser, winding);
     }
-#if DEBUG_MARK_DONE
-    debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
-#endif
-    span->setDone(true);
-    ++fDoneCount;
+    do {
+        markOneDone(__FUNCTION__, index, winding);
+    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
     debugValidate();
 }
 
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
-    SkASSERT(this == span->segment());
-    SkASSERT(winding);
-    if (span->done()) {
+void SkOpSegment::markDoneBinary(int index) {
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        markOneDoneBinary(__FUNCTION__, lesser);
+    }
+    do {
+        markOneDoneBinary(__FUNCTION__, index);
+    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    debugValidate();
+}
+
+void SkOpSegment::markDoneFinal(int index) {
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        markOneDoneFinal(__FUNCTION__, lesser);
+    }
+    do {
+        markOneDoneFinal(__FUNCTION__, index);
+    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    debugValidate();
+}
+
+void SkOpSegment::markDoneUnary(int index) {
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        markOneDoneUnary(__FUNCTION__, lesser);
+    }
+    do {
+        markOneDoneUnary(__FUNCTION__, index);
+    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    debugValidate();
+}
+
+void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
+    SkOpSpan* span;
+    (void) markOneWinding(funName, tIndex, winding, &span);  // allowed to do nothing
+    if (span->fDone) {
+        return;
+    }
+    span->fDone = true;
+    ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
+    SkOpSpan* span = &fTs[tIndex];
+    if (span->fDone) {
+        return;
+    }
+    span->fDone = true;
+    ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
+    SkOpSpan* span = verifyOneWinding(funName, tIndex);
+    if (!span) {
+        return;
+    }
+    SkASSERT(!span->fDone);
+    span->fDone = true;
+    ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
+    SkOpSpan* span = verifyOneWindingU(funName, tIndex);
+    if (!span) {
+        return;
+    }
+    if (span->fWindSum == SK_MinS32) {
+        SkDebugf("%s uncomputed\n", __FUNCTION__);
+    }
+    SkASSERT(!span->fDone);
+    span->fDone = true;
+    ++fDoneSpans;
+}
+
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
+    SkOpSpan* span = &fTs[tIndex];
+    if (lastPtr) {
+        *lastPtr = span;
+    }
+    if (span->fDone && !span->fSmall) {
         return false;
     }
 #if DEBUG_MARK_DONE
-    debugShowNewWinding(__FUNCTION__, span, winding);
+    debugShowNewWinding(funName, *span, winding);
 #endif
-    span->setWindSum(winding);
-    debugValidate();
+    SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
+#if DEBUG_LIMIT_WIND_SUM
+    SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+    span->fWindSum = winding;
     return true;
 }
 
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
-    SkASSERT(this == span->segment());
-    SkASSERT(winding || oppWinding);
-    if (span->done()) {
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
+        int oppWinding, SkOpSpan** lastPtr) {
+    SkOpSpan* span = &fTs[tIndex];
+    if (span->fDone && !span->fSmall) {
         return false;
     }
 #if DEBUG_MARK_DONE
-    debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
+    debugShowNewWinding(funName, *span, winding, oppWinding);
+#endif
+    SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
+#if DEBUG_LIMIT_WIND_SUM
+    SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+    span->fWindSum = winding;
+    SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
+#if DEBUG_LIMIT_WIND_SUM
+    SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
 #endif
-    span->setWindSum(winding);
-    span->setOppSum(oppWinding);
+    span->fOppSum = oppWinding;
     debugValidate();
+    if (lastPtr) {
+        *lastPtr = span;
+    }
     return true;
 }
 
-bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
-        const SkPoint& testPt) const {
-    const SkOpSegment* baseParent = base->segment();
-    if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
-        return true;
+// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
+bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
+    SkASSERT(fVerb != SkPath::kLine_Verb);
+    SkPoint edge[4];
+    subDivide(tStart, tEnd, edge);
+    int points = SkPathOpsVerbToPoints(fVerb);
+    double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
+    bool sumSet = false;
+    if (fVerb == SkPath::kCubic_Verb) {
+        SkDCubic cubic;
+        cubic.set(edge);
+        double inflectionTs[2];
+        int inflections = cubic.findInflections(inflectionTs);
+        // FIXME: this fixes cubicOp114 and breaks cubicOp58d
+        // the trouble is that cubics with inflections confuse whether the curve breaks towards
+        // or away, which in turn is used to determine if it is on the far right or left.
+        // Probably a totally different approach is in order. At one time I tried to project a
+        // horizontal ray to determine winding, but was confused by how to map the vertically
+        // oriented winding computation over. 
+        if (0 && inflections) {
+            double tLo = this->span(tStart).fT;
+            double tHi = this->span(tEnd).fT;
+            double tLoStart = tLo;
+            for (int index = 0; index < inflections; ++index) {
+                if (between(tLo, inflectionTs[index], tHi)) {
+                    tLo = inflectionTs[index];
+                }
+            }
+            if (tLo != tLoStart && tLo != tHi) {
+                SkDPoint sub[2];
+                sub[0] = cubic.ptAtT(tLo);
+                sub[1].set(edge[3]);
+                SkDPoint ctrl[2];
+                SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
+                edge[0] = sub[0].asSkPoint();
+                edge[1] = ctrl[0].asSkPoint();
+                edge[2] = ctrl[1].asSkPoint();
+                sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
+            }
+        }
+        SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
+        if (edge[1].fY < lesser && edge[2].fY < lesser) {
+            SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
+            SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
+            if (SkIntersections::Test(tangent1, tangent2)) {
+                SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+                sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
+                sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
+                sumSet = true;
+            }
+        }
     }
-    if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
-        return false;
+    if (!sumSet) {
+        for (int idx = 0; idx < points; ++idx){
+            sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+        }
     }
-    return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
-}
-
-static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
-    if (last) {
-        *last = endSpan;
+    if (fVerb == SkPath::kCubic_Verb) {
+        SkDCubic cubic;
+        cubic.set(edge);
+         *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
+    } else {
+        SkDQuad quad;
+        quad.set(edge);
+        *swap = sum > 0 && !quad.monotonicInY();
     }
-    return NULL;
+    return sum <= 0;
 }
 
-bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
     SkASSERT(fVerb != SkPath::kLine_Verb);
     if (fVerb == SkPath::kQuad_Verb) {
-        SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
+        SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
         return dst.monotonicInY();
     }
     SkASSERT(fVerb == SkPath::kCubic_Verb);
-    SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
     return dst.monotonicInY();
 }
 
-bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
-        SkOpSpanBase** end) {
-    while (span->final() || span->upCast()->done()) {
-        if (span->final()) {
+bool SkOpSegment::serpentine(int tStart, int tEnd) const {
+    if (fVerb != SkPath::kCubic_Verb) {
+        return false;
+    }
+    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+    return dst.serpentine();
+}
+
+SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
+    SkOpSpan& span = fTs[tIndex];
+    if (span.fDone) {
+        return NULL;
+    }
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+//    SkASSERT(span.fWindSum != SK_MinS32);
+//    SkASSERT(span.fOppSum != SK_MinS32);
+    return &span;
+}
+
+SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
+    SkOpSpan& span = fTs[tIndex];
+    if (span.fDone) {
+        return NULL;
+    }
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(funName, span, span.fWindSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+//    SkASSERT(span.fWindSum != SK_MinS32);
+    return &span;
+}
+
+bool SkOpSegment::markWinding(int index, int winding) {
+//    SkASSERT(!done());
+    SkASSERT(winding);
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    bool success = false;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
+    }
+    do {
+        success |= markOneWinding(__FUNCTION__, index, winding, NULL);
+   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    debugValidate();
+    return success;
+}
+
+bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
+//    SkASSERT(!done());
+    SkASSERT(winding || oppWinding);
+    double referenceT = fTs[index].fT;
+    int lesser = index;
+    bool success = false;
+    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+        success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
+    }
+    do {
+        success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
+   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    debugValidate();
+    return success;
+}
+
+void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
+    int nextDoorWind = SK_MaxS32;
+    int nextOppWind = SK_MaxS32;
+    // prefer exact matches
+    if (tIndex > 0) {
+        const SkOpSpan& below = fTs[tIndex - 1];
+        if (below.fT == t) {
+            nextDoorWind = below.fWindValue;
+            nextOppWind = below.fOppValue;
+        }
+    }
+    if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+        const SkOpSpan& above = fTs[tIndex + 1];
+        if (above.fT == t) {
+            nextDoorWind = above.fWindValue;
+            nextOppWind = above.fOppValue;
+        }
+    }
+    if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
+        const SkOpSpan& below = fTs[tIndex - 1];
+        if (approximately_negative(t - below.fT)) {
+            nextDoorWind = below.fWindValue;
+            nextOppWind = below.fOppValue;
+        }
+    }
+    if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+        const SkOpSpan& above = fTs[tIndex + 1];
+        if (approximately_negative(above.fT - t)) {
+            nextDoorWind = above.fWindValue;
+            nextOppWind = above.fOppValue;
+        }
+    }
+    if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
+        const SkOpSpan& below = fTs[tIndex - 1];
+        nextDoorWind = below.fWindValue;
+        nextOppWind = below.fOppValue;
+    }
+    if (nextDoorWind != SK_MaxS32) {
+        SkOpSpan& newSpan = fTs[tIndex];
+        newSpan.fWindValue = nextDoorWind;
+        newSpan.fOppValue = nextOppWind;
+        if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
+            newSpan.fDone = true;
+            ++fDoneSpans;
+        }
+    }
+}
+
+bool SkOpSegment::nextCandidate(int* start, int* end) const {
+    while (fTs[*end].fDone) {
+        if (fTs[*end].fT == 1) {
             return false;
         }
-        span = span->upCast()->next();
+        ++(*end);
     }
-    *start = span;
-    *end = span->upCast()->next();
+    *start = *end;
+    *end = nextExactSpan(*start, 1);
     return true;
 }
 
-SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
-        SkOpSpanBase** last) const {
-    SkOpSpanBase* origStart = *startPtr;
+static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
+    if (last && !endSpan->fSmall) {
+        *last = const_cast<SkOpSpan*>(endSpan);  // FIXME: get rid of cast
+    }
+    return NULL;
+}
+
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
+        SkOpSpan** last) const {
+    int origIndex = *indexPtr;
     int step = *stepPtr;
-    SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
-    SkASSERT(endSpan);
-    SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
-    SkOpSpanBase* foundSpan;
-    SkOpSpanBase* otherEnd;
+    int end = nextExactSpan(origIndex, step);
+    SkASSERT(end >= 0);
+    const SkOpSpan& endSpan = this->span(end);
+    SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
+    int foundIndex;
+    int otherEnd;
     SkOpSegment* other;
     if (angle == NULL) {
-        if (endSpan->t() != 0 && endSpan->t() != 1) {
+        if (endSpan.fT != 0 && endSpan.fT != 1) {
             return NULL;
         }
-        SkOpPtT* otherPtT = endSpan->ptT()->next();
-        other = otherPtT->segment();
-        foundSpan = otherPtT->span();
-        otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
+        other = endSpan.fOther;
+        foundIndex = endSpan.fOtherIndex;
+        otherEnd = other->nextExactSpan(foundIndex, step);
     } else {
         int loopCount = angle->loopCount();
         if (loopCount > 2) {
-            return set_last(last, endSpan);
+            return set_last(last, &endSpan);
         }
         const SkOpAngle* next = angle->next();
         if (NULL == next) {
             return NULL;
         }
+        if (angle->sign() != next->sign()) {
 #if DEBUG_WINDING
-        if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
-                && !next->segment()->contour()->isXor()) {
             SkDebugf("%s mismatched signs\n", __FUNCTION__);
-        }
 #endif
+        //    return set_last(last, &endSpan);
+        }
         other = next->segment();
-        foundSpan = endSpan = next->start();
+        foundIndex = end = next->start();
         otherEnd = next->end();
     }
-    int foundStep = foundSpan->step(otherEnd);
+    int foundStep = foundIndex < otherEnd ? 1 : -1;
     if (*stepPtr != foundStep) {
-        return set_last(last, endSpan);
+        return set_last(last, &endSpan);
     }
-    SkASSERT(*startPtr);
-    if (!otherEnd) {
+    SkASSERT(*indexPtr >= 0);
+    if (otherEnd < 0) {
         return NULL;
     }
 //    SkASSERT(otherEnd >= 0);
-    SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
-    SkOpSpan* foundMin = foundSpan->starter(otherEnd);
-    if (foundMin->windValue() != origMin->windValue()
-            || foundMin->oppValue() != origMin->oppValue()) {
-          return set_last(last, endSpan);
+#if 1
+    int origMin = origIndex + (step < 0 ? step : 0);
+    const SkOpSpan& orig = this->span(origMin);
+#endif
+    int foundMin = SkMin32(foundIndex, otherEnd);
+#if 1
+    const SkOpSpan& found = other->span(foundMin);
+    if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
+          return set_last(last, &endSpan);
     }
-    *startPtr = foundSpan;
+#endif
+    *indexPtr = foundIndex;
     *stepPtr = foundStep;
     if (minPtr) {
         *minPtr = foundMin;
@@ -1531,217 +4378,101 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS
     return other;
 }
 
-static void clear_visited(SkOpSpan* span) {
-    // reset visited flag back to false
-    do {
-        SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
-        while ((ptT = ptT->next()) != stopPtT) {
-            SkOpSegment* opp = ptT->segment();
-            opp->resetVisited();
+// This has callers for two different situations: one establishes the end
+// of the current span, and one establishes the beginning of the next span
+// (thus the name). When this is looking for the end of the current span,
+// coincidence is found when the beginning Ts contain -step and the end
+// contains step. When it is looking for the beginning of the next, the
+// first Ts found can be ignored and the last Ts should contain -step.
+// OPTIMIZATION: probably should split into two functions
+int SkOpSegment::nextSpan(int from, int step) const {
+    const SkOpSpan& fromSpan = fTs[from];
+    int count = fTs.count();
+    int to = from;
+    while (step > 0 ? ++to < count : --to >= 0) {
+        const SkOpSpan& span = fTs[to];
+        if (approximately_zero(span.fT - fromSpan.fT)) {
+            continue;
         }
-    } while ((span = span->next()->upCastable()));
+        return to;
+    }
+    return -1;
 }
 
-// look for pairs of undetected coincident curves
-// assumes that segments going in have visited flag clear
-// curve/curve intersection should now do a pretty good job of finding coincident runs so 
-// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
-// the opp is not a line
-void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
-    if (this->verb() != SkPath::kLine_Verb) {
-        return;
-    }
-    SkOpSpan* prior = NULL;
-    SkOpSpan* span = &fHead;
-    do {
-        SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
-        SkASSERT(ptT->span() == span);
-        while ((ptT = ptT->next()) != spanStopPtT) {
-            SkOpSegment* opp = ptT->span()->segment();
-            if (opp->setVisited()) {
-                continue;
-            }
-            if (opp->verb() == SkPath::kLine_Verb) {
-                continue;
-            }
-            if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
-                                                  // segment is coincident then no more coincidence
-                                                  // needs to be detected. This may not be true. 
-                continue;
-            }
-            if (span->containsCoinEnd(opp)) {
-                continue;
-            } 
-            // if already visited and visited again, check for coin
-            if (span == &fHead) {
+// FIXME
+// this returns at any difference in T, vs. a preset minimum. It may be
+// that all callers to nextSpan should use this instead.
+int SkOpSegment::nextExactSpan(int from, int step) const {
+    int to = from;
+    if (step < 0) {
+        const SkOpSpan& fromSpan = fTs[from];
+        while (--to >= 0) {
+            const SkOpSpan& span = fTs[to];
+            if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
                 continue;
             }
-            SkOpPtT* priorPtT = NULL, * priorStopPtT;
-            // find prior span containing opp segment
-            SkOpSegment* priorOpp = NULL;
-            prior = span;
-            while (!priorOpp && (prior = prior->prev())) {
-                priorStopPtT = priorPtT = prior->ptT();
-                while ((priorPtT = priorPtT->next()) != priorStopPtT) {
-                    SkOpSegment* segment = priorPtT->span()->segment();
-                    if (segment == opp) {
-                        priorOpp = opp;
-                        break;
-                    }
-                }
-            }
-            if (!priorOpp) {
-                continue;
-            }
-            SkOpPtT* oppStart = prior->ptT();
-            SkOpPtT* oppEnd = span->ptT();
-            bool swapped = priorPtT->fT > ptT->fT;
-            if (swapped) {
-                SkTSwap(priorPtT, ptT);
-                SkTSwap(oppStart, oppEnd);
-            }
-            bool flipped = oppStart->fT > oppEnd->fT;
-            bool coincident;
-            if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
-                goto swapBack;
-            }
-            {
-                // average t, find mid pt
-                double midT = (prior->t() + span->t()) / 2;
-                SkPoint midPt = this->ptAtT(midT);
-                coincident = true;
-                // if the mid pt is not near either end pt, project perpendicular through opp seg
-                if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
-                        && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
-                    coincident = false;
-                    SkIntersections i;
-                    int ptCount = SkPathOpsVerbToPoints(this->verb());
-                    SkVector dxdy = (*CurveSlopeAtT[ptCount])(pts(), midT);
-                    SkDLine ray = {{{midPt.fX, midPt.fY},
-                            {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
-                    int oppPtCount = SkPathOpsVerbToPoints(opp->verb());
-                    (*CurveIntersectRay[oppPtCount])(opp->pts(), ray, &i);
-                    // measure distance and see if it's small enough to denote coincidence
-                    for (int index = 0; index < i.used(); ++index) {
-                        SkDPoint oppPt = i.pt(index);
-                        if (oppPt.approximatelyEqual(midPt)) {
-                            SkVector oppDxdy = (*CurveSlopeAtT[oppPtCount])(opp->pts(),
-                                    i[index][0]);
-                            oppDxdy.normalize();
-                            dxdy.normalize();
-                            SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
-                            coincident |= flatness < 5000;  // FIXME: replace with tuned value
-                        }
-                    }
-                }
-            }
-            if (coincident) {
-            // mark coincidence
-                coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
-                clear_visited(&fHead);
-                missingCoincidence(coincidences, allocator);
-                return;
-            }
-    swapBack:
-            if (swapped) {
-                SkTSwap(priorPtT, ptT);
-            }
+            return to;
         }
-    } while ((span = span->next()->upCastable()));
-    clear_visited(&fHead);
-}
-
-// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
-bool SkOpSegment::moveNearby() {
-    debugValidate();
-    SkOpSpanBase* spanS = &fHead;
-    do {
-        SkOpSpanBase* test = spanS->upCast()->next();
-        SkOpSpanBase* next;
-        if (spanS->contains(test)) {
-            if (!test->final()) {
-                test->upCast()->detach(spanS->ptT());
-                continue;
-            } else if (spanS != &fHead) {
-                spanS->upCast()->detach(test->ptT());
-                spanS = test;
+    } else {
+        while (fTs[from].fTiny) {
+            from++;
+        }
+        const SkOpSpan& fromSpan = fTs[from];
+        int count = fTs.count();
+        while (++to < count) {
+            const SkOpSpan& span = fTs[to];
+            if (precisely_negative(span.fT - fromSpan.fT)) {
                 continue;
             }
+            return to;
         }
-        do {  // iterate through all spans associated with start
-            SkOpPtT* startBase = spanS->ptT();
-            next = test->final() ? NULL : test->upCast()->next();
-            do {
-                SkOpPtT* testBase = test->ptT();
-                do {
-                    if (startBase == testBase) {
-                        goto checkNextSpan;
-                    }
-                    if (testBase->duplicate()) {
-                        continue;
-                    }
-                    if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
-                        if (test == &this->fTail) {
-                            if (spanS == &fHead) {
-                                debugValidate();
-                                return true;  // if this span has collapsed, remove it from parent
-                            }
-                            this->fTail.merge(spanS->upCast());
-                            debugValidate();
-                            return true;
-                        }
-                        spanS->merge(test->upCast());
-                        spanS->upCast()->setNext(next);
-                        goto checkNextSpan;
-                    }
-                } while ((testBase = testBase->next()) != test->ptT());
-            } while ((startBase = startBase->next()) != spanS->ptT());
-    checkNextSpan:
-            ;
-        } while ((test = next));
-        spanS = spanS->upCast()->next();
-    } while (!spanS->final());
-    debugValidate();
-    return true;
-}
-
-bool SkOpSegment::operand() const {
-    return fContour->operand();
+    }
+    return -1;
 }
 
-bool SkOpSegment::oppXor() const {
-    return fContour->oppXor();
+void SkOpSegment::pinT(const SkPoint& pt, double* t) {
+    if (pt == fPts[0]) {
+        *t = 0;
+    }
+    int count = SkPathOpsVerbToPoints(fVerb);
+    if (pt == fPts[count]) {
+        *t = 1;
+    }
 }
 
-bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
-    if (fVerb == SkPath::kLine_Verb) {
-        return false;
+bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
+    SkASSERT(p1 != p2);
+    int spanCount = count();
+    int p1IndexMin = -1;
+    int p2IndexMax = spanCount;
+    for (int index = 0; index < spanCount; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fPt == p1) {
+            if (p1IndexMin < 0) {
+                p1IndexMin = index;
+            }
+        } else if (span.fPt == p2) {
+            p2IndexMax = index;
+        }
     }
-    // quads (and cubics) can loop back to nearly a line so that an opposite curve
-    // hits in two places with very different t values.
-    // OPTIMIZATION: curves could be preflighted so that, for example, something like
-    // 'controls contained by ends' could avoid this check for common curves
-    // 'ends are extremes in x or y' is cheaper to compute and real-world common
-    // on the other hand, the below check is relatively inexpensive
-    double midT = (t1 + t2) / 2;
-    SkPoint midPt = this->ptAtT(midT);
-    double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
-    return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
+    return p1IndexMin > p2IndexMax;
 }
 
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
-        int* maxWinding, int* sumWinding) {
-    int deltaSum = SpanSign(start, end);
-    *maxWinding = *sumMiWinding;
-    *sumWinding = *sumMiWinding -= deltaSum;
-    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt, 
+        SkOpSegment* other) {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        SkOpSpan &span = fTs[index];
+        if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
+            span.fCoincident = true;
+        }
+    }
 }
 
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
-        int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
-        int* oppSumWinding) {
-    int deltaSum = SpanSign(start, end);
-    int oppDeltaSum = OppSign(start, end);
+void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
+        int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
+    int deltaSum = spanSign(index, endIndex);
+    int oppDeltaSum = oppSign(index, endIndex);
     if (operand()) {
         *maxWinding = *sumSuWinding;
         *sumWinding = *sumSuWinding -= deltaSum;
@@ -1753,94 +4484,130 @@ void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
         *oppMaxWinding = *sumSuWinding;
         *oppSumWinding = *sumSuWinding -= oppDeltaSum;
     }
-    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
-    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#if DEBUG_LIMIT_WIND_SUM
+    SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+    SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+}
+
+void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
+        int* maxWinding, int* sumWinding) {
+    int deltaSum = spanSign(index, endIndex);
+    *maxWinding = *sumMiWinding;
+    *sumWinding = *sumMiWinding -= deltaSum;
+#if DEBUG_LIMIT_WIND_SUM
+    SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
 }
 
 void SkOpSegment::sortAngles() {
-    SkOpSpanBase* span = &this->fHead;
+    int spanCount = fTs.count();
+    if (spanCount <= 2) {
+        return;
+    }
+    int index = 0;
     do {
-        SkOpAngle* fromAngle = span->fromAngle();
-        SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
+        SkOpAngle* fromAngle = fTs[index].fFromAngle;
+        SkOpAngle* toAngle = fTs[index].fToAngle;
         if (!fromAngle && !toAngle) {
+            index += 1;
             continue;
         }
+        SkOpAngle* baseAngle = NULL;
+        if (fromAngle) {
+            baseAngle = fromAngle;
+            if (inLoop(baseAngle, spanCount, &index)) {
+                continue;
+            }
+        }
 #if DEBUG_ANGLE
         bool wroteAfterHeader = false;
 #endif
-        SkOpAngle* baseAngle = fromAngle;
-        if (fromAngle && toAngle) {
+        if (toAngle) {
+            if (!baseAngle) {
+                baseAngle = toAngle;
+                if (inLoop(baseAngle, spanCount, &index)) {
+                    continue;
+                }
+            } else {
+                SkDEBUGCODE(int newIndex = index);
+                SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
 #if DEBUG_ANGLE
-            SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
-                    span->debugID());
-            wroteAfterHeader = true;
+                SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+                        index);
+                wroteAfterHeader = true;
 #endif
-            fromAngle->insert(toAngle);
-        } else if (!fromAngle) {
-            baseAngle = toAngle;
+                baseAngle->insert(toAngle);
+            }
         }
-        SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+        SkOpAngle* nextFrom, * nextTo;
+        int firstIndex = index;
         do {
-            SkOpSpanBase* oSpan = ptT->span();
-            if (oSpan == span) {
-                continue;
-            }
-            SkOpAngle* oAngle = oSpan->fromAngle();
+            SkOpSpan& span = fTs[index];
+            SkOpSegment* other = span.fOther;
+            SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
+            SkOpAngle* oAngle = oSpan.fFromAngle;
             if (oAngle) {
 #if DEBUG_ANGLE
                 if (!wroteAfterHeader) {
-                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
-                            span->t(), span->debugID());
+                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+                            index);
                     wroteAfterHeader = true;
                 }
 #endif
-                if (!oAngle->loopContains(baseAngle)) {
+                if (!oAngle->loopContains(*baseAngle)) {
                     baseAngle->insert(oAngle);
                 }
             }
-            if (!oSpan->final()) {
-                oAngle = oSpan->upCast()->toAngle();
-                if (oAngle) {
+            oAngle = oSpan.fToAngle;
+            if (oAngle) {
 #if DEBUG_ANGLE
-                    if (!wroteAfterHeader) {
-                        SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
-                                span->t(), span->debugID());
-                        wroteAfterHeader = true;
-                    }
+                if (!wroteAfterHeader) {
+                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+                            index);
+                    wroteAfterHeader = true;
+                }
 #endif
-                    if (!oAngle->loopContains(baseAngle)) {
-                        baseAngle->insert(oAngle);
-                    }
+                if (!oAngle->loopContains(*baseAngle)) {
+                    baseAngle->insert(oAngle);
                 }
             }
-        } while ((ptT = ptT->next()) != stopPtT);
-        if (baseAngle->loopCount() == 1) {
-            span->setFromAngle(NULL);
-            if (toAngle) {
-                span->upCast()->setToAngle(NULL);
+            if (++index == spanCount) {
+                break;
             }
+            nextFrom = fTs[index].fFromAngle;
+            nextTo = fTs[index].fToAngle;
+        } while (fromAngle == nextFrom && toAngle == nextTo);
+        if (baseAngle && baseAngle->loopCount() == 1) {
+            index = firstIndex;
+            do {
+                SkOpSpan& span = fTs[index];
+                span.fFromAngle = span.fToAngle = NULL;
+                if (++index == spanCount) {
+                    break;
+                }
+                nextFrom = fTs[index].fFromAngle;
+                nextTo = fTs[index].fToAngle;
+            } while (fromAngle == nextFrom && toAngle == nextTo);
             baseAngle = NULL;
         }
 #if DEBUG_SORT
         SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
 #endif
-    } while (!span->final() && (span = span->upCast()->next()));
+    } while (index < spanCount);
 }
 
 // return true if midpoints were computed
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
-        SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
     SkASSERT(start != end);
-    const SkOpPtT& startPtT = *start->ptT();
-    const SkOpPtT& endPtT = *end->ptT();
-    edge[0] = startPtT.fPt;
+    edge[0] = fTs[start].fPt;
     int points = SkPathOpsVerbToPoints(fVerb);
-    edge[points] = endPtT.fPt;
+    edge[points] = fTs[end].fPt;
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = startPtT.fT;
-    double endT = endPtT.fT;
+    double startT = fTs[start].fT;
+    double endT = fTs[end].fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
@@ -1870,19 +4637,17 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
     return true;
 }
 
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
-        SkDCubic* result) const {
+// return true if midpoints were computed
+bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
     SkASSERT(start != end);
-    const SkOpPtT& startPtT = *start->ptT();
-    const SkOpPtT& endPtT = *end->ptT();
-    (*result)[0].set(startPtT.fPt);
+    (*result)[0].set(fTs[start].fPt);
     int points = SkPathOpsVerbToPoints(fVerb);
-    (*result)[points].set(endPtT.fPt);
+    (*result)[points].set(fTs[end].fPt);
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = startPtT.fT;
-    double endT = endPtT.fT;
+    double startT = fTs[start].fT;
+    double endT = fTs[end].fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
@@ -1890,7 +4655,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
             return false;
         }
         SkASSERT(fVerb == SkPath::kCubic_Verb);
-        if (startT == 0) {
+        if (start < end) {
             (*result)[1].set(fPts[1]);
             (*result)[2].set(fPts[2]);
             return false;
@@ -1908,29 +4673,49 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
     return true;
 }
 
-void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
-        SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
     SkPoint edge[4];
     subDivide(start, end, edge);
     (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
 }
 
-void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
-    SkOpSpan* span = this->head();
-    do {
-        if (!span->done()) {
+void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
+        const SkPoint& startPt) {
+    int outCount = outsidePts->count();
+    if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
+        outsidePts->push_back(endPt);
+        outsidePts->push_back(startPt);
+    }
+}
+
+void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
+    int outCount = outsidePts->count();
+    if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
+        outsidePts->push_back(startPt);
+    }
+}
+
+void SkOpSegment::undoneSpan(int* start, int* end) {
+    int tCount = fTs.count();
+    int index;
+    for (index = 0; index < tCount; ++index) {
+        if (!fTs[index].fDone) {
             break;
         }
-    } while ((span = span->next()->upCastable()));
-    SkASSERT(span);
-    *start = span;
-    *end = span->next();
+    }
+    SkASSERT(index < tCount - 1);
+    *start = index;
+    double startT = fTs[index].fT;
+    while (approximately_negative(fTs[++index].fT - startT))
+        SkASSERT(index < tCount);
+    SkASSERT(index < tCount);
+    *end = index;
 }
 
-int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
-    const SkOpSpan* lesser = start->starter(end);
-    int oppWinding = lesser->oppSum();
-    int oppSpanWinding = SkOpSegment::OppSign(start, end);
+int SkOpSegment::updateOppWinding(int index, int endIndex) const {
+    int lesser = SkMin32(index, endIndex);
+    int oppWinding = oppSum(lesser);
+    int oppSpanWinding = oppSign(index, endIndex);
     if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
             && oppWinding != SK_MaxS32) {
         oppWinding -= oppSpanWinding;
@@ -1939,24 +4724,24 @@ int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase*
 }
 
 int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
-    const SkOpSpanBase* startSpan = angle->start();
-    const SkOpSpanBase* endSpan = angle->end();
-    return updateOppWinding(endSpan, startSpan);
+    int startIndex = angle->start();
+    int endIndex = angle->end();
+    return updateOppWinding(endIndex, startIndex);
 }
 
 int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
-    const SkOpSpanBase* startSpan = angle->start();
-    const SkOpSpanBase* endSpan = angle->end();
-    return updateOppWinding(startSpan, endSpan);
+    int startIndex = angle->start();
+    int endIndex = angle->end();
+    return updateOppWinding(startIndex, endIndex);
 }
 
-int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
-    const SkOpSpan* lesser = start->starter(end);
-    int winding = lesser->windSum();
+int SkOpSegment::updateWinding(int index, int endIndex) const {
+    int lesser = SkMin32(index, endIndex);
+    int winding = windSum(lesser);
     if (winding == SK_MinS32) {
         return winding;
     }
-    int spanWinding = SkOpSegment::SpanSign(start, end);
+    int spanWinding = spanSign(index, endIndex);
     if (winding && UseInnerWinding(winding - spanWinding, winding)
             && winding != SK_MaxS32) {
         winding -= spanWinding;
@@ -1965,15 +4750,26 @@ int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* en
 }
 
 int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
-    const SkOpSpanBase* startSpan = angle->start();
-    const SkOpSpanBase* endSpan = angle->end();
-    return updateWinding(endSpan, startSpan);
+    int startIndex = angle->start();
+    int endIndex = angle->end();
+    return updateWinding(endIndex, startIndex);
+}
+
+int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
+    int lesser = SkMin32(index, endIndex);
+    int winding = windSum(lesser);
+    int spanWinding = spanSign(endIndex, index);
+    if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
+            && winding != SK_MaxS32) {
+        winding -= spanWinding;
+    }
+    return winding;
 }
 
 int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
-    const SkOpSpanBase* startSpan = angle->start();
-    const SkOpSpanBase* endSpan = angle->end();
-    return updateWinding(startSpan, endSpan);
+    int startIndex = angle->start();
+    int endIndex = angle->end();
+    return updateWindingReverse(endIndex, startIndex);
 }
 
 // OPTIMIZATION: does the following also work, and is it any faster?
@@ -1988,17 +4784,25 @@ bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
     return result;
 }
 
-int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
-        SkScalar* dx) const {
-    if (approximately_zero(tHit - span->t())) {  // if we hit the end of a span, disregard
+bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
+    SkASSERT(outerWinding != SK_MaxS32);
+    SkASSERT(innerWinding != SK_MaxS32);
+    int absOut = abs(outerWinding);
+    int absIn = abs(innerWinding);
+    bool result = absOut == absIn ? true : absOut < absIn;
+    return result;
+}
+
+int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
+    if (approximately_zero(tHit - t(tIndex))) {  // if we hit the end of a span, disregard
         return SK_MinS32;
     }
-    int winding = crossOpp ? span->oppSum() : span->windSum();
+    int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
     SkASSERT(winding != SK_MinS32);
-    int windVal = crossOpp ? span->oppValue() : span->windValue();
+    int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
 #if DEBUG_WINDING_AT_T
     SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
-            debugID(), crossOpp, tHit, span->t(), winding, windVal);
+            debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
 #endif
     // see if a + change in T results in a +/- change in X (compute x'(T))
     *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
@@ -2024,6 +4828,20 @@ int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
 }
 
 int SkOpSegment::windSum(const SkOpAngle* angle) const {
-    const SkOpSpan* minSpan = angle->start()->starter(angle->end());
-    return minSpan->windSum();
+    int start = angle->start();
+    int end = angle->end();
+    int index = SkMin32(start, end);
+    return windSum(index);
+}
+
+void SkOpSegment::zeroSpan(SkOpSpan* span) {
+    SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
+    span->fWindValue = 0;
+    span->fOppValue = 0;
+    if (span->fTiny || span->fSmall) {
+        return;
+    }
+    SkASSERT(!span->fDone);
+    span->fDone = true;
+    ++fDoneSpans;
 }
index c1c6e696feb436b1847e97d6d38429144b5a0e13..b4da929d99c4e573acf887292b43f8f64d371510 100644 (file)
 
 #include "SkOpAngle.h"
 #include "SkOpSpan.h"
-#include "SkOpTAllocator.h"
 #include "SkPathOpsBounds.h"
 #include "SkPathOpsCurve.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
 
-class SkOpCoincidence;
-class SkOpContour;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+#include "SkThread.h"
+#endif
+
+struct SkCoincidence;
 class SkPathWriter;
 
 class SkOpSegment {
 public:
-    enum AllowAlias {
-        kAllowAlias,
-        kNoAlias
-    };
+    SkOpSegment() {
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+        fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
+#endif
+    }
 
     bool operator<(const SkOpSegment& rh) const {
         return fBounds.fTop < rh.fBounds.fTop;
     }
 
-    SkOpAngle* activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
-                            bool* done, bool* sortable);
-    SkOpAngle* activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
-                                       SkOpSpanBase** endPtr, bool* done, bool* sortable);
-    SkOpAngle* activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
-                                       SkOpSpanBase** endPtr, bool* done, bool* sortable);
-    bool activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
-                  SkPathOp op);
-    bool activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end, SkPathOp op,
-                  int* sumMiWinding, int* sumSuWinding);
-
-    SkPoint activeLeftTop(SkOpSpanBase** firstT);
-
-    bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
-    bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
+    struct AlignedSpan  {
+        double fOldT;
+        double fT;
+        SkPoint fOldPt;
+        SkPoint fPt;
+        const SkOpSegment* fSegment;
+        const SkOpSegment* fOther1;
+        const SkOpSegment* fOther2;
+    };
 
-    void addCubic(SkPoint pts[4], SkOpContour* parent) {
-        init(pts, parent, SkPath::kCubic_Verb);
-        fBounds.setCubicBounds(pts);
+    const SkPathOpsBounds& bounds() const {
+        return fBounds;
     }
 
-    void addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path,
-                    bool active) const;
-
-    SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
-        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-        angle->set(&fTail, fTail.prev());
-        fTail.setFromAngle(angle);
-        return angle;
+    // OPTIMIZE
+    // when the edges are initially walked, they don't automatically get the prior and next
+    // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
+    // and would additionally remove the need for similar checks in condition edges. It would
+    // also allow intersection code to assume end of segment intersections (maybe?)
+    bool complete() const {
+        int count = fTs.count();
+        return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
     }
 
-    void addLine(SkPoint pts[2], SkOpContour* parent) {
-        init(pts, parent, SkPath::kLine_Verb);
-        fBounds.set(pts, 2);
+    int count() const {
+        return fTs.count();
     }
 
-    SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
-    SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
-    SkOpAngle* addSingletonAngles(int step, SkChunkAlloc* );
-    SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
-
-    SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
-        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-        angle->set(&fHead, fHead.next());
-        fHead.setToAngle(angle);
-        return angle;
+    bool done() const {
+        SkASSERT(fDoneSpans <= fTs.count());
+        return fDoneSpans == fTs.count();
     }
 
-    void addQuad(SkPoint pts[3], SkOpContour* parent) {
-        init(pts, parent, SkPath::kQuad_Verb);
-        fBounds.setQuadBounds(pts);
+    bool done(int min) const {
+        return fTs[min].fDone;
     }
 
-    SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
-
-    void align();
-    static bool BetweenTs(const SkOpSpanBase* lesser, double testT, const SkOpSpanBase* greater);
-
-    const SkPathOpsBounds& bounds() const {
-        return fBounds;
+    bool done(const SkOpAngle* angle) const {
+        return done(SkMin32(angle->start(), angle->end()));
     }
 
-    void bumpCount() {
-        ++fCount;
+    SkDPoint dPtAtT(double mid) const {
+        return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
     }
 
-    void calcAngles(SkChunkAlloc*);
-    void checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
-    void checkNearCoincidence(SkOpAngle* );
-    bool clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const;
-    static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-                              SkOpAngle::IncludeType );
-    static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-                                     SkOpAngle::IncludeType );
-    int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
-
-    SkOpContour* contour() const {
-        return fContour;
+    SkVector dxdy(int index) const {
+        return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
     }
 
-    int count() const {
-        return fCount;
+    SkScalar dy(int index) const {
+        return dxdy(index).fY;
     }
 
-    SkOpSpan* crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
-                            SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical);
-
-    void debugAddAngle(double startT, double endT, SkChunkAlloc*);
-    const SkOpAngle* debugAngle(int id) const;
-    SkOpContour* debugContour(int id);
-
-    int debugID() const {
-        return PATH_OPS_DEBUG_RELEASE(fID, -1);
+    bool hasMultiples() const {
+        return fMultiples;
     }
 
-#if DEBUG_SWAP_TOP
-    int debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
-#endif
-
-    SkOpAngle* debugLastAngle();
-    const SkOpPtT* debugPtT(int id) const;
-    void debugReset();
-    const SkOpSegment* debugSegment(int id) const;
-
-#if DEBUG_ACTIVE_SPANS
-    void debugShowActiveSpans() const;
-#endif
-#if DEBUG_MARK_DONE
-    void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding);
-    void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, int oppWinding);
-#endif
-
-    const SkOpSpanBase* debugSpan(int id) const;
-    void debugValidate() const;
-    void detach(const SkOpSpan* );
-    double distSq(double t, SkOpAngle* opp);
-
-    bool done() const {
-        SkASSERT(fDoneCount <= fCount);
-        return fDoneCount == fCount;
+    bool hasSmall() const {
+        return fSmall;
     }
 
-    bool done(const SkOpAngle* angle) const {
-        return angle->start()->starter(angle->end())->done();
+    bool hasTiny() const {
+        return fTiny;
     }
 
-    SkDPoint dPtAtT(double mid) const {
-        return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+    bool intersected() const {
+        return fTs.count() > 0;
     }
 
-    SkDVector dSlopeAtT(double mid) const {
-        return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+    bool isCanceled(int tIndex) const {
+        return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
     }
 
-    void dump() const;
-    void dumpAll() const;
-    void dumpAngles() const;
-    void dumpCoin() const;
-    void dumpPts() const;
-
-    SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
-                             SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
-                             int xorMiMask, int xorSuMask);
-    SkOpSegment* findNextWinding(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
-                                  SkOpSpanBase** nextEnd, bool* unsortable);
-    SkOpSegment* findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable);
-    SkOpSegment* findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
-                          bool* unsortable, SkChunkAlloc* );
-    SkOpGlobalState* globalState() const;
-
-    const SkOpSpan* head() const {
-        return &fHead;
-    }
-
-    SkOpSpan* head() {
-        return &fHead;
-    }
-
-    void init(SkPoint pts[], SkOpContour* parent, SkPath::Verb verb);
-    void initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
-                     SkOpAngle::IncludeType angleIncludeType);
-    bool initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit, int winding,
-            SkScalar hitDx, int oppWind, SkScalar hitOppDx);
-
-    SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
-        SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
-        SkOpSpanBase* next = prev->next();
-        result->setPrev(prev);
-        prev->setNext(result);
-        SkDEBUGCODE(result->ptT()->fT = 0);
-        result->setNext(next);
-        if (next) {
-            next->setPrev(result);
-        }
-        return result;
+    bool isConnected(int startIndex, int endIndex) const {
+        return fTs[startIndex].fWindSum != SK_MinS32 || fTs[endIndex].fWindSum != SK_MinS32;
     }
 
-    bool isClose(double t, const SkOpSegment* opp) const;
-
     bool isHorizontal() const {
         return fBounds.fTop == fBounds.fBottom;
     }
 
-    SkOpSegment* isSimple(SkOpSpanBase** end, int* step) {
-        return nextChase(end, step, NULL, NULL);
-    }
-
     bool isVertical() const {
         return fBounds.fLeft == fBounds.fRight;
     }
 
-    bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
-        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start->t(), end->t());
+    bool isVertical(int start, int end) const {
+        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
     }
 
-    bool isXor() const;
+    bool operand() const {
+        return fOperand;
+    }
 
-    const SkPoint& lastPt() const {
-        return fPts[SkPathOpsVerbToPoints(fVerb)];
+    int oppSign(const SkOpAngle* angle) const {
+        SkASSERT(angle->segment() == this);
+        return oppSign(angle->start(), angle->end());
     }
 
-    SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
-    bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
-            SkOpSpanBase** lastPtr);
-    bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
-            int oppWinding, SkOpSpanBase** lastPtr);
-    SkOpSpanBase* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
-    SkOpSpanBase* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
-                         const SkOpAngle* angle);
-    void markDone(SkOpSpan* );
-    bool markWinding(SkOpSpan* , int winding);
-    bool markWinding(SkOpSpan* , int winding, int oppWinding);
-    bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
-    void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
-    bool monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
-    bool moveNearby();
+    int oppSign(int startIndex, int endIndex) const {
+        int result = startIndex < endIndex ? -fTs[startIndex].fOppValue : fTs[endIndex].fOppValue;
+#if DEBUG_WIND_BUMP
+        SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
+#endif
+        return result;
+    }
 
-    SkOpSegment* next() const {
-        return fNext;
+    int oppSum(int tIndex) const {
+        return fTs[tIndex].fOppSum;
     }
 
-    static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
-    SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
-    bool operand() const;
+    int oppSum(const SkOpAngle* angle) const {
+        int lesser = SkMin32(angle->start(), angle->end());
+        return fTs[lesser].fOppSum;
+    }
 
-    static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
-        int result = start->t() < end->t() ? -start->upCast()->oppValue()
-                : end->upCast()->oppValue();
-        return result;
+    int oppValue(int tIndex) const {
+        return fTs[tIndex].fOppValue;
     }
 
-    bool oppXor() const;
+    int oppValue(const SkOpAngle* angle) const {
+        int lesser = SkMin32(angle->start(), angle->end());
+        return fTs[lesser].fOppValue;
+    }
 
-    const SkOpSegment* prev() const {
-        return fPrev;
+#if DEBUG_VALIDATE
+    bool oppXor() const {
+        return fOppXor;
     }
+#endif
 
     SkPoint ptAtT(double mid) const {
         return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
@@ -273,113 +171,399 @@ public:
         return fPts;
     }
 
-    bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
-        return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
+    void reset() {
+        init(NULL, (SkPath::Verb) -1, false, false);
+        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        fTs.reset();
     }
 
-    bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
-        return ptsDisjoint(span.fT, span.fPt, t, pt);
+    bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
+
+    void setOppXor(bool isOppXor) {
+        fOppXor = isOppXor;
     }
 
-    bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
+    void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
+        int deltaSum = spanSign(index, endIndex);
+        *maxWinding = *sumWinding;
+        *sumWinding -= deltaSum;
+    }
 
-    void resetVisited() {
-        fVisited = false;
+    const SkOpSpan& span(int tIndex) const {
+        return fTs[tIndex];
     }
 
-    void setContour(SkOpContour* contour) {
-        fContour = contour;
+    const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
+        SkASSERT(tStart != tEnd);
+        const SkOpSpan& span = fTs[tStart];
+        return tStart < tEnd ? span.fToAngle : span.fFromAngle;
     }
 
-    void setNext(SkOpSegment* next) {
-        fNext = next;
+    // FIXME: create some sort of macro or template that avoids casting
+    SkOpAngle* spanToAngle(int tStart, int tEnd) {
+        const SkOpAngle* cAngle = (const_cast<const SkOpSegment*>(this))->spanToAngle(tStart, tEnd);
+        return const_cast<SkOpAngle*>(cAngle);
     }
 
-    void setPrev(SkOpSegment* prev) {
-        fPrev = prev;
+    int spanSign(const SkOpAngle* angle) const {
+        SkASSERT(angle->segment() == this);
+        return spanSign(angle->start(), angle->end());
     }
 
-    bool setVisited() {
-        if (fVisited) {
-            return false;
-        }
-        return (fVisited = true);
+    int spanSign(int startIndex, int endIndex) const {
+        int result = startIndex < endIndex ? -fTs[startIndex].fWindValue : fTs[endIndex].fWindValue;
+#if DEBUG_WIND_BUMP
+        SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
+#endif
+        return result;
     }
 
-    void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
-        int deltaSum = SpanSign(start, end);
-        *maxWinding = *sumWinding;
-        *sumWinding -= deltaSum;
+    double t(int tIndex) const {
+        return fTs[tIndex].fT;
     }
 
-    void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
-                       int* maxWinding, int* sumWinding);
-    void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
-                       int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
-    void sortAngles();
+    double tAtMid(int start, int end, double mid) const {
+        return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+    }
 
-    static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
-        int result = start->t() < end->t() ? -start->upCast()->windValue()
-                : end->upCast()->windValue();
-        return result;
+    void updatePts(const SkPoint pts[]) {
+        fPts = pts;
     }
 
-    SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
-        SkASSERT(start != end);
-        return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
+    SkPath::Verb verb() const {
+        return fVerb;
     }
 
-    bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPoint edge[4]) const;
-    bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkDCubic* result) const;
-    void subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
-                         SkPathOpsBounds* bounds) const;
+    int windSum(int tIndex) const {
+        return fTs[tIndex].fWindSum;
+    }
 
-    const SkOpSpanBase* tail() const {
-        return &fTail;
+    int windValue(int tIndex) const {
+        return fTs[tIndex].fWindValue;
     }
 
-    SkOpSpanBase* tail() {
-        return &fTail;
+#if defined(SK_DEBUG) || DEBUG_WINDING
+    SkScalar xAtT(int index) const {
+        return xAtT(&fTs[index]);
     }
+#endif
 
-    static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
-        return start->t() * (1 - mid) + end->t() * mid;
+#if DEBUG_VALIDATE
+    bool _xor() const {  // FIXME: used only by SkOpAngle::debugValidateLoop()
+        return fXor;
     }
+#endif
 
-    void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
-    int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
-    int updateOppWinding(const SkOpAngle* angle) const;
-    int updateOppWindingReverse(const SkOpAngle* angle) const;
-    int updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
-    int updateWinding(const SkOpAngle* angle) const;
-    int updateWindingReverse(const SkOpAngle* angle) const;
+    const SkPoint& xyAtT(const SkOpSpan* span) const {
+        return span->fPt;
+    }
 
-    static bool UseInnerWinding(int outerWinding, int innerWinding);
+    const SkPoint& xyAtT(int index) const {
+        return xyAtT(&fTs[index]);
+    }
 
-    SkPath::Verb verb() const {
-        return fVerb;
+#if defined(SK_DEBUG) || DEBUG_WINDING
+    SkScalar yAtT(int index) const {
+        return yAtT(&fTs[index]);
     }
+#endif
 
-    int windingAtT(double tHit, const SkOpSpan* span, bool crossOpp, SkScalar* dx) const;
+    const SkOpAngle* activeAngle(int index, int* start, int* end, bool* done,
+                                 bool* sortable) const;
+    SkPoint activeLeftTop(int* firstT) const;
+    bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op);
+    bool activeWinding(int index, int endIndex);
+    void addCubic(const SkPoint pts[4], bool operand, bool evenOdd);
+    void addCurveTo(int start, int end, SkPathWriter* path, bool active) const;
+    void addEndSpan(int endIndex);
+    void addLine(const SkPoint pts[2], bool operand, bool evenOdd);
+    void addOtherT(int index, double otherT, int otherIndex);
+    void addQuad(const SkPoint pts[3], bool operand, bool evenOdd);
+    void addSimpleAngle(int endIndex);
+    int addSelfT(const SkPoint& pt, double newT);
+    void addStartSpan(int endIndex);
+    int addT(SkOpSegment* other, const SkPoint& pt, double newT);
+    void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+    bool addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+                        SkOpSegment* other);
+    const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+                             const SkPoint& pt);
+    const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+                             const SkPoint& pt, const SkPoint& oPt);
+    void alignMultiples(SkTDArray<AlignedSpan>* aligned);
+    bool alignSpan(int index, double thisT, const SkPoint& thisPt);
+    void alignSpanState(int start, int end);
+    bool betweenTs(int lesser, double testT, int greater) const;
+    void blindCancel(const SkCoincidence& coincidence, SkOpSegment* other);
+    void blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other);
+    bool calcAngles();
+    double calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+                           double hiEnd, const SkOpSegment* other, int thisEnd);
+    double calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+                             double hiEnd, const SkOpSegment* other, int thisEnd);
+    void checkDuplicates();
+    bool checkEnds();
+    void checkMultiples();
+    void checkSmall();
+    bool checkSmall(int index) const;
+    void checkTiny();
+    int computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType);
+    bool containsPt(const SkPoint& , int index, int endIndex) const;
+    int crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, bool* hitSomething,
+                     double mid, bool opp, bool current) const;
+    bool findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, int oEnd,
+                             int step, SkPoint* startPt, SkPoint* endPt, double* endT) const;
+    SkOpSegment* findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+                            bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask);
+    SkOpSegment* findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+                                 bool* unsortable);
+    SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable);
+    int findExactT(double t, const SkOpSegment* ) const;
+    int findOtherT(double t, const SkOpSegment* ) const;
+    int findT(double t, const SkPoint& , const SkOpSegment* ) const;
+    SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool firstPass);
+    void fixOtherTIndex();
+    bool inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
+                        const SkOpAngle* angle) const;
+    void initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType);
+    bool initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
+                     SkScalar hitOppDx);
+    bool isMissing(double startT, const SkPoint& pt) const;
+    bool isTiny(const SkOpAngle* angle) const;
+    bool joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt, int step,
+                         bool cancel);
+    SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
+    SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
+    bool markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
+                             SkOpSpan** lastPtr);
+    SkOpSpan* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
+                        const SkOpAngle* angle);
+    void markDone(int index, int winding);
+    void markDoneBinary(int index);
+    void markDoneFinal(int index);
+    void markDoneUnary(int index);
+    bool nextCandidate(int* start, int* end) const;
+    int nextSpan(int from, int step) const;
+    void pinT(const SkPoint& pt, double* t);
+    void setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
+            int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
+    void sortAngles();
+    bool subDivide(int start, int end, SkPoint edge[4]) const;
+    bool subDivide(int start, int end, SkDCubic* result) const;
+    void undoneSpan(int* start, int* end);
+    int updateOppWindingReverse(const SkOpAngle* angle) const;
+    int updateWindingReverse(const SkOpAngle* angle) const;
+    static bool UseInnerWinding(int outerWinding, int innerWinding);
+    static bool UseInnerWindingReverse(int outerWinding, int innerWinding);
+    int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const;
     int windSum(const SkOpAngle* angle) const;
-
-    SkPoint* writablePt(bool end) {
-        return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
+// available for testing only
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+    int debugID() const {
+        return fID;
+    }
+#else
+    int debugID() const {
+        return -1;
     }
+#endif
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+    void debugShowActiveSpans() const;
+#endif
+#if DEBUG_CONCIDENT
+    void debugShowTs(const char* prefix) const;
+#endif
+#if DEBUG_SHOW_WINDING
+    int debugShowWindingValues(int slotCount, int ofInterest) const;
+#endif
+    const SkTDArray<SkOpSpan>& debugSpans() const;
+    void debugValidate() const;
+    // available to testing only
+    const SkOpAngle* debugLastAngle() const;
+    void dumpAngles() const;
+    void dumpContour(int firstID, int lastID) const;
+    void dumpPts() const;
+    void dumpSpans() const;
 
 private:
-    SkOpSpan fHead;  // the head span always has its t set to zero
-    SkOpSpanBase fTail;  // the tail span always has its t set to one
-    SkOpContour* fContour;
-    SkOpSegment* fNext;  // forward-only linked list used by contour to walk the segments
-    const SkOpSegment* fPrev;
-    SkPoint* fPts;  // pointer into array of points owned by edge builder that may be tweaked
-    SkPathOpsBounds fBounds;  // tight bounds
-    int fCount;  // number of spans (one for a non-intersecting segment)
-    int fDoneCount;  // number of processed spans (zero initially)
+    struct MissingSpan  {
+        double fT;
+        double fEndT;
+        SkOpSegment* fSegment;
+        SkOpSegment* fOther;
+        double fOtherT;
+        SkPoint fPt;
+    };
+
+    const SkOpAngle* activeAngleInner(int index, int* start, int* end, bool* done,
+                                      bool* sortable) const;
+    const SkOpAngle* activeAngleOther(int index, int* start, int* end, bool* done,
+                                      bool* sortable) const;
+    bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
+                  int* sumMiWinding, int* sumSuWinding);
+    bool activeWinding(int index, int endIndex, int* sumWinding);
+    void addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+    void addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+    SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** );
+    SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** );
+    SkOpAngle* addSingletonAngles(int step);
+    void alignRange(int lower, int upper, const SkOpSegment* other, int oLower, int oUpper);
+    void alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other, double otherT,
+                   const SkOpSegment* other2, SkOpSpan* oSpan, SkTDArray<AlignedSpan>* );
+    bool betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const;
+    void bumpCoincidentBlind(bool binary, int index, int last);
+    bool bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* index,
+                            SkTArray<SkPoint, true>* outsideTs);
+    void bumpCoincidentOBlind(int index, int last);
+    bool bumpCoincidentOther(const SkOpSpan& oTest, int* index,
+                             SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt);
+    bool bumpSpan(SkOpSpan* span, int windDelta, int oppDelta);
+    bool calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts);
+    bool checkForSmall(const SkOpSpan* span, const SkPoint& pt, double newT,
+                       int* less, int* more) const;
+    void checkLinks(const SkOpSpan* ,
+                    SkTArray<MissingSpan, true>* missingSpans) const;
+    static void CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
+                             const SkOpSpan* oFirst, const SkOpSpan* oLast,
+                             const SkOpSpan** missingPtr,
+                             SkTArray<MissingSpan, true>* missingSpans);
+    int checkSetAngle(int tIndex) const;
+    void checkSmallCoincidence(const SkOpSpan& span, SkTArray<MissingSpan, true>* );
+    bool coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const;
+    bool clockwise(int tStart, int tEnd, bool* swap) const;
+    static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+                              SkOpAngle::IncludeType );
+    static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+                                     SkOpAngle::IncludeType );
+    bool containsT(double t, const SkOpSegment* other, double otherT) const;
+    bool decrementSpan(SkOpSpan* span);
+    int findEndSpan(int endIndex) const;
+    int findStartSpan(int startIndex) const;
+    int firstActive(int tIndex) const;
+    const SkOpSpan& firstSpan(const SkOpSpan& thisSpan) const;
+    void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd);
+    bool inCoincidentSpan(double t, const SkOpSegment* other) const;
+    bool inconsistentWinding(const SkOpAngle* , int maxWinding, int oppMaxWinding) const;
+    bool inconsistentWinding(int min, int maxWinding, int oppMaxWinding) const;
+    bool inconsistentWinding(const char* funName, int tIndex, int winding, int oppWinding) const;
+    bool inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const;
+#if OLD_CHASE
+    bool isSimple(int end) const;
+#else
+    SkOpSegment* isSimple(int* end, int* step);
+#endif
+    bool isTiny(int index) const;
+    const SkOpSpan& lastSpan(const SkOpSpan& thisSpan) const;
+    void matchWindingValue(int tIndex, double t, bool borrowWind);
+    SkOpSpan* markAndChaseDone(int index, int endIndex, int winding);
+    SkOpSpan* markAndChaseDoneBinary(const SkOpAngle* angle, int winding, int oppWinding);
+    bool markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr);
+    bool markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr);
+    bool markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+                             SkOpSpan** lastPtr);
+    SkOpSpan* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
+    void markDoneBinary(int index, int winding, int oppWinding);
+    SkOpSpan* markAndChaseDoneUnary(const SkOpAngle* angle, int winding);
+    void markOneDone(const char* funName, int tIndex, int winding);
+    void markOneDoneBinary(const char* funName, int tIndex);
+    void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding);
+    void markOneDoneFinal(const char* funName, int tIndex);
+    void markOneDoneUnary(const char* funName, int tIndex);
+    bool markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr);
+    bool markOneWinding(const char* funName, int tIndex, int winding, int oppWinding,
+                        SkOpSpan** lastPtr);
+    bool markWinding(int index, int winding);
+    bool markWinding(int index, int winding, int oppWinding);
+    bool monotonicInY(int tStart, int tEnd) const;
+
+    bool multipleEnds() const { return fTs[count() - 2].fT == 1; }
+    bool multipleStarts() const { return fTs[1].fT == 0; }
+
+    SkOpSegment* nextChase(int* index, int* step, int* min, SkOpSpan** last) const;
+    int nextExactSpan(int from, int step) const;
+    void resetSpanFlags();
+    bool serpentine(int tStart, int tEnd) const;
+    void setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,  SkOpSegment* other);
+    void setFromAngle(int endIndex, SkOpAngle* );
+    void setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span);
+    void setToAngle(int endIndex, SkOpAngle* );
+    void setUpWindings(int index, int endIndex, int* sumMiWinding,
+            int* maxWinding, int* sumWinding);
+    void subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const;
+    static void TrackOutsidePair(SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt,
+            const SkPoint& startPt);
+    static void TrackOutside(SkTArray<SkPoint, true>* outsideTs, const SkPoint& startPt);
+    int updateOppWinding(int index, int endIndex) const;
+    int updateOppWinding(const SkOpAngle* angle) const;
+    int updateWinding(int index, int endIndex) const;
+    int updateWinding(const SkOpAngle* angle) const;
+    int updateWindingReverse(int index, int endIndex) const;
+    SkOpSpan* verifyOneWinding(const char* funName, int tIndex);
+    SkOpSpan* verifyOneWindingU(const char* funName, int tIndex);
+
+    SkScalar xAtT(const SkOpSpan* span) const {
+        return xyAtT(span).fX;
+    }
+
+    SkScalar yAtT(const SkOpSpan* span) const {
+        return xyAtT(span).fY;
+    }
+
+    void zeroSpan(SkOpSpan* span);
+
+#if DEBUG_SWAP_TOP
+    bool controlsContainedByEnds(int tStart, int tEnd) const;
+#endif
+    void debugAddAngle(int start, int end);
+#if DEBUG_CONCIDENT
+    void debugAddTPair(double t, const SkOpSegment& other, double otherT) const;
+#endif
+#if DEBUG_ANGLE
+    void debugCheckPointsEqualish(int tStart, int tEnd) const;
+#endif
+#if DEBUG_SWAP_TOP
+    int debugInflections(int index, int endIndex) const;
+#endif
+#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
+    void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
+    void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);
+#endif
+#if DEBUG_WINDING
+    static char as_digit(int value) {
+        return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
+    }
+#endif
+    // available to testing only
+    void debugConstruct();
+    void debugConstructCubic(SkPoint shortQuad[4]);
+    void debugConstructLine(SkPoint shortQuad[2]);
+    void debugConstructQuad(SkPoint shortQuad[3]);
+    void debugReset();
+    void dumpDPts() const;
+    void dumpHexPts() const;
+    void dumpSpan(int index) const;
+
+    const SkPoint* fPts;
+    SkPathOpsBounds fBounds;
+    // FIXME: can't convert to SkTArray because it uses insert
+    SkTDArray<SkOpSpan> fTs;  // 2+ (always includes t=0 t=1) -- at least (number of spans) + 1
+    SkOpAngleSet fAngles;  // empty or 2+ -- (number of non-zero spans) * 2
+    // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
+    int fDoneSpans;  // quick check that segment is finished
+    // OPTIMIZATION: force the following to be byte-sized
     SkPath::Verb fVerb;
-    bool fVisited;  // used by missing coincidence check
-    PATH_OPS_DEBUG_CODE(int fID);
+    bool fLoop;   // set if cubic intersects itself
+    bool fMultiples;  // set if curve intersects multiple other curves at one interior point
+    bool fOperand;
+    bool fXor;  // set if original contour had even-odd fill
+    bool fOppXor;  // set if opposite operand had even-odd fill
+    bool fSmall;  // set if some span is small
+    bool fTiny;  // set if some span is tiny
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+    int fID;
+#endif
+
+    friend class PathOpsSegmentTester;
 };
 
 #endif
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
deleted file mode 100755 (executable)
index 37d5120..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkOpCoincidence.h"
-#include "SkOpContour.h"
-#include "SkOpSegment.h"
-#include "SkPathWriter.h"
-
-bool SkOpPtT::alias() const {
-    return this->span()->ptT() != this;
-}
-
-SkOpContour* SkOpPtT::contour() const {
-    return segment()->contour();
-}
-
-SkOpGlobalState* SkOpPtT::globalState() const {
-    return contour()->globalState(); 
-}
-
-void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
-    fT = t;
-    fPt = pt;
-    fSpan = span;
-    fNext = this;
-    fDuplicatePt = duplicate;
-    fDeleted = false;
-    PATH_OPS_DEBUG_CODE(fID = span->globalState()->nextPtTID());
-}
-
-bool SkOpPtT::onEnd() const {
-    const SkOpSpanBase* span = this->span();
-    if (span->ptT() != this) {
-        return false;
-    }
-    const SkOpSegment* segment = this->segment();
-    return span == segment->head() || span == segment->tail();
-}
-
-SkOpPtT* SkOpPtT::prev() {
-    SkOpPtT* result = this;
-    SkOpPtT* next = this;
-    while ((next = next->fNext) != this) {
-        result = next;
-    }
-    SkASSERT(result->fNext == this);
-    return result;
-}
-
-SkOpPtT* SkOpPtT::remove() {
-    SkOpPtT* prev = this;
-    do {
-        SkOpPtT* next = prev->fNext;
-        if (next == this) {
-            prev->removeNext(this);
-            SkASSERT(prev->fNext != prev);
-            fDeleted = true;
-            return prev;
-        }
-        prev = next;
-    } while (prev != this);
-    SkASSERT(0);
-    return NULL;
-}
-
-void SkOpPtT::removeNext(SkOpPtT* kept) {
-    SkASSERT(this->fNext);
-    SkOpPtT* next = this->fNext;
-    SkASSERT(this != next->fNext);
-    this->fNext = next->fNext;
-    SkOpSpanBase* span = next->span();
-    next->setDeleted();
-    if (span->ptT() == next) {
-        span->upCast()->detach(kept);
-    }
-}
-
-const SkOpSegment* SkOpPtT::segment() const {
-    return span()->segment();
-}
-
-SkOpSegment* SkOpPtT::segment() {
-    return span()->segment();
-}
-
-// find the starting or ending span with an existing loop of angles
-// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
-// FIXME? assert that only one other span has a valid windValue or oppValue
-void SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) {
-    SkOpAngle* angle;
-    if (checkFrom) {
-        SkASSERT(this->final());
-        if (this->fromAngle()) {
-            SkASSERT(this->fromAngle()->loopCount() == 2);
-            return;
-        }
-        angle = this->segment()->addEndSpan(allocator);
-    } else {
-        SkASSERT(this->t() == 0);
-        SkOpSpan* span = this->upCast();
-        if (span->toAngle()) {
-            SkASSERT(span->toAngle()->loopCount() == 2);
-            SkASSERT(!span->fromAngle());
-            span->setFromAngle(span->toAngle()->next());
-            return;
-        }
-        angle = this->segment()->addStartSpan(allocator);
-    }
-    SkOpPtT* ptT = this->ptT();
-    SkOpSpanBase* oSpanBase;
-    SkOpSpan* oSpan;
-    SkOpSegment* other;
-    do {
-        ptT = ptT->next();
-        oSpanBase = ptT->span();
-        oSpan = oSpanBase->upCastable();
-        other = oSpanBase->segment();
-        if (oSpan && oSpan->windValue()) {
-            break;
-        }
-        if (oSpanBase->t() == 0) {
-            continue;
-        }
-        SkOpSpan* oFromSpan = oSpanBase->prev();
-        SkASSERT(oFromSpan->t() < 1);
-        if (oFromSpan->windValue()) {
-            break;
-        }
-    } while (ptT != this->ptT());
-    SkOpAngle* oAngle;
-    if (checkFrom) {
-        oAngle = other->addStartSpan(allocator);
-        SkASSERT(oSpan && !oSpan->final());
-        SkASSERT(oAngle == oSpan->toAngle());
-    } else {
-        oAngle = other->addEndSpan(allocator);
-        SkASSERT(oAngle == oSpanBase->fromAngle());
-    }
-    angle->insert(oAngle);
-}
-
-void SkOpSpanBase::align() {
-    if (this->fAligned) {
-        return;
-    }
-    SkASSERT(!zero_or_one(this->fPtT.fT));
-    SkASSERT(this->fPtT.next());
-    // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
-    SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
-    while ((ptT = ptT->next()) != stopPtT) {
-        if (zero_or_one(ptT->fT)) {
-            SkOpSegment* segment = ptT->segment();
-            SkASSERT(this->segment() != segment);
-            SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
-            if (ptT->fT) {
-                segment->tail()->alignEnd(1, segment->lastPt());
-            } else {
-                segment->head()->alignEnd(0, segment->pts()[0]);
-            }
-            return;
-        }
-    }
-    alignInner();
-    this->fAligned = true;
-}
-
-
-// FIXME: delete spans that collapse
-// delete segments that collapse
-// delete contours that collapse
-void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
-    SkASSERT(zero_or_one(t));
-    SkOpSegment* segment = this->segment();
-    SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
-    alignInner();
-    *segment->writablePt(!!t) = pt;
-    SkOpPtT* ptT = &this->fPtT;
-    SkASSERT(t == ptT->fT);
-    SkASSERT(pt == ptT->fPt);
-    SkOpPtT* test = ptT, * stopPtT = ptT;
-    while ((test = test->next()) != stopPtT) {
-        SkOpSegment* other = test->segment();
-        if (other == this->segment()) {
-            continue;
-        }
-        if (!zero_or_one(test->fT)) {
-            continue;
-        }
-        *other->writablePt(!!test->fT) = pt;
-    }
-    this->fAligned = true;
-}
-
-void SkOpSpanBase::alignInner() {
-    // force the spans to share points and t values
-    SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
-    const SkPoint& pt = ptT->fPt;
-    do {
-        ptT->fPt = pt;
-        const SkOpSpanBase* span = ptT->span();
-        SkOpPtT* test = ptT;
-        do {
-            SkOpPtT* prev = test;
-            if ((test = test->next()) == stopPtT) {
-                break;
-            }
-            if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
-                // omit aliases that alignment makes redundant
-                if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
-                    SkASSERT(test->alias());
-                    prev->removeNext(ptT);
-                    test = prev;
-                } else {
-                    SkASSERT(ptT->alias());
-                    stopPtT = ptT = ptT->remove();
-                    break;
-                }
-            }
-        } while (true);
-    } while ((ptT = ptT->next()) != stopPtT);
-}
-
-bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
-    const SkOpPtT* start = &fPtT;
-    const SkOpPtT* check = &span->fPtT;
-    SkASSERT(start != check);
-    const SkOpPtT* walk = start;
-    while ((walk = walk->next()) != start) {
-        if (walk == check) {
-            return true;
-        }
-    }
-    return false;
-}
-
-SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
-    SkOpPtT* start = &fPtT;
-    SkOpPtT* walk = start;
-    while ((walk = walk->next()) != start) {
-        if (walk->segment() == segment) {
-            return walk;
-        }
-    }
-    return NULL;
-}
-
-bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
-    SkASSERT(this->segment() != segment);
-    const SkOpSpanBase* next = this;
-    while ((next = next->fCoinEnd) != this) {
-        if (next->segment() == segment) {
-            return true;
-        }
-    }
-    return false;
-}
-
-SkOpContour* SkOpSpanBase::contour() const {
-    return segment()->contour();
-}
-
-SkOpGlobalState* SkOpSpanBase::globalState() const {
-    return contour()->globalState(); 
-}
-
-void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
-    fSegment = segment;
-    fPtT.init(this, t, pt, false);
-    fCoinEnd = this;
-    fFromAngle = NULL;
-    fPrev = prev;
-    fAligned = true;
-    fChased = false;
-    PATH_OPS_DEBUG_CODE(fCount = 1);
-    PATH_OPS_DEBUG_CODE(fID = globalState()->nextSpanID());
-}
-
-// this pair of spans share a common t value or point; merge them and eliminate duplicates
-// this does not compute the best t or pt value; this merely moves all data into a single list
-void SkOpSpanBase::merge(SkOpSpan* span) {
-    SkOpPtT* spanPtT = span->ptT();
-    SkASSERT(this->t() != spanPtT->fT);
-    SkASSERT(!zero_or_one(spanPtT->fT));
-    span->detach(this->ptT());
-    SkOpPtT* remainder = spanPtT->next();
-    ptT()->insert(spanPtT);
-    while (remainder != spanPtT) {
-        SkOpPtT* next = remainder->next();
-        SkOpPtT* compare = spanPtT->next();
-        while (compare != spanPtT) {
-            SkOpPtT* nextC = compare->next();
-            if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
-                goto tryNextRemainder;
-            }
-            compare = nextC;
-        }
-        spanPtT->insert(remainder);
-tryNextRemainder:
-        remainder = next;
-    }
-}
-
-void SkOpSpan::applyCoincidence(SkOpSpan* opp) {
-    SkASSERT(!final());
-    SkASSERT(0);  // incomplete
-}
-
-bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
-    SkASSERT(this->segment() != segment);
-    const SkOpSpan* next = fCoincident;
-    do {
-        if (next->segment() == segment) {
-            return true;
-        }
-    } while ((next = next->fCoincident) != this);
-    return false;
-}
-
-void SkOpSpan::detach(SkOpPtT* kept) {
-    SkASSERT(!final());
-    SkOpSpan* prev = this->prev();
-    SkASSERT(prev);
-    SkOpSpanBase* next = this->next();
-    SkASSERT(next);
-    prev->setNext(next);
-    next->setPrev(prev);
-    this->segment()->detach(this);
-    this->globalState()->coincidence()->fixUp(this->ptT(), kept);
-    this->ptT()->setDeleted();
-}
-
-void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
-    SkASSERT(t != 1);
-    initBase(segment, prev, t, pt);
-    fCoincident = this;
-    fToAngle = NULL;
-    fWindSum = fOppSum = SK_MinS32;
-    fWindValue = 1;
-    fOppValue = 0;
-    fChased = fDone = false;
-    segment->bumpCount();
-}
-
-void SkOpSpan::setOppSum(int oppSum) {
-    SkASSERT(!final());
-    if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
-        this->globalState()->setWindingFailed();
-        return;
-    }
-    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
-    fOppSum = oppSum;
-}
index 9e5939a5e1e90f15a696b01af243144d1470085a..d9ce44eb7720fb4fb4cd78521b1ec750f15b9358 100644 (file)
 #ifndef SkOpSpan_DEFINED
 #define SkOpSpan_DEFINED
 
-#include "SkPathOpsDebug.h"
 #include "SkPoint.h"
 
-class SkChunkAlloc;
-struct SkOpAngle;
-class SkOpContour;
-class SkOpGlobalState;
+class SkOpAngle;
 class SkOpSegment;
-class SkOpSpanBase;
-class SkOpSpan;
 
-// subset of op span used by terminal span (when t is equal to one)
-class SkOpPtT {
-public:
-    enum {
-        kIsAlias = 1,
-        kIsDuplicate = 1
-    };
-
-    void addOpp(SkOpPtT* opp) {
-        // find the fOpp ptr to opp
-        SkOpPtT* oppPrev = opp->fNext;
-        if (oppPrev == this) {
-            return;
-        }
-        while (oppPrev->fNext != opp) {
-            oppPrev = oppPrev->fNext;
-             if (oppPrev == this) {
-                 return;
-             }
-        }
-        
-        SkOpPtT* oldNext = this->fNext;
-        SkASSERT(this != opp);
-        this->fNext = opp;
-        SkASSERT(oppPrev != oldNext);
-        oppPrev->fNext = oldNext;
-    }
-
-    bool alias() const;
-    SkOpContour* contour() const;
-
-    int debugID() const {
-        return PATH_OPS_DEBUG_RELEASE(fID, -1);
-    }
-
-    const SkOpAngle* debugAngle(int id) const;
-    SkOpContour* debugContour(int id);
-    int debugLoopLimit(bool report) const;
-    bool debugMatchID(int id) const;
-    const SkOpPtT* debugPtT(int id) const;
-    const SkOpSegment* debugSegment(int id) const;
-    const SkOpSpanBase* debugSpan(int id) const;
-    SkOpGlobalState* globalState() const;
-    void debugValidate() const;
-
-    bool deleted() const {
-        return fDeleted;
-    }
-
-    bool duplicate() const {
-        return fDuplicatePt;
-    }
-
-    void dump() const;  // available to testing only
-    void dumpAll() const;
-    void dumpBase() const;
-
-    void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
-
-    void insert(SkOpPtT* span) {
-        SkASSERT(span != this);
-        span->fNext = fNext;
-        fNext = span;
-    }
-
-    const SkOpPtT* next() const {
-        return fNext;
-    }
-
-    SkOpPtT* next() {
-        return fNext;
-    }
-
-    bool onEnd() const;
-    SkOpPtT* prev();
-    SkOpPtT* remove();
-    void removeNext(SkOpPtT* kept);
-
-    const SkOpSegment* segment() const;
-    SkOpSegment* segment();
-
-    void setDeleted() {
-        SkASSERT(!fDeleted);
-        fDeleted = true;
-    }
-
-    const SkOpSpanBase* span() const {
-        return fSpan;
-    }
-
-    SkOpSpanBase* span() {
-        return fSpan;
-    }
-
-    double fT; 
-    SkPoint fPt;   // cache of point value at this t
-protected:
-    SkOpSpanBase* fSpan;  // contains winding data
-    SkOpPtT* fNext;  // intersection on opposite curve or alias on this curve
-    bool fDeleted;  // set if removed from span list 
-    bool fDuplicatePt;  // set if identical pt is somewhere in the next loop
-    PATH_OPS_DEBUG_CODE(int fID);
-};
-
-class SkOpSpanBase {
-public:
-    void addSimpleAngle(bool checkFrom , SkChunkAlloc* );
-    void align();
-
-    bool aligned() const {
-        return fAligned;
-    }
-
-    void alignEnd(double t, const SkPoint& pt);
-
-    bool chased() const {
-        return fChased;
-    }
-
-    void clearCoinEnd() {
-        SkASSERT(fCoinEnd != this);
-        fCoinEnd = this;
-    }
-
-    const SkOpSpanBase* coinEnd() const {
-        return fCoinEnd;
-    }
-
-    bool contains(const SkOpSpanBase* ) const;
-    SkOpPtT* contains(const SkOpSegment* );
-
-    bool containsCoinEnd(const SkOpSpanBase* coin) const {
-        SkASSERT(this != coin);
-        const SkOpSpanBase* next = this;
-        while ((next = next->fCoinEnd) != this) {
-            if (next == coin) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool containsCoinEnd(const SkOpSegment* ) const;
-    SkOpContour* contour() const;
-
-    int debugBumpCount() {
-        return PATH_OPS_DEBUG_RELEASE(++fCount, -1);
-    }
-
-    int debugID() const {
-        return PATH_OPS_DEBUG_RELEASE(fID, -1);
-    }
-
-    const SkOpAngle* debugAngle(int id) const;
-    bool debugCoinEndLoopCheck() const;
-    SkOpContour* debugContour(int id);
-    const SkOpPtT* debugPtT(int id) const;
-    const SkOpSegment* debugSegment(int id) const;
-    const SkOpSpanBase* debugSpan(int id) const;
-    SkOpGlobalState* globalState() const;
-    void debugValidate() const;
-
-    bool deleted() const {
-        return fPtT.deleted();
-    }
-
-    void dump() const;  // available to testing only
-    void dumpCoin() const;
-    void dumpAll() const;
-    void dumpBase() const;
-
-    bool final() const {
-        return fPtT.fT == 1;
-    }
-
-    SkOpAngle* fromAngle() const {
-        return fFromAngle;
-    }
-
-    void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
-
-    void insertCoinEnd(SkOpSpanBase* coin) {
-        if (containsCoinEnd(coin)) {
-            SkASSERT(coin->containsCoinEnd(this));
-            return;
-        }
-        debugValidate();
-        SkASSERT(this != coin);
-        SkOpSpanBase* coinNext = coin->fCoinEnd;
-        coin->fCoinEnd = this->fCoinEnd;
-        this->fCoinEnd = coinNext;
-        debugValidate();
-    }
-
-    void merge(SkOpSpan* span);
-
-    SkOpSpan* prev() const {
-        return fPrev;
-    }
-
-    const SkPoint& pt() const {
-        return fPtT.fPt;
-    }
-
-    const SkOpPtT* ptT() const {
-        return &fPtT;
-    }
-
-    SkOpPtT* ptT() {
-        return &fPtT;
-    }
-
-    SkOpSegment* segment() const {
-        return fSegment;
-    }
-
-    void setChased(bool chased) {
-        fChased = chased;
-    }
-
-    SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
-
-    void setFromAngle(SkOpAngle* angle) {
-        fFromAngle = angle;
-    }
-
-    void setPrev(SkOpSpan* prev) {
-        fPrev = prev;
-    }
-
-    bool simple() const {
-        fPtT.debugValidate();
-        return fPtT.next()->next() == &fPtT; 
-    }
-
-    const SkOpSpan* starter(const SkOpSpanBase* end) const {
-        const SkOpSpanBase* result = t() < end->t() ? this : end;
-        return result->upCast();
-    }
-
-    SkOpSpan* starter(SkOpSpanBase* end) {
-        SkASSERT(this->segment() == end->segment());
-        SkOpSpanBase* result = t() < end->t() ? this : end;
-        return result->upCast();
-    }
-
-    SkOpSpan* starter(SkOpSpanBase** endPtr) {
-        SkOpSpanBase* end = *endPtr;
-        SkASSERT(this->segment() == end->segment());
-        SkOpSpanBase* result;
-        if (t() < end->t()) {
-            result = this;
-        } else {
-            result = end;
-            *endPtr = this;
-        }
-        return result->upCast();
-    }
-
-    int step(const SkOpSpanBase* end) const {
-        return t() < end->t() ? 1 : -1;
-    }
-
-    double t() const {
-        return fPtT.fT;
-    }
-
-    void unaligned() {
-        fAligned = false;
-    }
-
-    SkOpSpan* upCast() {
-        SkASSERT(!final());
-        return (SkOpSpan*) this;
-    }
-
-    const SkOpSpan* upCast() const {
-        SkASSERT(!final());
-        return (const SkOpSpan*) this;
-    }
-
-    SkOpSpan* upCastable() {
-        return final() ? NULL : upCast();
-    }
-
-    const SkOpSpan* upCastable() const {
-        return final() ? NULL : upCast();
-    }
-
-private:
-    void alignInner();
-
-protected:  // no direct access to internals to avoid treating a span base as a span
-    SkOpPtT fPtT;  // list of points and t values associated with the start of this span
-    SkOpSegment* fSegment;  // segment that contains this span
-    SkOpSpanBase* fCoinEnd;  // linked list of coincident spans that end here (may point to itself)
-    SkOpAngle* fFromAngle;  // points to next angle from span start to end
-    SkOpSpan* fPrev;  // previous intersection point
-    bool fAligned;
-    bool fChased;  // set after span has been added to chase array
-    PATH_OPS_DEBUG_CODE(int fCount);  // number of pt/t pairs added
-    PATH_OPS_DEBUG_CODE(int fID);
-};
-
-class SkOpSpan : public SkOpSpanBase {
-public:
-    void applyCoincidence(SkOpSpan* opp);
-
-    bool clearCoincident() {
-        SkASSERT(!final());
-        if (fCoincident == this) {
-            return false;
-        }
-        fCoincident = this;
-        return true;
-    }
-
-    bool containsCoincidence(const SkOpSegment* ) const;
-
-    bool containsCoincidence(const SkOpSpan* coin) const {
-        SkASSERT(this != coin);
-        const SkOpSpan* next = this;
-        while ((next = next->fCoincident) != this) {
-            if (next == coin) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool debugCoinLoopCheck() const;
-    void detach(SkOpPtT* );
-
-    bool done() const {
-        SkASSERT(!final());
-        return fDone;
-    }
-
-    void dumpCoin() const;
-    bool dumpSpan() const;
-    void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
-
-    void insertCoincidence(SkOpSpan* coin) {
-        if (containsCoincidence(coin)) {
-            SkASSERT(coin->containsCoincidence(this));
-            return;
-        }
-        debugValidate();
-        SkASSERT(this != coin);
-        SkOpSpan* coinNext = coin->fCoincident;
-        coin->fCoincident = this->fCoincident;
-        this->fCoincident = coinNext;
-        debugValidate();
-    }
-
-    bool isCanceled() const {
-        SkASSERT(!final());
-        return fWindValue == 0 && fOppValue == 0;
-    }
-
-    bool isCoincident() const {
-        SkASSERT(!final());
-        return fCoincident != this;
-    }
-
-    SkOpSpanBase* next() const {
-        SkASSERT(!final());
-        return fNext;
-    }
-
-    int oppSum() const {
-        SkASSERT(!final());
-        return fOppSum;
-    }
-
-    int oppValue() const {
-        SkASSERT(!final());
-        return fOppValue;
-    }
-
-    SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
-
-    void setDone(bool done) {
-        SkASSERT(!final());
-        fDone = done;
-    }
-
-    void setNext(SkOpSpanBase* nextT) {
-        SkASSERT(!final());
-        fNext = nextT;
-    }
-
-    void setOppSum(int oppSum);
-
-    void setOppValue(int oppValue) {
-        SkASSERT(!final());
-        SkASSERT(fOppSum == SK_MinS32);
-        fOppValue = oppValue;
-    }
-
-    void setToAngle(SkOpAngle* angle) {
-        SkASSERT(!final());
-        fToAngle = angle;
-    }
-
-    void setWindSum(int windSum) {
-        SkASSERT(!final());
-        SkASSERT(fWindSum == SK_MinS32 || fWindSum == windSum);
-        SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(windSum) <= DEBUG_LIMIT_WIND_SUM);
-        fWindSum = windSum;
-    }
-
-    void setWindValue(int windValue) {
-        SkASSERT(!final());
-        SkASSERT(windValue >= 0);
-        SkASSERT(fWindSum == SK_MinS32);
-        fWindValue = windValue;
-    }
-
-    SkOpAngle* toAngle() const {
-        SkASSERT(!final());
-        return fToAngle;
-    }
-
-    int windSum() const {
-        SkASSERT(!final());
-        return fWindSum;
-    }
-
-    int windValue() const {
-        SkASSERT(!final());
-        return fWindValue;
-    }
-
-private:  // no direct access to internals to avoid treating a span base as a span
-    SkOpSpan* fCoincident;  // linked list of spans coincident with this one (may point to itself)
-    SkOpAngle* fToAngle;  // points to next angle from span start to end
-    SkOpSpanBase* fNext;  // next intersection point
+struct SkOpSpan {
+    SkPoint fPt;  // computed when the curves are intersected
+    double fT;
+    double fOtherT;  // value at fOther[fOtherIndex].fT
+    SkOpSegment* fOther;
+    SkOpAngle* fFromAngle;  // (if t > 0) index into segment's angle array going negative in t
+    SkOpAngle* fToAngle;  // (if t < 1) index into segment's angle array going positive in t
+    int fOtherIndex;  // can't be used during intersection
     int fWindSum;  // accumulated from contours surrounding this one.
     int fOppSum;  // for binary operators: the opposite winding sum
     int fWindValue;  // 0 == canceled; 1 == normal; >1 == coincident
     int fOppValue;  // normally 0 -- when binary coincident edges combine, opp value goes here
+    bool fChased;  // set after span has been added to chase array
+    bool fCoincident;  // set if span is bumped -- if set additional points aren't inserted
     bool fDone;  // if set, this span to next higher T has been processed
+    bool fLoop;  // set when a cubic loops back to this point
+    bool fMultiple;  // set if this is one of mutiple spans with identical t and pt values
+    bool fNear;  // set if opposite end point is near but not equal to this one
+    bool fSmall;   // if set, consecutive points are almost equal
+    bool fTiny;  // if set, consecutive points are equal but consecutive ts are not precisely equal
+
+    // available to testing only
+    const SkOpSegment* debugToSegment(ptrdiff_t* ) const;
+    void dump() const;
+    void dumpOne() const;
 };
 
 #endif
index e8835f02e6cb45a3bc7b75ec38d64eb04e0e3bdb..c80c12f63b09f9e39219a2495d75e369ae0c21e1 100644 (file)
@@ -19,12 +19,6 @@ public:
         return record;
     }
 
-    static T* AllocateArray(SkChunkAlloc* allocator, int count) {
-        void* ptr = allocator->allocThrow(sizeof(T) * count);
-        T* record = (T*) ptr;
-        return record;
-    }
-
     static T* New(SkChunkAlloc* allocator) {
         return new (Allocate(allocator)) T();
     }
index b0fd822a9d359d08ca8f378b71e8551a466f011c..1a5bfc18896d11375766b5971aff9065483f26d5 100644 (file)
@@ -5,25 +5,47 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 #include "SkTSort.h"
 
-static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
-        SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
-        double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) {
-    SkOpSpanBase* start = *startPtr;
-    SkOpSpanBase* end = *endPtr;
+static void alignMultiples(SkTArray<SkOpContour*, true>* contourList,
+        SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        if (contour->hasMultiples()) {
+            contour->alignMultiples(aligned);
+        }
+    }
+}
+
+static void alignCoincidence(SkTArray<SkOpContour*, true>* contourList,
+        const SkTDArray<SkOpSegment::AlignedSpan>& aligned) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        int count = aligned.count();
+        for (int index = 0; index < count; ++index) {
+            contour->alignCoincidence(aligned[index]);
+        }
+    }    
+}
+
+static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
+                              int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
+                              bool* tryAgain, double* midPtr, bool opp) {
+    const int index = *indexPtr;
+    const int endIndex = *endIndexPtr;
     const double mid = *midPtr;
     const SkOpSegment* current = *currentPtr;
-    double tAtMid = SkOpSegment::TAtMid(start, end, mid);
+    double tAtMid = current->tAtMid(index, endIndex, mid);
     SkPoint basePt = current->ptAtT(tAtMid);
     int contourCount = contourList.count();
     SkScalar bestY = SK_ScalarMin;
     SkOpSegment* bestSeg = NULL;
-    SkOpSpan* bestTSpan = NULL;
+    int bestTIndex = 0;
     bool bestOpp;
     bool hitSomething = false;
     for (int cTest = 0; cTest < contourCount; ++cTest) {
@@ -35,38 +57,37 @@ static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
         if (bestY > contour->bounds().fBottom) {
             continue;
         }
-        SkOpSegment* testSeg = contour->first();
-        SkASSERT(testSeg);
-        do {
+        int segmentCount = contour->segments().count();
+        for (int test = 0; test < segmentCount; ++test) {
+            SkOpSegment* testSeg = &contour->segments()[test];
             SkScalar testY = bestY;
             double testHit;
-            bool vertical;
-            SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
-                    testSeg == current, &testY, &testHit, &hitSomething, &vertical);
-            if (!testTSpan) {
-                if (vertical) {
+            int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
+                    testOpp, testSeg == current);
+            if (testTIndex < 0) {
+                if (testTIndex == SK_MinS32) {
                     hitSomething = true;
                     bestSeg = NULL;
                     goto abortContours;  // vertical encountered, return and try different point
                 }
                 continue;
             }
-            if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
-                double baseT = start->t();
-                double endT = end->t();
+            if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
+                double baseT = current->t(index);
+                double endT = current->t(endIndex);
                 double newMid = (testHit - baseT) / (endT - baseT);
 #if DEBUG_WINDING
-                double midT = SkOpSegment::TAtMid(start, end, mid);
-                SkPoint midXY = current->ptAtT(midT);
-                double newMidT = SkOpSegment::TAtMid(start, end, newMid);
-                SkPoint newXY = current->ptAtT(newMidT);
+                double midT = current->tAtMid(index, endIndex, mid);
+                SkPoint midXY = current->xyAtT(midT);
+                double newMidT = current->tAtMid(index, endIndex, newMid);
+                SkPoint newXY = current->xyAtT(newMidT);
                 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
                         " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
                         current->debugID(), mid, newMid,
-                        baseT, start->pt().fX, start->pt().fY,
+                        baseT, current->xAtT(index), current->yAtT(index),
                         baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
                         baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
-                        endT, end->pt().fX, end->pt().fY);
+                        endT, current->xAtT(endIndex), current->yAtT(endIndex));
 #endif
                 *midPtr = newMid * 2;  // calling loop with divide by 2 before continuing
                 return SK_MinS32;
@@ -74,39 +95,38 @@ static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
             bestSeg = testSeg;
             *bestHit = testHit;
             bestOpp = testOpp;
-            bestTSpan = testTSpan;
+            bestTIndex = testTIndex;
             bestY = testY;
-        } while ((testSeg = testSeg->next()));
+        }
     }
 abortContours:
     int result;
     if (!bestSeg) {
         result = hitSomething ? SK_MinS32 : 0;
     } else {
-        if (bestTSpan->windSum() == SK_MinS32) {
+        if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
             *currentPtr = bestSeg;
-            *startPtr = bestTSpan;
-            *endPtr = bestTSpan->next();
-            SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+            *indexPtr = bestTIndex;
+            *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
+            SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
             *tryAgain = true;
             return 0;
         }
-        result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
+        result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
         SkASSERT(result == SK_MinS32 || *bestDx);
     }
-    double baseT = (*startPtr)->t();
-    double endT = (*endPtr)->t();
+    double baseT = current->t(index);
+    double endT = current->t(endIndex);
     *bestHit = baseT + mid * (endT - baseT);
     return result;
 }
 
-SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
-         SkOpSpanBase** endPtr) {
+SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
     int contourCount = contourList.count();
     SkOpSegment* result;
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         SkOpContour* contour = contourList[cIndex];
-        result = contour->undoneSegment(startPtr, endPtr);
+        result = contour->undoneSegment(start, end);
         if (result) {
             return result;
         }
@@ -114,23 +134,20 @@ SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** st
     return NULL;
 }
 
-SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr) {
+SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
     while (chase->count()) {
-        SkOpSpanBase* span;
+        SkOpSpan* span;
         chase->pop(&span);
-        SkOpSegment* segment = span->segment();
-        *startPtr = span->ptT()->next()->span();
+        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
+        SkOpSegment* segment = backPtr.fOther;
+        *tIndex = backPtr.fOtherIndex;
         bool sortable = true;
         bool done = true;
-        *endPtr = NULL;
-        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
+        *endIndex = -1;
+        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
                 &sortable)) {
-            if (last->unorderable()) {
-                continue;
-            }
-            *startPtr = last->start();
-            *endPtr = last->end();
+            *tIndex = last->start();
+            *endIndex = last->end();
     #if TRY_ROTATE
             *chase->insert(0) = span;
     #else
@@ -145,58 +162,65 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
             continue;
         }
         // find first angle, initialize winding to computed wind sum
-        const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
-        if (!angle) {
-            continue;
-        }
-        const SkOpAngle* firstAngle = angle;
-        bool loop = false;
-        int winding = SK_MinS32;
+        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
+        const SkOpAngle* firstAngle;
+        SkDEBUGCODE(firstAngle = angle);
+        SkDEBUGCODE(bool loop = false);
+        int winding;
         do {
             angle = angle->next();
-            if (angle == firstAngle && loop) {
-                break;    // if we get here, there's no winding, loop is unorderable
-            }
-            loop |= angle == firstAngle;
+            SkASSERT(angle != firstAngle || !loop);
+            SkDEBUGCODE(loop |= angle == firstAngle);
             segment = angle->segment();
             winding = segment->windSum(angle);
         } while (winding == SK_MinS32);
-        if (winding == SK_MinS32) {
-            continue;
-        }
-        int sumWinding = segment->updateWindingReverse(angle);
-        SkOpSegment* first = NULL;
+        int spanWinding = segment->spanSign(angle->start(), angle->end());
+    #if DEBUG_WINDING
+        SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding);
+    #endif
+        // turn span winding into contour winding
+        if (spanWinding * winding < 0) {
+            winding += spanWinding;
+        }
+        // we care about first sign and whether wind sum indicates this
+        // edge is inside or outside. Maybe need to pass span winding
+        // or first winding or something into this function?
+        // advance to first undone angle, then return it and winding
+        // (to set whether edges are active or not)
         firstAngle = angle;
+        winding -= firstAngle->segment()->spanSign(firstAngle);
         while ((angle = angle->next()) != firstAngle) {
             segment = angle->segment();
-            SkOpSpanBase* start = angle->start();
-            SkOpSpanBase* end = angle->end();
-            int maxWinding;
-            segment->setUpWinding(start, end, &maxWinding, &sumWinding);
-            if (!segment->done(angle)) {
-                if (!first) {
-                    first = segment;
-                    *startPtr = start;
-                    *endPtr = end;
+            int maxWinding = winding;
+            winding -= segment->spanSign(angle);
+    #if DEBUG_SORT
+            SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
+                    segment->debugID(), maxWinding, winding, angle->sign());
+    #endif
+            *tIndex = angle->start();
+            *endIndex = angle->end();
+            int lesser = SkMin32(*tIndex, *endIndex);
+            const SkOpSpan& nextSpan = segment->span(lesser);
+            if (!nextSpan.fDone) {
+            // FIXME: this be wrong? assign startWinding if edge is in
+            // same direction. If the direction is opposite, winding to
+            // assign is flipped sign or +/- 1?
+                if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
+                    maxWinding = winding;
                 }
-                // OPTIMIZATION: should this also add to the chase?
-                (void) segment->markAngle(maxWinding, sumWinding, angle);
+                // allowed to do nothing
+                (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
+                break;
             }
         }
-        if (first) {
-       #if TRY_ROTATE
-            *chase->insert(0) = span;
-       #else
-            *chase->append() = span;
-       #endif
-            return first;
-        }
+        *chase->insert(0) = span;
+        return segment;
     }
     return NULL;
 }
 
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
     int index;
     for (index = 0; index < contourList.count(); ++ index) {
         contourList[index]->debugShowActiveSpans();
@@ -204,12 +228,11 @@ void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
 }
 #endif
 
-static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
-        bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
-        bool* unsortable, bool* done, SkChunkAlloc* allocator) {
+static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
+        int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
     SkOpSegment* result;
     const SkOpSegment* lastTopStart = NULL;
-    SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
+    int lastIndex = -1, lastEndIndex = -1;
     do {
         SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
         int contourCount = contourList.count();
@@ -238,27 +261,27 @@ static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
             return NULL;
         }
         *topLeft = bestXY;
-        result = topStart->findTop(firstPass, start, end, unsortable, allocator);
+        result = topStart->findTop(index, endIndex, unsortable, firstPass);
         if (!result) {
-            if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
+            if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
                 *done = true;
                 return NULL;
             }
             lastTopStart = topStart;
-            lastStart = *start;
-            lastEnd = *end;
+            lastIndex = *index;
+            lastEndIndex = *endIndex;
         }
     } while (!result);
     return result;
 }
 
-static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
-        SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
+static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
+        SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
         SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
     double test = 0.9;
     int contourWinding;
     do {
-        contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
+        contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
                 tHit, hitDx, tryAgain, &test, opp);
         if (contourWinding != SK_MinS32 || *tryAgain) {
             return contourWinding;
@@ -273,9 +296,9 @@ static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
     return contourWinding;
 }
 
-static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
-        SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
-    if (!(*current)->isVertical(*start, *end)) {
+static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
+        SkOpSegment** current, int* index, int* endIndex) {
+    if (!(*current)->isVertical(*index, *endIndex)) {
         return;
     }
     int contourCount = contourList.count();
@@ -284,7 +307,7 @@ static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
         if (contour->done()) {
             continue;
         }
-        SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
+        SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
         if (nonVertical) {
             *current = nonVertical;
             return;
@@ -293,41 +316,41 @@ static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
     return;
 }
 
-struct SortableTop2 {  // error if local in pre-C++11
-    SkOpSpanBase* fStart;
-    SkOpSpanBase* fEnd;
+struct SortableTop {  // error if local in pre-C++11
+    SkOpSegment* fSegment;
+    int fIndex;
+    int fEndIndex;
 };
 
-SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass,
-        SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
-        SkChunkAlloc* allocator) {
-    SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft,
-            unsortable, done, allocator);
+SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
+        SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
+        int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
+        bool firstPass) {
+    SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
+            done, firstPass);
     if (!current) {
         return NULL;
     }
-    SkOpSpanBase* start = *startPtr;
-    SkOpSpanBase* end = *endPtr;
-    SkASSERT(current == start->segment());
+    const int startIndex = *indexPtr;
+    const int endIndex = *endIndexPtr;
     if (*firstContour) {
-        current->initWinding(start, end, angleIncludeType);
+        current->initWinding(startIndex, endIndex, angleIncludeType);
         *firstContour = false;
         return current;
     }
-    SkOpSpan* minSpan = start->starter(end);
-    int sumWinding = minSpan->windSum();
+    int minIndex = SkMin32(startIndex, endIndex);
+    int sumWinding = current->windSum(minIndex);
     if (sumWinding == SK_MinS32) {
-        SkOpSpanBase* iSpan = end;
-        SkOpSpanBase* oSpan = start;
-        do {
-            bool checkFrom = oSpan->t() < iSpan->t();
-            if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) {
-                iSpan->addSimpleAngle(checkFrom, allocator);
+        int index = endIndex;
+        int oIndex = startIndex;
+        do { 
+            const SkOpSpan& span = current->span(index);
+            if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
+                current->addSimpleAngle(index);
             }
-            sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
-            SkTSwap(iSpan, oSpan);
-        } while (sumWinding == SK_MinS32 && iSpan == start);
+            sumWinding = current->computeSum(oIndex, index, angleIncludeType);
+            SkTSwap(index, oIndex);
+        } while (sumWinding == SK_MinS32 && index == startIndex);
     }
     if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
         return current;
@@ -341,28 +364,26 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
     SkScalar hitDx = 0;
     SkScalar hitOppDx = 0;
     // keep track of subsequent returns to detect infinite loops
-    SkTDArray<SortableTop2> sortableTops;
+    SkTDArray<SortableTop> sortableTops;
     do {
         // if current is vertical, find another candidate which is not
         // if only remaining candidates are vertical, then they can be marked done
-        SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
-        SkASSERT(current == (*startPtr)->segment());
-        skipVertical(contourList, &current, startPtr, endPtr);
+        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+        skipVertical(contourList, &current, indexPtr, endIndexPtr);
         SkASSERT(current);  // FIXME: if null, all remaining are vertical
-        SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
-        SkASSERT(current == (*startPtr)->segment());
+        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
         tryAgain = false;
-        contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
+        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                 &hitDx, &tryAgain, onlyVertical, false);
-        SkASSERT(current == (*startPtr)->segment());
         if (tryAgain) {
             bool giveUp = false;
             int count = sortableTops.count();
             for (int index = 0; index < count; ++index) {
-                const SortableTop2& prev = sortableTops[index];
+                const SortableTop& prev = sortableTops[index];
                 if (giveUp) {
-                    prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
-                } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
+                    prev.fSegment->markDoneFinal(prev.fIndex);
+                } else if (prev.fSegment == current
+                        && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
                     // remaining edges are non-vertical and cannot have their winding computed
                     // mark them as done and return, and hope that assembly can fill the holes
                     giveUp = true;
@@ -374,13 +395,14 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
                 return NULL;
             }
         }
-        SortableTop2* sortableTop = sortableTops.append();
-        sortableTop->fStart = *startPtr;
-        sortableTop->fEnd = *endPtr;
+        SortableTop* sortableTop = sortableTops.append();
+        sortableTop->fSegment = current;
+        sortableTop->fIndex = *indexPtr;
+        sortableTop->fEndIndex = *endIndexPtr;
 #if DEBUG_SORT
         SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
-                __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
-                tHit, hitDx, tryAgain, *onlyVertical);
+                __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
+                *onlyVertical);
 #endif
         if (*onlyVertical) {
             return current;
@@ -391,35 +413,127 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
         if (angleIncludeType < SkOpAngle::kBinarySingle) {
             break;
         }
-        oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
+        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
                 &hitOppDx, &tryAgain, NULL, true);
-        SkASSERT(current == (*startPtr)->segment());
     } while (tryAgain);
-    bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
+    bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
             oppContourWinding, hitOppDx);
     if (current->done()) {
         return NULL;
     } else if (!success) {  // check if the span has a valid winding
-        SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
-            : (*endPtr)->upCast();
-        if (minSpan->windSum() == SK_MinS32) {
+        int min = SkTMin(*indexPtr, *endIndexPtr);
+        const SkOpSpan& span = current->span(min);
+        if (span.fWindSum == SK_MinS32) {
             return NULL;
         }
     }
     return current;
 }
 
-void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
-                     bool evenOdd, bool oppEvenOdd) {
-    do {
-        if (contour->count()) {
-            contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
-            *list.append() = contour;
+static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        if (!contour->calcAngles()) {
+            return false;
         }
-    } while ((contour = contour->next()));
-    if (list.count() < 2) {
+    }
+    return true;
+}
+
+static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->checkDuplicates();
+    }
+}
+
+static bool checkEnds(SkTArray<SkOpContour*, true>* contourList) {
+    // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
+    // instead, look to see if the connecting curve intersected at that same end.
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        if (!contour->checkEnds()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
+    bool hasMultiples = false;
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->checkMultiples();
+        hasMultiples |= contour->hasMultiples();
+    }
+    return hasMultiples;
+}
+
+// A small interval of a pair of curves may collapse to lines for each, triggering coincidence
+static void checkSmall(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->checkSmall();
+    }
+}
+
+// A tiny interval may indicate an undiscovered coincidence. Find and fix.
+static void checkTiny(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->checkTiny();
+    }
+}
+
+static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->fixOtherTIndex();
+    }
+}
+
+static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->joinCoincidence();
+    }
+}
+
+static void sortAngles(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->sortAngles();
+    }
+}
+
+static void sortSegments(SkTArray<SkOpContour*, true>* contourList) {
+    int contourCount = (*contourList).count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        SkOpContour* contour = (*contourList)[cTest];
+        contour->sortSegments();
+    }
+}
+
+void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
+                     bool evenOdd, bool oppEvenOdd) {
+    int count = contours.count();
+    if (count == 0) {
         return;
     }
+    for (int index = 0; index < count; ++index) {
+        SkOpContour& contour = contours[index];
+        contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
+        list.push_back(&contour);
+    }
     SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
 }
 
@@ -440,22 +554,19 @@ public:
         reassemble contour pieces into new path
     */
 void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
-    SkOpContour contour;
-    SkOpGlobalState globalState(NULL  PATH_OPS_DEBUG_PARAMS(&contour));
 #if DEBUG_PATH_CONSTRUCTION
     SkDebugf("%s\n", __FUNCTION__);
 #endif
-    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
-    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
-    builder.finish(&allocator);
-    SkTDArray<const SkOpContour* > runs;  // indices of partial contours
-    const SkOpContour* eContour = builder.head();
-    do {
-        if (!eContour->count()) {
-            continue;
-        }
-        const SkPoint& eStart = eContour->start();
-        const SkPoint& eEnd = eContour->end();
+    SkTArray<SkOpContour> contours;
+    SkOpEdgeBuilder builder(path, contours);
+    builder.finish();
+    int count = contours.count();
+    int outer;
+    SkTArray<int, true> runs(count);  // indices of partial contours
+    for (outer = 0; outer < count; ++outer) {
+        const SkOpContour& eContour = contours[outer];
+        const SkPoint& eStart = eContour.start();
+        const SkPoint& eEnd = eContour.end();
 #if DEBUG_ASSEMBLE
         SkDebugf("%s contour", __FUNCTION__);
         if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -467,42 +578,44 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
                 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
 #endif
         if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
-            eContour->toPath(simple);
+            eContour.toPath(simple);
             continue;
         }
-        *runs.append() = eContour;
-    } while ((eContour = eContour->next()));
-    int count = runs.count();
+        runs.push_back(outer);
+    }
+    count = runs.count();
     if (count == 0) {
         return;
     }
-    SkTDArray<int> sLink, eLink;
-    sLink.append(count);
-    eLink.append(count);
+    SkTArray<int, true> sLink, eLink;
+    sLink.push_back_n(count);
+    eLink.push_back_n(count);
     int rIndex, iIndex;
     for (rIndex = 0; rIndex < count; ++rIndex) {
         sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
     }
     const int ends = count * 2;  // all starts and ends
     const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
-    SkTDArray<double> distances;
-    distances.append(entries);
+    SkTArray<double, true> distances;
+    distances.push_back_n(entries);
     for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
-        const SkOpContour* oContour = runs[rIndex >> 1];
-        const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
+        outer = runs[rIndex >> 1];
+        const SkOpContour& oContour = contours[outer];
+        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
         const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
                 * ends - rIndex - 1;
         for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
-            const SkOpContour* iContour = runs[iIndex >> 1];
-            const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
+            int inner = runs[iIndex >> 1];
+            const SkOpContour& iContour = contours[inner];
+            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
             double dx = iPt.fX - oPt.fX;
             double dy = iPt.fY - oPt.fY;
             double dist = dx * dx + dy * dy;
             distances[row + iIndex] = dist;  // oStart distance from iStart
         }
     }
-    SkTDArray<int> sortedDist;
-    sortedDist.append(entries);
+    SkTArray<int, true> sortedDist;
+    sortedDist.push_back_n(entries);
     for (rIndex = 0; rIndex < entries; ++rIndex) {
         sortedDist[rIndex] = rIndex;
     }
@@ -565,16 +678,17 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
                     eIndex < 0 ? ~eIndex : eIndex);
 #endif
         do {
-            const SkOpContour* contour = runs[rIndex];
+            outer = runs[rIndex];
+            const SkOpContour& contour = contours[outer];
             if (first) {
                 first = false;
-                const SkPoint* startPtr = &contour->start();
+                const SkPoint* startPtr = &contour.start();
                 simple->deferredMove(startPtr[0]);
             }
             if (forward) {
-                contour->toPartialForward(simple);
+                contour.toPartialForward(simple);
             } else {
-                contour->toPartialBackward(simple);
+                contour.toPartialBackward(simple);
             }
 #if DEBUG_ASSEMBLE
             SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
@@ -628,88 +742,36 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
 #endif
 }
 
-static void align(SkTDArray<SkOpContour* >* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->align();
-    }
-}
-
-static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->calcAngles(allocator);
-    }
-}
-
-static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
-        SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->missingCoincidence(coincidence, allocator);
-    }
-}
-
-static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        if (!contour->moveNearby()) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->sortAngles();
-    }
-}
-
-static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->sortSegments();
-    }
-}
-
-bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
-        SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
-    // move t values and points together to eliminate small/tiny gaps
-    if (!moveNearby(contourList)) {
+bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
+#if DEBUG_SHOW_WINDING
+    SkOpContour::debugShowWindingValues(contourList);
+#endif
+    if (!CoincidenceCheck(contourList, total)) {
         return false;
     }
-    align(contourList);  // give all span members common values
-#if DEBUG_VALIDATE
-    globalState->setPhase(SkOpGlobalState::kIntersecting);
-#endif
-    coincidence->addMissing(allocator);
-#if DEBUG_VALIDATE
-    globalState->setPhase(SkOpGlobalState::kWalking);
+#if DEBUG_SHOW_WINDING
+    SkOpContour::debugShowWindingValues(contourList);
 #endif
-    coincidence->expand();  // check to see if, loosely, coincident ranges may be expanded
-    coincidence->mark();  // mark spans of coincident segments as coincident
-    missingCoincidence(contourList, coincidence, allocator);  // look for coincidence missed earlier
-    if (!coincidence->apply()) {  // adjust the winding value to account for coincident edges
+    fixOtherTIndex(contourList);
+    if (!checkEnds(contourList)) {  // check if connecting curve intersected at the same end
         return false;
     }
+    bool hasM = checkMultiples(contourList);  // check if intersections agree on t and point values
+    SkTDArray<SkOpSegment::AlignedSpan> aligned;
+    if (hasM) {
+        alignMultiples(contourList, &aligned);  // align pairs of identical points
+        alignCoincidence(contourList, aligned);
+    }
+    checkDuplicates(contourList);  // check if spans have the same number on the other end
+    checkTiny(contourList);  // if pair have the same end points, mark them as parallel
+    checkSmall(contourList);  // a pair of curves with a small span may turn into coincident lines
+    joinCoincidence(contourList);  // join curves that connect to a coincident pair
     sortSegments(contourList);
-    calcAngles(contourList, allocator);
-    sortAngles(contourList);
-    if (globalState->angleCoincidence()) {
-        missingCoincidence(contourList, coincidence, allocator);
-        if (!coincidence->apply()) {
-            return false;
-        }
+    if (!calcAngles(contourList)) {
+        return false;
     }
-#if DEBUG_ACTIVE_SPANS
+    sortAngles(contourList);
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
     DebugShowActiveSpans(*contourList);
 #endif
     return true;
index 1bf17919adba14e64c6d4c37e41d14d4f9bdc75b..0d8cfc42f9fd934979887a5a254bc9e8367c5007 100644 (file)
@@ -8,28 +8,24 @@
 #define SkPathOpsCommon_DEFINED
 
 #include "SkOpAngle.h"
+#include "SkOpContour.h"
 #include "SkTDArray.h"
 
-class SkOpCoincidence;
-class SkOpContour;
 class SkPathWriter;
 
 void Assemble(const SkPathWriter& path, SkPathWriter* simple);
-SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
-                       SkOpSpanBase** endPtr);
-SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour*>& , bool firstPass,
-                              SkOpAngle::IncludeType , bool* firstContour, SkOpSpanBase** index,
-                              SkOpSpanBase** endIndex, SkPoint* topLeft, bool* unsortable,
-                              bool* done, bool* onlyVertical, SkChunkAlloc* );
-SkOpSegment* FindUndone(SkTDArray<SkOpContour*>& contourList, SkOpSpanBase** startPtr,
-                         SkOpSpanBase** endPtr);
-void MakeContourList(SkOpContour* , SkTDArray<SkOpContour*>& list,
+// FIXME: find chase uses insert, so it can't be converted to SkTArray yet
+SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex);
+SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& , SkOpAngle::IncludeType ,
+                             bool* firstContour, int* index, int* endIndex, SkPoint* topLeft,
+                             bool* unsortable, bool* done, bool* onlyVertical, bool firstPass);
+SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end);
+void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
                      bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkTDArray<SkOpContour*>* , SkOpCoincidence* , SkChunkAlloc* ,
-                       SkOpGlobalState* );
+bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
 
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList);
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
 #endif
 
 #endif
index d4a5898a1d16a60e521c14baf66b702cd02faa0f..9d70d58ec15d7a5219637e2eff3f7b869b9f2b59 100644 (file)
@@ -4,7 +4,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkGeometry.h"
 #include "SkLineParameters.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
@@ -27,8 +26,8 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
         double priorT = t - step;
         SkASSERT(priorT >= min);
         SkDPoint lessPt = ptAtT(priorT);
-        if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
-                && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
+        if (approximately_equal(lessPt.fX, cubicAtT.fX)
+                && approximately_equal(lessPt.fY, cubicAtT.fY)) {
             return -1;  // binary search found no point at this axis intercept
         }
         double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@@ -42,12 +41,10 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
             t = priorT;
         } else {
             double nextT = t + lastStep;
-            if (nextT > max) {
-                return -1;
-            }
+            SkASSERT(nextT <= max);
             SkDPoint morePt = ptAtT(nextT);
-            if (approximately_equal_half(morePt.fX, cubicAtT.fX)
-                    && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
+            if (approximately_equal(morePt.fX, cubicAtT.fX)
+                    && approximately_equal(morePt.fY, cubicAtT.fY)) {
                 return -1;  // binary search found no point at this axis intercept
             }
             double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@@ -91,6 +88,35 @@ void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
     *C -= 3 * *D;           // C = -3*a + 3*b
 }
 
+bool SkDCubic::controlsContainedByEnds() const {
+    SkDVector startTan = fPts[1] - fPts[0];
+    if (startTan.fX == 0 && startTan.fY == 0) {
+        startTan = fPts[2] - fPts[0];
+    }
+    SkDVector endTan = fPts[2] - fPts[3];
+    if (endTan.fX == 0 && endTan.fY == 0) {
+        endTan = fPts[1] - fPts[3];
+    }
+    if (startTan.dot(endTan) >= 0) {
+        return false;
+    }
+    SkDLine startEdge = {{fPts[0], fPts[0]}};
+    startEdge[1].fX -= startTan.fY;
+    startEdge[1].fY += startTan.fX;
+    SkDLine endEdge = {{fPts[3], fPts[3]}};
+    endEdge[1].fX -= endTan.fY;
+    endEdge[1].fY += endTan.fX;
+    double leftStart1 = startEdge.isLeft(fPts[1]);
+    if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
+        return false;
+    }
+    double leftEnd1 = endEdge.isLeft(fPts[1]);
+    if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
+        return false;
+    }
+    return leftStart1 * leftEnd1 >= 0;
+}
+
 bool SkDCubic::endsAreExtremaInXOrY() const {
     return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
             && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
@@ -98,120 +124,17 @@ bool SkDCubic::endsAreExtremaInXOrY() const {
             && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
 }
 
-// Do a quick reject by rotating all points relative to a line formed by
-// a pair of one cubic's points. If the 2nd cubic's points
-// are on the line or on the opposite side from the 1st cubic's 'odd man', the
-// curves at most intersect at the endpoints.
-/* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
-   if returning false, check contains true if the the cubic pair have only the end point in common
-*/
-bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
-    bool linear = true;
-    char hullOrder[4];
-    int hullCount = convexHull(hullOrder);
-    int end1 = hullOrder[0];
-    int hullIndex = 0;
-    const SkDPoint* endPt[2];
-    endPt[0] = &fPts[end1];
-    do {
-        hullIndex = (hullIndex + 1) % hullCount;
-        int end2 = hullOrder[hullIndex];
-        endPt[1] = &fPts[end2];
-        double origX = endPt[0]->fX;
-        double origY = endPt[0]->fY;
-        double adj = endPt[1]->fX - origX;
-        double opp = endPt[1]->fY - origY;
-        int oddManMask = other_two(end1, end2);
-        int oddMan = end1 ^ oddManMask;
-        double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
-        int oddMan2 = end2 ^ oddManMask;
-        double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
-        if (sign * sign2 < 0) {
-            continue;
-        }
-        if (approximately_zero(sign)) {
-            sign = sign2;
-            if (approximately_zero(sign)) {
-                continue;
-            }
-        }
-        linear = false;
-        bool foundOutlier = false;
-        for (int n = 0; n < kPointCount; ++n) {
-            double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
-            if (test * sign > 0 && !precisely_zero(test)) {
-                foundOutlier = true;
-                break;
-            }
-        }
-        if (!foundOutlier) {
-            return false;
-        }
-        endPt[0] = endPt[1];
-        end1 = end2;
-    } while (hullIndex);
-    *isLinear = linear;
-    return true;
-}
-
 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
     SkLineParameters lineParameters;
     lineParameters.cubicEndPoints(*this, startIndex, endIndex);
     // FIXME: maybe it's possible to avoid this and compare non-normalized
     lineParameters.normalize();
-    double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
-            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
-    double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
-            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
-    largest = SkTMax(largest, -tiniest);
     double distance = lineParameters.controlPtDistance(*this, 1);
-    if (!approximately_zero_when_compared_to(distance, largest)) {
+    if (!approximately_zero(distance)) {
         return false;
     }
     distance = lineParameters.controlPtDistance(*this, 2);
-    return approximately_zero_when_compared_to(distance, largest);
-}
-
-bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
-    SkScalar d[3];
-    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
-    if (cubicType == kLoop_SkCubicType) {
-        // crib code from gpu path utils that finds t values where loop self-intersects
-        // use it to find mid of t values which should be a friendly place to chop
-        SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
-        SkScalar ls = d[1] - tempSqrt;
-        SkScalar lt = 2.f * d[0];
-        SkScalar ms = d[1] + tempSqrt;
-        SkScalar mt = 2.f * d[0];
-        if (between(0, ls, lt) || between(0, ms, mt)) {
-            ls = ls / lt;
-            ms = ms / mt;
-            SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
-            SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
-            *t = (smaller + larger) / 2;
-            return *t > 0 && *t < 1;
-        }
-    } else if (cubicType == kSerpentine_SkCubicType) {
-        SkDCubic cubic;
-        cubic.set(pointsPtr);
-        double inflectionTs[2];
-        int infTCount = cubic.findInflections(inflectionTs);
-        if (infTCount == 2) {
-            double maxCurvature[3];
-            int roots = cubic.findMaxCurvature(maxCurvature);
-            for (int index = 0; index < roots; ++index) {
-                if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
-                    *t = maxCurvature[index];
-                    return true;
-                }
-            }
-        } else if (infTCount == 1) {
-            *t = inflectionTs[0];
-            return *t > 0 && *t < 1;
-        }
-        return false;
-    }
-    return false;
+    return approximately_zero(distance);
 }
 
 bool SkDCubic::monotonicInY() const {
@@ -219,13 +142,6 @@ bool SkDCubic::monotonicInY() const {
             && between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
 }
 
-void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
-    int offset = (int) !SkToBool(index);
-    o1Pts[0] = &fPts[offset];
-    o1Pts[1] = &fPts[++offset];
-    o1Pts[2] = &fPts[++offset];
-}
-
 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
         SearchAxis xAxis, double* validRoots) const {
     extrema += findInflections(&extremeTs[extrema]);
@@ -247,6 +163,26 @@ int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
     return validCount;
 }
 
+bool SkDCubic::serpentine() const {
+#if 0  // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicOp53d
+    double tValues[2];
+    // OPTIMIZATION : another case where caching the present of cubic inflections would be useful
+    return findInflections(tValues) > 1;
+#endif
+    if (!controlsContainedByEnds()) {
+        return false;
+    }
+    double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY);
+    for (int idx = 0; idx < 2; ++idx) {
+        wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
+    }
+    double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY);
+    for (int idx = 1; idx < 3; ++idx) {
+        waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
+    }
+    return wiggle * waggle < 0;
+}
+
 // cubic roots
 
 static const double PI = 3.141592653589793;
@@ -569,10 +505,25 @@ void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
                          double t1, double t2, SkDPoint dst[2]) const {
     SkASSERT(t1 != t2);
+#if 0
+    double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3);
+    double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3);
+    double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3);
+    double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3);
+    double mx = ex * 27 - a.fX * 8 - d.fX;
+    double my = ey * 27 - a.fY * 8 - d.fY;
+    double nx = fx * 27 - a.fX - d.fX * 8;
+    double ny = fy * 27 - a.fY - d.fY * 8;
+    /* bx = */ dst[0].fX = (mx * 2 - nx) / 18;
+    /* by = */ dst[0].fY = (my * 2 - ny) / 18;
+    /* cx = */ dst[1].fX = (nx * 2 - mx) / 18;
+    /* cy = */ dst[1].fY = (ny * 2 - my) / 18;
+#else
     // this approach assumes that the control points computed directly are accurate enough
     SkDCubic sub = subDivide(t1, t2);
     dst[0] = sub[1] + (a - sub[0]);
     dst[1] = sub[2] + (d - sub[3]);
+#endif
     if (t1 == 0 || t2 == 0) {
         align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
     }
index 9932e1d1bc300917783e4aa9416dad6f938dbf54..1037cae4f759f94e5c01a3bd3784a44c16ceb3d5 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "SkPath.h"
 #include "SkPathOpsPoint.h"
+#include "SkTArray.h"
 
 struct SkDCubicPair {
     const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@@ -18,33 +19,13 @@ struct SkDCubicPair {
 };
 
 struct SkDCubic {
-    static const int kPointCount = 4;
-    static const int kPointLast = kPointCount - 1;
-    static const int kMaxIntersections = 9;
-
     enum SearchAxis {
         kXAxis,
         kYAxis
     };
 
-    bool collapsed() const {
-        return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2])
-                && fPts[0].approximatelyEqual(fPts[3]);
-    }
-
-    bool controlsInside() const {
-        SkDVector v01 = fPts[0] - fPts[1];
-        SkDVector v02 = fPts[0] - fPts[2];
-        SkDVector v03 = fPts[0] - fPts[3];
-        SkDVector v13 = fPts[1] - fPts[3];
-        SkDVector v23 = fPts[2] - fPts[3];
-        return v03.dot(v01) > 0 && v03.dot(v02) > 0 && v03.dot(v13) > 0 && v03.dot(v23) > 0;
-    }
-
-    static bool IsCubic() { return true; }
-
-    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
-    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
+    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
 
     void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
     double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
@@ -52,35 +33,30 @@ struct SkDCubic {
     SkDCubicPair chopAt(double t) const;
     bool clockwise() const;
     static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
-    static bool ComplexBreak(const SkPoint pts[4], SkScalar* t);
-    int convexHull(char order[kPointCount]) const;
-    void dump() const;  // callable from the debugger when the implementation code is linked in
-    void dumpID(int id) const;
-    void dumpInner() const;
+    bool controlsContainedByEnds() const;
     SkDVector dxdyAtT(double t) const;
     bool endsAreExtremaInXOrY() const;
     static int FindExtrema(double a, double b, double c, double d, double tValue[2]);
     int findInflections(double tValues[2]) const;
 
-    static int FindInflections(const SkPoint a[kPointCount], double tValues[2]) {
+    static int FindInflections(const SkPoint a[4], double tValues[2]) {
         SkDCubic cubic;
         cubic.set(a);
         return cubic.findInflections(tValues);
     }
 
     int findMaxCurvature(double tValues[]) const;
-    bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
     bool isLinear(int startIndex, int endIndex) const;
     bool monotonicInY() const;
-    void otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const;
     SkDPoint ptAtT(double t) const;
     static int RootsReal(double A, double B, double C, double D, double t[3]);
     static int RootsValidT(const double A, const double B, const double C, double D, double s[3]);
 
     int searchRoots(double extremes[6], int extrema, double axisIntercept,
                     SearchAxis xAxis, double* validRoots) const;
+    bool serpentine() const;
 
-    void set(const SkPoint pts[kPointCount]) {
+    void set(const SkPoint pts[4]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
@@ -89,7 +65,7 @@ struct SkDCubic {
 
     SkDCubic subDivide(double t1, double t2) const;
 
-    static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
+    static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
         SkDCubic cubic;
         cubic.set(a);
         return cubic.subDivide(t1, t2);
@@ -97,7 +73,7 @@ struct SkDCubic {
 
     void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
 
-    static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
+    static void SubDivide(const SkPoint pts[4], const SkDPoint& a, const SkDPoint& d, double t1,
                           double t2, SkDPoint p[2]) {
         SkDCubic cubic;
         cubic.set(pts);
@@ -105,29 +81,16 @@ struct SkDCubic {
     }
 
     SkDPoint top(double startT, double endT) const;
+    void toQuadraticTs(double precision, SkTArray<double, true>* ts) const;
     SkDQuad toQuad() const;
 
+    // utilities callable by the user from the debugger when the implementation code is linked in
+    void dump() const;
+    void dumpNumber() const;
+
     static const int gPrecisionUnit;
 
-    SkDPoint fPts[kPointCount];
+    SkDPoint fPts[4];
 };
 
-/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
-   that computes the other two. Note that:
-
-   one ^ two == 3 for (0, 3), (1, 2)
-   one ^ two <  3 for (0, 1), (0, 2), (1, 3), (2, 3)
-   3 - (one ^ two) is either 0, 1, or 2
-   1 >> (3 - (one ^ two)) is either 0 or 1
-thus:
-   returned == 2 for (0, 3), (1, 2)
-   returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
-given that:
-   (0, 3) ^ 2 -> (2, 1)  (1, 2) ^ 2 -> (3, 0)
-   (0, 1) ^ 3 -> (3, 2)  (0, 2) ^ 3 -> (3, 1)  (1, 3) ^ 3 -> (2, 0)  (2, 3) ^ 3 -> (1, 0)
-*/
-inline int other_two(int one, int two) {
-    return 1 >> (3 - (one ^ two)) ^ 3;
-}
-
 #endif
diff --git a/src/pathops/SkPathOpsCubicSect.h b/src/pathops/SkPathOpsCubicSect.h
new file mode 100644 (file)
index 0000000..d763444
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkCubicSpan_DEFINE
+#define SkCubicSpan_DEFINE
+
+#include "SkChunkAlloc.h"
+#include "SkPathOpsRect.h"
+#include "SkPathOpsCubic.h"
+#include "SkTArray.h"
+
+class SkIntersections;
+
+class SkCubicCoincident {
+public:
+    bool isCoincident() const {
+        return fCoincident;
+    }
+
+    void init() {
+        fCoincident = false;
+        SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
+        SkDEBUGCODE(fPerpT = SK_ScalarNaN);
+    }
+
+    void markCoincident() {
+        if (!fCoincident) {
+            fPerpT = -1;
+        }
+        fCoincident = true;
+    }
+
+    const SkDPoint& perpPt() const {
+        return fPerpPt;
+    }
+
+    double perpT() const {
+        return fPerpT;
+    }
+
+    void setPerp(const SkDCubic& cubic1, double t, const SkDPoint& qPt, const SkDCubic& cubic2);
+
+private:
+    SkDPoint fPerpPt;
+    double fPerpT;  // perpendicular intersection on opposite Cubic
+    bool fCoincident;
+};
+
+class SkCubicSect;  // used only by debug id
+
+class SkCubicSpan {
+public:
+    void init(const SkDCubic& Cubic);
+    void initBounds(const SkDCubic& Cubic);
+
+    bool contains(double t) const {
+        return !! const_cast<SkCubicSpan*>(this)->innerFind(t);
+    }
+
+    bool contains(const SkCubicSpan* span) const;
+
+    SkCubicSpan* find(double t) {
+        SkCubicSpan* result = innerFind(t);
+        SkASSERT(result);
+        return result;
+    }
+
+    bool intersects(const SkCubicSpan* span) const;
+
+    const SkCubicSpan* next() const {
+        return fNext;
+    }
+
+    void reset() {
+        fBounded.reset();
+    }
+
+    bool split(SkCubicSpan* work) {
+        return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+    }
+
+    bool splitAt(SkCubicSpan* work, double t);
+    bool tightBoundsIntersects(const SkCubicSpan* span) const;
+
+    // implementation is for testing only
+    void dump() const;
+
+private:
+    bool hullIntersects(const SkDCubic& ) const;
+    SkCubicSpan* innerFind(double t);
+    bool linearIntersects(const SkDCubic& ) const;
+
+    // implementation is for testing only
+#if DEBUG_BINARY_CUBIC
+    int debugID(const SkCubicSect* ) const { return fDebugID; }
+#else
+    int debugID(const SkCubicSect* ) const;
+#endif
+    void dump(const SkCubicSect* ) const;
+    void dumpID(const SkCubicSect* ) const;
+
+#if DEBUG_BINARY_CUBIC
+    void validate() const;
+#endif
+
+    SkDCubic fPart;
+    SkCubicCoincident fCoinStart;
+    SkCubicCoincident fCoinEnd;
+    SkSTArray<4, SkCubicSpan*, true> fBounded;
+    SkCubicSpan* fPrev;
+    SkCubicSpan* fNext;
+    SkDRect fBounds;
+    double fStartT;
+    double fEndT;
+    double fBoundsMax;
+    bool fCollapsed;
+    bool fHasPerp;
+    mutable bool fIsLinear;
+#if DEBUG_BINARY_CUBIC
+    int fDebugID;
+    bool fDebugDeleted;
+#endif
+    friend class SkCubicSect;
+};
+
+class SkCubicSect {
+public:
+    SkCubicSect(const SkDCubic& Cubic PATH_OPS_DEBUG_PARAMS(int id));
+    static void BinarySearch(SkCubicSect* sect1, SkCubicSect* sect2, SkIntersections* intersections);
+
+    // for testing only
+    void dumpCubics() const;
+private:
+    SkCubicSpan* addOne();
+    bool binarySearchCoin(const SkCubicSect& , double tStart, double tStep, double* t,
+            double* oppT);
+    SkCubicSpan* boundsMax() const;
+    void coincidentCheck(SkCubicSect* sect2);
+    bool intersects(const SkCubicSpan* span, const SkCubicSect* opp, const SkCubicSpan* oppSpan) const;
+    void onCurveCheck(SkCubicSect* sect2, SkCubicSpan* first, SkCubicSpan* last);
+    void recoverCollapsed();
+    void removeSpan(SkCubicSpan* span);
+    void removeOne(const SkCubicSpan* test, SkCubicSpan* span);
+    void removeSpans(SkCubicSpan* span, SkCubicSect* opp);
+    void setPerp(const SkDCubic& opp, SkCubicSpan* first, SkCubicSpan* last);
+    void trim(SkCubicSpan* span, SkCubicSect* opp);
+
+    // for testing only
+    void dump() const;
+    void dumpBoth(const SkCubicSect& opp) const;
+    void dumpBoth(const SkCubicSect* opp) const;
+
+#if DEBUG_BINARY_CUBIC
+    int debugID() const { return fDebugID; }
+    void validate() const;
+#else
+    int debugID() const { return 0; }
+#endif
+    const SkDCubic& fCubic;
+    SkChunkAlloc fHeap;
+    SkCubicSpan* fHead;
+    SkCubicSpan* fDeleted;
+    int fActiveCount;
+#if DEBUG_BINARY_CUBIC
+    int fDebugID;
+    int fDebugCount;
+    int fDebugAllocatedCount;
+#endif
+    friend class SkCubicSpan;  // only used by debug id
+};
+
+#endif
index 0331f34e8a74d91ffb34f8b6d0b0d7f88f7cab25..7db93f5e969b5ba53689be47c5f9936f76601e20 100644 (file)
@@ -7,13 +7,6 @@
 
 #include "SkPathOpsDebug.h"
 #include "SkPath.h"
-#if DEBUG_ANGLE
-#include "SkString.h"
-#endif
-
-#if DEBUG_VALIDATE
-extern bool FLAGS_runFail;
-#endif
 
 #if defined SK_DEBUG || !FORCE_RELEASE
 
@@ -33,10 +26,10 @@ int SkPathOpsDebug::gSortCount;
 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
 #endif
 
-bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
-        const SkOpSpanBase* span) {
+bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
+        const SkOpSpan* span) {
     for (int index = 0; index < chaseArray.count(); ++index) {
-        const SkOpSpanBase* entry = chaseArray[index];
+        const SkOpSpan* entry = chaseArray[index];
         if (entry == span) {
             return true;
         }
@@ -72,8 +65,6 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
         SkDebugf("%d", wind);
     }
 }
-#endif //  defined SK_DEBUG || !FORCE_RELEASE
-
 
 #if DEBUG_SHOW_TEST_NAME
 void* SkPathOpsDebug::CreateNameStr() {
@@ -106,368 +97,470 @@ void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op,
 }
 #endif
 
+#endif //  defined SK_DEBUG || !FORCE_RELEASE
+
 #include "SkOpAngle.h"
 #include "SkOpSegment.h"
 
+#if DEBUG_SORT
+void SkOpAngle::debugLoop() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->dumpOne(true);
+        SkDebugf("\n");
+        next = next->fNext;
+    } while (next && next != first);
+}
+#endif
+
+#if DEBUG_ANGLE
+void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
+    SK_ALWAYSBREAK(fSegment == compare->fSegment);
+    const SkOpSpan& startSpan = fSegment->span(fStart);
+    const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
+    SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
+    SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
+    const SkOpSpan& endSpan = fSegment->span(fEnd);
+    const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
+    SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
+    SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
+}
+#endif
+
+#if DEBUG_VALIDATE
+void SkOpAngle::debugValidateNext() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = first;
+    SkTDArray<const SkOpAngle*>(angles);
+    do {
+//        SK_ALWAYSBREAK(next->fSegment->debugContains(next));
+        angles.push(next);
+        next = next->next();
+        if (next == first) {
+            break;
+        }
+        SK_ALWAYSBREAK(!angles.contains(next));
+        if (!next) {
+            return;
+        }
+    } while (true);
+}
+
+void SkOpAngle::debugValidateLoop() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = first;
+    SK_ALWAYSBREAK(first->next() != first);
+    int signSum = 0;
+    int oppSum = 0;
+    bool firstOperand = fSegment->operand();
+    bool unorderable = false;
+    do {
+        unorderable |= next->fUnorderable;
+        const SkOpSegment* segment = next->fSegment;
+        bool operandsMatch = firstOperand == segment->operand();
+        signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
+        oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
+        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+        if (segment->_xor()) {
+//            SK_ALWAYSBREAK(span.fWindValue == 1);
+//            SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
+        }
+        if (segment->oppXor()) {
+            SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
+//            SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
+        }
+        next = next->next();
+        if (!next) {
+            return;
+        }
+    } while (next != first);
+    if (unorderable) {
+        return;
+    }
+    SK_ALWAYSBREAK(!signSum || fSegment->_xor());
+    SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
+    int lastWinding;
+    int lastOppWinding;
+    int winding;
+    int oppWinding;
+    do {
+        const SkOpSegment* segment = next->fSegment;
+        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+        winding = span.fWindSum;
+        if (winding != SK_MinS32) {
+//            SK_ALWAYSBREAK(winding != 0);
+            SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+            lastWinding = winding;
+            int diffWinding = segment->spanSign(next);
+            if (!segment->_xor()) {
+                SK_ALWAYSBREAK(diffWinding != 0);
+                bool sameSign = (winding > 0) == (diffWinding > 0);
+                winding -= sameSign ? diffWinding : -diffWinding;
+                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+                SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
+                if (!sameSign) {
+                    SkTSwap(winding, lastWinding);
+                }
+            }
+            lastOppWinding = oppWinding = span.fOppSum;
+            if (oppWinding != SK_MinS32 && !segment->oppXor()) {
+                int oppDiffWinding = segment->oppSign(next);
+//                SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
+                if (oppDiffWinding) {
+                    bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
+                    oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
+                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+                    SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
+                    if (!oppSameSign) {
+                        SkTSwap(oppWinding, lastOppWinding);
+                    }
+                }
+            }
+            firstOperand = segment->operand();
+            break;
+        }
+        SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
+        next = next->next();
+    } while (next != first);
+    if (winding == SK_MinS32) {
+        return;
+    }
+    SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
+    first = next;
+    next = next->next();
+    do {
+        const SkOpSegment* segment = next->fSegment;
+        lastWinding = winding;
+        lastOppWinding = oppWinding;
+        bool operandsMatch = firstOperand == segment->operand();
+        if (operandsMatch) {
+            if (!segment->_xor()) {
+                winding -= segment->spanSign(next);
+                SK_ALWAYSBREAK(winding != lastWinding);
+                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+            }
+            if (!segment->oppXor()) {
+                int oppDiffWinding = segment->oppSign(next);
+                if (oppWinding != SK_MinS32) {
+                    oppWinding -= oppDiffWinding;
+                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+                } else {
+                    SK_ALWAYSBREAK(oppDiffWinding == 0);
+                }
+            }
+        } else {
+            if (!segment->oppXor()) {
+                winding -= segment->oppSign(next);
+                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+            }
+            if (!segment->_xor()) {
+                oppWinding -= segment->spanSign(next);
+                SK_ALWAYSBREAK(oppWinding != lastOppWinding);
+                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+            }
+        }
+        bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
+        int sumWinding = useInner ? winding : lastWinding;
+        bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
+        int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
+        if (!operandsMatch) {
+            SkTSwap(useInner, oppUseInner);
+            SkTSwap(sumWinding, oppSumWinding);
+        }
+        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+        if (winding == -lastWinding) {
+            if (span.fWindSum != SK_MinS32) {
+                SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
+                        __FUNCTION__,
+                        useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
+            }
+        }
+        if (oppWinding != SK_MinS32) {
+            if (span.fOppSum != SK_MinS32) {
+                SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
+            }
+        } else {
+            SK_ALWAYSBREAK(!firstOperand);
+            SK_ALWAYSBREAK(!segment->operand());
+            SK_ALWAYSBREAK(!span.fOppValue);
+        }
+        next = next->next();
+    } while (next != first);
+}
+#endif
+
 #if DEBUG_SWAP_TOP
-int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
     if (fVerb != SkPath::kCubic_Verb) {
         return false;
     }
-    SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+    return dst.controlsContainedByEnds();
+}
+#endif
+
+#if DEBUG_CONCIDENT
+// SK_ALWAYSBREAK if pair has not already been added
+void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
+    for (int i = 0; i < fTs.count(); ++i) {
+        if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
+            return;
+        }
+    }
+    SK_ALWAYSBREAK(0);
+}
+#endif
+
+#if DEBUG_ANGLE
+void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
+    const SkPoint& basePt = fTs[tStart].fPt;
+    while (++tStart < tEnd) {
+       const SkPoint& cmpPt = fTs[tStart].fPt;
+       SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
+    }
+}
+#endif
+
+#if DEBUG_SWAP_TOP
+int SkOpSegment::debugInflections(int tStart, int tEnd) const {
+    if (fVerb != SkPath::kCubic_Verb) {
+        return false;
+    }
+    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
     double inflections[2];
     return dst.findInflections(inflections);
 }
 #endif
 
-SkOpAngle* SkOpSegment::debugLastAngle() {
-    SkOpAngle* result = NULL;
-    SkOpSpan* span = this->head();
-    do {
-        if (span->toAngle()) {
+const SkOpAngle* SkOpSegment::debugLastAngle() const {
+    const SkOpAngle* result = NULL;
+    for (int index = 0; index < count(); ++index) {
+        const SkOpSpan& span = this->span(index);
+        if (span.fToAngle) {
             SkASSERT(!result);
-            result = span->toAngle();
+            result = span.fToAngle;
         }
-    } while ((span = span->next()->upCastable()));
+    }
     SkASSERT(result);
     return result;
 }
 
 void SkOpSegment::debugReset() {
-    this->init(this->fPts, this->contour(), this->verb());
+    fTs.reset();
+    fAngles.reset();
+}
+
+#if DEBUG_CONCIDENT
+void SkOpSegment::debugShowTs(const char* prefix) const {
+    SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
+    int lastWind = -1;
+    int lastOpp = -1;
+    double lastT = -1;
+    int i;
+    for (i = 0; i < fTs.count(); ++i) {
+        bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
+                || lastOpp != fTs[i].fOppValue;
+        if (change && lastWind >= 0) {
+            SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
+                    lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
+        }
+        if (change) {
+            SkDebugf(" [o=%d", fTs[i].fOther->fID);
+            lastWind = fTs[i].fWindValue;
+            lastOpp = fTs[i].fOppValue;
+            lastT = fTs[i].fT;
+        } else {
+            SkDebugf(",%d", fTs[i].fOther->fID);
+        }
+    }
+    if (i <= 0) {
+        return;
+    }
+    SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
+            lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
+    if (fOperand) {
+        SkDebugf(" operand");
+    }
+    if (done()) {
+        SkDebugf(" done");
+    }
+    SkDebugf("\n");
 }
+#endif
 
-#if DEBUG_ACTIVE_SPANS
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
 void SkOpSegment::debugShowActiveSpans() const {
     debugValidate();
     if (done()) {
         return;
     }
+#if DEBUG_ACTIVE_SPANS_SHORT_FORM
     int lastId = -1;
     double lastT = -1;
-    const SkOpSpan* span = &fHead;
-    do {
-        if (span->done()) {
+#endif
+    for (int i = 0; i < fTs.count(); ++i) {
+        if (fTs[i].fDone) {
             continue;
         }
-        if (lastId == fID && lastT == span->t()) {
+        SK_ALWAYSBREAK(i < fTs.count() - 1);
+#if DEBUG_ACTIVE_SPANS_SHORT_FORM
+        if (lastId == fID && lastT == fTs[i].fT) {
             continue;
         }
         lastId = fID;
-        lastT = span->t();
+        lastT = fTs[i].fT;
+#endif
         SkDebugf("%s id=%d", __FUNCTION__, fID);
         SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
         for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
             SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
         }
-        const SkOpPtT* ptT = span->ptT();
-        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
-        SkDebugf(" tEnd=%1.9g", span->next()->t());
-        SkDebugf(" windSum=");
-        if (span->windSum() == SK_MinS32) {
+        const SkOpSpan* span = &fTs[i];
+        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
+        int iEnd = i + 1;
+        while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
+            ++iEnd;
+        }
+        SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
+        const SkOpSegment* other = fTs[i].fOther;
+        SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
+                other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
+        if (fTs[i].fWindSum == SK_MinS32) {
             SkDebugf("?");
         } else {
-            SkDebugf("%d", span->windSum());
+            SkDebugf("%d", fTs[i].fWindSum);
         }
-        SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
-        SkDebugf("\n");
-   } while ((span = span->next()->upCastable()));
+        SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
+    }
 }
 #endif
 
-#if DEBUG_MARK_DONE
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
-    const SkPoint& pt = span->ptT()->fPt;
+#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
+    const SkPoint& pt = xyAtT(&span);
     SkDebugf("%s id=%d", fun, fID);
     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
     for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
     }
-    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
-            span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
-    if (winding == SK_MinS32) {
+    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
+            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
+    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
+            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
+            (&span)[1].fT, winding);
+    if (span.fWindSum == SK_MinS32) {
         SkDebugf("?");
     } else {
-        SkDebugf("%d", winding);
+        SkDebugf("%d", span.fWindSum);
     }
-    SkDebugf(" windSum=");
-    if (span->windSum() == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", span->windSum());
-    }
-    SkDebugf(" windValue=%d\n", span->windValue());
+    SkDebugf(" windValue=%d\n", span.fWindValue);
 }
 
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
                                       int oppWinding) {
-    const SkPoint& pt = span->ptT()->fPt;
+    const SkPoint& pt = xyAtT(&span);
     SkDebugf("%s id=%d", fun, fID);
     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
     for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
     }
-    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
-            span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
-    if (winding == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", winding);
-    }
-    SkDebugf(" newOppSum=");
-    if (oppWinding == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", oppWinding);
-    }
-    SkDebugf(" oppSum=");
-    if (span->oppSum() == SK_MinS32) {
+    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
+            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
+    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
+            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
+            (&span)[1].fT, winding, oppWinding);
+    if (span.fOppSum == SK_MinS32) {
         SkDebugf("?");
     } else {
-        SkDebugf("%d", span->oppSum());
+        SkDebugf("%d", span.fOppSum);
     }
     SkDebugf(" windSum=");
-    if (span->windSum() == SK_MinS32) {
+    if (span.fWindSum == SK_MinS32) {
         SkDebugf("?");
     } else {
-        SkDebugf("%d", span->windSum());
+        SkDebugf("%d", span.fWindSum);
     }
-    SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
+    SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
 }
-
 #endif
 
-#if DEBUG_ANGLE
-SkString SkOpAngle::debugPart() const {
-    SkString result;
-    switch (this->segment()->verb()) {
-        case SkPath::kLine_Verb:
-            result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
-                    this->segment()->debugID());
-            break;
-        case SkPath::kQuad_Verb:
-            result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
-                    this->segment()->debugID());
-            break;
-        case SkPath::kCubic_Verb:
-            result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
-                    this->segment()->debugID());
-            break;
-        default:
-            SkASSERT(0);
-    } 
-    return result;
-}
-#endif
-
-#if DEBUG_SORT
-void SkOpAngle::debugLoop() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->dumpOne(true);
-        SkDebugf("\n");
-        next = next->fNext;
-    } while (next && next != first);
-    next = first;
-    do {
-        next->debugValidate();
-        next = next->fNext;
-    } while (next && next != first);
-}
-#endif
-
-void SkOpAngle::debugValidate() const {
-#if DEBUG_VALIDATE
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    int wind = 0;
-    int opp = 0;
-    int lastXor = -1;
-    int lastOppXor = -1;
-    do {
-        if (next->unorderable()) {
-            return;
-        }
-        const SkOpSpan* minSpan = next->start()->starter(next->end());
-        if (minSpan->windValue() == SK_MinS32) {
-            return;
-        }
-        bool op = next->segment()->operand();
-        bool isXor = next->segment()->isXor();
-        bool oppXor = next->segment()->oppXor();
-        SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
-        SkASSERT(!DEBUG_LIMIT_WIND_SUM
-                || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
-        bool useXor = op ? oppXor : isXor;
-        SkASSERT(lastXor == -1 || lastXor == (int) useXor);
-        lastXor = (int) useXor;
-        wind += next->sign() * (op ? minSpan->oppValue() : minSpan->windValue());
-        if (useXor) {
-            wind &= 1;
-        }
-        useXor = op ? isXor : oppXor;
-        SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
-        lastOppXor = (int) useXor;
-        opp += next->sign() * (op ? minSpan->windValue() : minSpan->oppValue());
-        if (useXor) {
-            opp &= 1;
-        }
-        next = next->fNext;
-    } while (next && next != first);
-    SkASSERT(wind == 0);
-    SkASSERT(opp == 0 || !FLAGS_runFail);
-#endif
+#if DEBUG_SHOW_WINDING
+int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
+    if (!(1 << fID & ofInterest)) {
+        return 0;
+    }
+    int sum = 0;
+    SkTArray<char, true> slots(slotCount * 2);
+    memset(slots.begin(), ' ', slotCount * 2);
+    for (int i = 0; i < fTs.count(); ++i) {
+   //     if (!(1 << fTs[i].fOther->fID & ofInterest)) {
+   //         continue;
+   //     }
+        sum += fTs[i].fWindValue;
+        slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
+        sum += fTs[i].fOppValue;
+        slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
+    }
+    SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
+            slots.begin() + slotCount);
+    return sum;
 }
-
-void SkOpAngle::debugValidateNext() const {
-#if !FORCE_RELEASE
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = first;
-    SkTDArray<const SkOpAngle*>(angles);
-    do {
-//        SK_ALWAYSBREAK(next->fSegment->debugContains(next));
-        angles.push(next);
-        next = next->next();
-        if (next == first) {
-            break;
-        }
-        SK_ALWAYSBREAK(!angles.contains(next));
-        if (!next) {
-            return;
-        }
-    } while (true);
 #endif
-}
 
 void SkOpSegment::debugValidate() const {
 #if DEBUG_VALIDATE
-    const SkOpSpanBase* span = &fHead;
-    double lastT = -1;
-    const SkOpSpanBase* prev = NULL;
-    int count = 0;
+    int count = fTs.count();
+    SK_ALWAYSBREAK(count >= 2);
+    SK_ALWAYSBREAK(fTs[0].fT == 0);
+    SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
     int done = 0;
-    do {
-        if (!span->final()) {
-            ++count;
-            done += span->upCast()->done() ? 1 : 0;
-        }
-        SkASSERT(span->segment() == this);
-        SkASSERT(!prev || prev->upCast()->next() == span);
-        SkASSERT(!prev || prev == span->prev());
-        prev = span;
-        double t = span->ptT()->fT;
-        SkASSERT(lastT < t);
-        lastT = t;
-        span->debugValidate();
-    } while (!span->final() && (span = span->upCast()->next()));
-    SkASSERT(count == fCount);
-    SkASSERT(done == fDoneCount);
-    SkASSERT(span->final());
-    span->debugValidate();
-#endif
-}
-
-bool SkOpSpanBase::debugCoinEndLoopCheck() const {
-    int loop = 0;
-    const SkOpSpanBase* next = this;
-    SkOpSpanBase* nextCoin;
-    do {
-        nextCoin = next->fCoinEnd;
-        SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
-        for (int check = 1; check < loop - 1; ++check) {
-            const SkOpSpanBase* checkCoin = this->fCoinEnd;
-            const SkOpSpanBase* innerCoin = checkCoin;
-            for (int inner = check + 1; inner < loop; ++inner) {
-                innerCoin = innerCoin->fCoinEnd;
-                if (checkCoin == innerCoin) {
-                    SkDebugf("*** bad coincident end loop ***\n");
-                    return false;
-                }
-            }
-        }
-        ++loop;
-    } while ((next = nextCoin) && next != this);
-    return true;
-}
-
-void SkOpSpanBase::debugValidate() const {
-#if DEBUG_VALIDATE
-    const SkOpPtT* ptT = &fPtT;
-    SkASSERT(ptT->span() == this);
-    do {
-//        SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
-        ptT->debugValidate();
-        ptT = ptT->next();
-    } while (ptT != &fPtT);
-    SkASSERT(this->debugCoinEndLoopCheck());
-    if (!this->final()) {
-        SkASSERT(this->upCast()->debugCoinLoopCheck());
-    }
-    if (fFromAngle) {
-        fFromAngle->debugValidate();
-    }
-    if (!this->final() && this->upCast()->toAngle()) {
-        this->upCast()->toAngle()->debugValidate();
-    }
+    double t = -1;
+    const SkOpSpan* last = NULL;
+    bool tinyTFound = false;
+    bool hasLoop = false;
+    for (int i = 0; i < count; ++i) {
+        const SkOpSpan& span = fTs[i];
+        SK_ALWAYSBREAK(t <= span.fT);
+        t = span.fT;
+        int otherIndex = span.fOtherIndex;
+        const SkOpSegment* other = span.fOther;
+        SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
+        const SkOpSpan& otherSpan = other->fTs[otherIndex];
+        SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
+        SK_ALWAYSBREAK(otherSpan.fOtherT == t);
+        SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
+        done += span.fDone;
+        if (last) {
+            SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
+            bool tsEqual = last->fT == span.fT;
+            bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
+            SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
+            bool pointsEqual = last->fPt == span.fPt;
+            bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
+#if 0  // bufferOverflow test triggers this
+            SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
 #endif
-}
-
-bool SkOpSpan::debugCoinLoopCheck() const {
-    int loop = 0;
-    const SkOpSpan* next = this;
-    SkOpSpan* nextCoin;
-    do {
-        nextCoin = next->fCoincident;
-        SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
-        for (int check = 1; check < loop - 1; ++check) {
-            const SkOpSpan* checkCoin = this->fCoincident;
-            const SkOpSpan* innerCoin = checkCoin;
-            for (int inner = check + 1; inner < loop; ++inner) {
-                innerCoin = innerCoin->fCoincident;
-                if (checkCoin == innerCoin) {
-                    SkDebugf("*** bad coincident loop ***\n");
-                    return false;
-                }
-            }
-        }
-        ++loop;
-    } while ((next = nextCoin) && next != this);
-    return true;
-}
-
-#include "SkOpContour.h"
-
-int SkOpPtT::debugLoopLimit(bool report) const {
-    int loop = 0;
-    const SkOpPtT* next = this;
-    do {
-        for (int check = 1; check < loop - 1; ++check) {
-            const SkOpPtT* checkPtT = this->fNext;
-            const SkOpPtT* innerPtT = checkPtT;
-            for (int inner = check + 1; inner < loop; ++inner) {
-                innerPtT = innerPtT->fNext;
-                if (checkPtT == innerPtT) {
-                    if (report) {
-                        SkDebugf("*** bad ptT loop ***\n");
-                    }
-                    return loop;
-                }
+//            SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
+            SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
+            SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
+            SK_ALWAYSBREAK(!last->fTiny || last->fDone);
+            SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
+            SK_ALWAYSBREAK(!last->fSmall || last->fDone);
+//            SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
+//            SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
+            if (last->fTiny) {
+                tinyTFound |= !tsPreciselyEqual;
+            } else {
+                tinyTFound = false;
             }
         }
-        ++loop;
-    } while ((next = next->fNext) && next != this);
-    return 0;
-}
-
-void SkOpPtT::debugValidate() const {
-#if DEBUG_VALIDATE
-    if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
-        return;
+        last = &span;
+        hasLoop |= last->fLoop;
     }
-    SkASSERT(fNext);
-    SkASSERT(fNext != this);
-    SkASSERT(fNext->fNext);
-    SkASSERT(debugLoopLimit(false) == 0);
+    SK_ALWAYSBREAK(done == fDoneSpans);
+//    if (fAngles.count() ) {
+//        fAngles.begin()->debugValidateLoop();
+//    }
 #endif
 }
index 72a9ea528f65be7fec1ea1090035f4598957c7ed..5770aefec50c83dad3b0adc23dbb686d5b5c426d 100644 (file)
 
 #define DEBUG_ACTIVE_OP 0
 #define DEBUG_ACTIVE_SPANS 0
+#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
+#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
 #define DEBUG_ADD_INTERSECTING_TS 0
-#define DEBUG_ADD_T 0
+#define DEBUG_ADD_T_PAIR 0
 #define DEBUG_ANGLE 0
+#define DEBUG_AS_C_CODE 1
 #define DEBUG_ASSEMBLE 0
+#define DEBUG_CHECK_ALIGN 0
+#define DEBUG_CHECK_TINY 0
+#define DEBUG_CONCIDENT 0
+#define DEBUG_CROSS 0
 #define DEBUG_CUBIC_BINARY_SEARCH 0
+#define DEBUG_DUPLICATES 0
+#define DEBUG_FLAT_QUADS 0
 #define DEBUG_FLOW 0
 #define DEBUG_LIMIT_WIND_SUM 0
 #define DEBUG_MARK_DONE 0
 #define DEBUG_PATH_CONSTRUCTION 0
-#define DEBUG_PERP 0
 #define DEBUG_SHOW_TEST_NAME 0
+#define DEBUG_SHOW_TEST_PROGRESS 0
+#define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 0
+#define DEBUG_SORT_COMPACT 0
+#define DEBUG_SORT_RAW 0
+#define DEBUG_SORT_SINGLE 0
 #define DEBUG_SWAP_TOP 0
-#define DEBUG_T_SECT 0
-#define DEBUG_T_SECT_DUMP 0
+#define DEBUG_UNSORTABLE 0
 #define DEBUG_VALIDATE 0
+#define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 0
 #define DEBUG_WINDING_AT_T 0
 
 
 #define DEBUG_ACTIVE_OP 1
 #define DEBUG_ACTIVE_SPANS 1
+#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
+#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
 #define DEBUG_ADD_INTERSECTING_TS 1
-#define DEBUG_ADD_T 1
+#define DEBUG_ADD_T_PAIR 1
 #define DEBUG_ANGLE 1
+#define DEBUG_AS_C_CODE 1
 #define DEBUG_ASSEMBLE 1
+#define DEBUG_CHECK_ALIGN 1
+#define DEBUG_CHECK_TINY 1
+#define DEBUG_CONCIDENT 1
+#define DEBUG_CROSS 01
 #define DEBUG_CUBIC_BINARY_SEARCH 0
+#define DEBUG_DUPLICATES 1
+#define DEBUG_FLAT_QUADS 0
 #define DEBUG_FLOW 1
-#define DEBUG_LIMIT_WIND_SUM 5
+#define DEBUG_LIMIT_WIND_SUM 4
 #define DEBUG_MARK_DONE 1
 #define DEBUG_PATH_CONSTRUCTION 1
-#define DEBUG_PERP 0
 #define DEBUG_SHOW_TEST_NAME 1
+#define DEBUG_SHOW_TEST_PROGRESS 1
+#define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 1
+#define DEBUG_SORT_COMPACT 0
+#define DEBUG_SORT_RAW 0
+#define DEBUG_SORT_SINGLE 0
 #define DEBUG_SWAP_TOP 1
-#define DEBUG_T_SECT 1
-#define DEBUG_T_SECT_DUMP 02
-#define DEBUG_VALIDATE 1
+#define DEBUG_UNSORTABLE 1
+#define DEBUG_VALIDATE 0
+#define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 1
 #define DEBUG_WINDING_AT_T 1
 
 #endif
 
-#ifdef SK_RELEASE
-    #define PATH_OPS_DEBUG_RELEASE(a, b) b
-    #define PATH_OPS_DEBUG_CODE(...)
-    #define PATH_OPS_DEBUG_PARAMS(...)
-#else
-    #define PATH_OPS_DEBUG_RELEASE(a, b) a
-    #define PATH_OPS_DEBUG_CODE(...) __VA_ARGS__
-    #define PATH_OPS_DEBUG_PARAMS(...) , __VA_ARGS__
-#endif
-
-#if DEBUG_T_SECT == 0
-    #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b
-    #define PATH_OPS_DEBUG_T_SECT_PARAMS(...)
-    #define PATH_OPS_DEBUG_T_SECT_CODE(...)
+#if DEBUG_AS_C_CODE
+#define CUBIC_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define QUAD_DEBUG_STR  "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define LINE_DEBUG_STR  "{{%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
 #else
-    #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) a
-    #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) , __VA_ARGS__
-    #define PATH_OPS_DEBUG_T_SECT_CODE(...) __VA_ARGS__
+#define CUBIC_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+#define QUAD_DEBUG_STR  "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+#define LINE_DEBUG_STR  "(%1.9g,%1.9g %1.9g,%1.9g)"
+#define PT_DEBUG_STR "(%1.9g,%1.9g)"
 #endif
-
-#if DEBUG_T_SECT_DUMP > 1
-    extern int gDumpTSectNum;
-#endif
-
-#define CUBIC_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define QUAD_DEBUG_STR  "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define LINE_DEBUG_STR  "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
-
 #define T_DEBUG_STR(t, n) #t "[" #n "]=%1.9g"
 #define TX_DEBUG_STR(t) #t "[%d]=%1.9g"
 #define CUBIC_DEBUG_DATA(c) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY
 #include "SkTLS.h"
 #endif
 
+#include "SkTArray.h"
 #include "SkTDArray.h"
 
 class SkPathOpsDebug {
@@ -147,6 +156,7 @@ public:
     static const char* kPathOpStr[];
 #endif
 
+    static bool ChaseContains(const SkTDArray<struct SkOpSpan *>& , const struct SkOpSpan * );
     static void MathematicaIze(char* str, size_t bufferSize);
     static bool ValidWind(int winding);
     static void WindingPrintf(int winding);
@@ -161,96 +171,66 @@ public:
 #endif
     static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
     static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
-
-    static bool ChaseContains(const SkTDArray<class SkOpSpanBase*>& , const class SkOpSpanBase* );
-
-    static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
-    static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
-    static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
-    static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
-    static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
-
-    static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
-    static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
-    static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
-    static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
-    static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
-
-    static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
-    static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
-    static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
-    static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
-    static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
-
-    static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
-    static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
-    static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
-    static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
-    static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
-
-    static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
-    static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
-    static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
-    static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
-    static const class SkOpSpanBase* DebugSpanSpan(const class SkOpSpanBase*, int id);
-
-    static void DumpContours(SkTDArray<class SkOpContour* >* contours);
-    static void DumpContoursAll(SkTDArray<class SkOpContour* >* contours);
-    static void DumpContoursAngles(const SkTDArray<class SkOpContour* >* contours);
-    static void DumpContoursPt(const SkTDArray<class SkOpContour* >* contours, int id);
-    static void DumpContoursPts(const SkTDArray<class SkOpContour* >* contours);
-    static void DumpContoursSegment(const SkTDArray<class SkOpContour* >* contours, int id);
-    static void DumpContoursSpan(const SkTDArray<class SkOpContour* >* contours, int id);
-    static void DumpContoursSpans(const SkTDArray<class SkOpContour* >* contours);
+    static void DumpCoincidence(const SkTArray<class SkOpContour, true>& contours);
+    static void DumpCoincidence(const SkTArray<class SkOpContour* , true>& contours);
+    static void DumpContours(const SkTArray<class SkOpContour, true>& contours);
+    static void DumpContours(const SkTArray<class SkOpContour* , true>& contours);
+    static void DumpContourAngles(const SkTArray<class SkOpContour, true>& contours);
+    static void DumpContourAngles(const SkTArray<class SkOpContour* , true>& contours);
+    static void DumpContourPt(const SkTArray<class SkOpContour, true>& contours, int id);
+    static void DumpContourPt(const SkTArray<class SkOpContour* , true>& contours, int id);
+    static void DumpContourPts(const SkTArray<class SkOpContour, true>& contours);
+    static void DumpContourPts(const SkTArray<class SkOpContour* , true>& contours);
+    static void DumpContourSpan(const SkTArray<class SkOpContour, true>& contours, int id);
+    static void DumpContourSpan(const SkTArray<class SkOpContour* , true>& contours, int id);
+    static void DumpContourSpans(const SkTArray<class SkOpContour, true>& contours);
+    static void DumpContourSpans(const SkTArray<class SkOpContour* , true>& contours);
+    static void DumpSpans(const SkTDArray<struct SkOpSpan *>& );
+    static void DumpSpans(const SkTDArray<struct SkOpSpan *>* );
 };
 
 // shorthand for calling from debugger
-template<typename TCurve> class SkTSect;
-template<typename TCurve> class SkTSpan;
-
-struct SkDQuad;
-struct SkDCubic;
-
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSect<SkDCubic>* , int id);
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSect<SkDQuad>* , int id);
-const SkTSpan<SkDCubic>* DebugT(const SkTSect<SkDCubic>* , double t);
-const SkTSpan<SkDQuad>* DebugT(const SkTSect<SkDQuad>* , double t);
-
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSpan<SkDCubic>* , int id);
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSpan<SkDQuad>* , int id);
-const SkTSpan<SkDCubic>* DebugT(const SkTSpan<SkDCubic>* , double t);
-const SkTSpan<SkDQuad>* DebugT(const SkTSpan<SkDQuad>* , double t);
-
-void Dump(const SkTSect<SkDCubic>* );
-void Dump(const SkTSect<SkDQuad>* );
-void Dump(const SkTSpan<SkDCubic>* , const SkTSect<SkDCubic>* = NULL);
-void Dump(const SkTSpan<SkDQuad>* , const SkTSect<SkDQuad>* = NULL);
-void DumpBoth(SkTSect<SkDCubic>* sect1, SkTSect<SkDCubic>* sect2);
-void DumpBoth(SkTSect<SkDQuad>* sect1, SkTSect<SkDQuad>* sect2);
-void DumpCoin(SkTSect<SkDCubic>* sect1);
-void DumpCoin(SkTSect<SkDQuad>* sect1);
-void DumpCoinCurves(SkTSect<SkDCubic>* sect1);
-void DumpCoinCurves(SkTSect<SkDQuad>* sect1);
-void DumpCurves(const SkTSpan<SkDCubic>* );
-void DumpCurves(const SkTSpan<SkDQuad>* );
+void Dump(const SkTArray<class SkOpContour, true>& contours);
+void Dump(const SkTArray<class SkOpContour* , true>& contours);
+void Dump(const SkTArray<class SkOpContour, true>* contours);
+void Dump(const SkTArray<class SkOpContour* , true>* contours);
+
+void Dump(const SkTDArray<SkOpSpan* >& chase);
+void Dump(const SkTDArray<SkOpSpan* >* chase);
+
+void DumpAngles(const SkTArray<class SkOpContour, true>& contours);
+void DumpAngles(const SkTArray<class SkOpContour* , true>& contours);
+void DumpAngles(const SkTArray<class SkOpContour, true>* contours);
+void DumpAngles(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpCoin(const SkTArray<class SkOpContour, true>& contours);
+void DumpCoin(const SkTArray<class SkOpContour* , true>& contours);
+void DumpCoin(const SkTArray<class SkOpContour, true>* contours);
+void DumpCoin(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpPts(const SkTArray<class SkOpContour, true>& contours);
+void DumpPts(const SkTArray<class SkOpContour* , true>& contours);
+void DumpPts(const SkTArray<class SkOpContour, true>* contours);
+void DumpPts(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
+
+void DumpSpans(const SkTArray<class SkOpContour, true>& contours);
+void DumpSpans(const SkTArray<class SkOpContour* , true>& contours);
+void DumpSpans(const SkTArray<class SkOpContour, true>* contours);
+void DumpSpans(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
 
 // generates tools/path_sorter.htm and path_visualizer.htm compatible data
-void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
-void DumpT(const SkDQuad& quad, double t);
+void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
 
-const struct SkOpAngle* DebugAngle(const SkTDArray<class SkOpContour* >* contours, int id);
-class SkOpContour* DebugContour(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpPtT* DebugPtT(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpSegment* DebugSegment(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpSpanBase* DebugSpan(const SkTDArray<class SkOpContour* >* contours, int id);
+void DumpT(const struct SkDQuad& quad, double t);
 
-void Dump(const SkTDArray<class SkOpContour* >* contours);
-void DumpAll(SkTDArray<class SkOpContour* >* contours);
-void DumpAngles(const SkTDArray<class SkOpContour* >* contours);
-void DumpCoin(const SkTDArray<class SkOpContour* >* contours);
-void DumpPt(const SkTDArray<class SkOpContour* >* contours, int segmentID);
-void DumpPts(const SkTDArray<class SkOpContour* >* contours);
-void DumpSegment(const SkTDArray<class SkOpContour* >* contours, int segmentID);
-void DumpSpan(const SkTDArray<class SkOpContour* >* contours, int spanID);
-void DumpSpans(const SkTDArray<class SkOpContour* >* contours);
 #endif
index 70f2e12472b401240a1875cb1d8b2023571805cf..e4fc97bc616ce9d3eaae698f19a0ead98ec92a8f 100644 (file)
@@ -6,6 +6,14 @@
  */
 #include "SkPathOpsLine.h"
 
+SkDLine SkDLine::subDivide(double t1, double t2) const {
+    SkDVector delta = tangent();
+    SkDLine dst = {{{
+            fPts[0].fX - t1 * delta.fX, fPts[0].fY - t1 * delta.fY}, {
+            fPts[0].fX - t2 * delta.fX, fPts[0].fY - t2 * delta.fY}}};
+    return dst;
+}
+
 // may have this below somewhere else already:
 // copying here because I thought it was clever
 
@@ -20,7 +28,6 @@
 //    Point with coordinates {float x, y;}
 //===================================================================
 
-// (only used by testing)
 // isLeft(): tests if a point is Left|On|Right of an infinite line.
 //    Input:  three points P0, P1, and P2
 //    Return: >0 for P2 left of the line through P0 and P1
@@ -103,6 +110,19 @@ bool SkDLine::nearRay(const SkDPoint& xy) const {
     return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
 }
 
+// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2)
+// OPTIMIZE: a specialty routine could speed this up -- may not be called very often though
+bool SkDLine::NearRay(double x1, double y1, double x2, double y2) {
+    double denom1 = x1 * x1 + y1 * y1;
+    double denom2 = x2 * x2 + y2 * y2;
+    SkDLine line = {{{0, 0}, {x1, y1}}};
+    SkDPoint pt = {x2, y2};
+    if (denom2 > denom1) {
+        SkTSwap(line[1], pt);
+    }
+    return line.nearRay(pt);
+}
+
 double SkDLine::ExactPointH(const SkDPoint& xy, double left, double right, double y) {
     if (xy.fY == y) {
         if (xy.fX == left) {
index bb2516286013e0784f2d4e8a31a5b2aebcdf1993..74eb6153488814e7a3ad41cdec71f9b4a0ab1030 100644 (file)
@@ -20,20 +20,27 @@ struct SkDLine {
         fPts[1] = pts[1];
     }
 
+    static SkDLine SubDivide(const SkPoint a[2], double t1, double t2) {
+        SkDLine line;
+        line.set(a);
+        return line.subDivide(t1, t2);
+    }
+
     double exactPoint(const SkDPoint& xy) const;
     static double ExactPointH(const SkDPoint& xy, double left, double right, double y);
     static double ExactPointV(const SkDPoint& xy, double top, double bottom, double x);
-
-    // only used by testing
     double isLeft(const SkDPoint& pt) const;
-
     double nearPoint(const SkDPoint& xy, bool* unequal) const;
     bool nearRay(const SkDPoint& xy) const;
     static double NearPointH(const SkDPoint& xy, double left, double right, double y);
     static double NearPointV(const SkDPoint& xy, double top, double bottom, double x);
+    static bool NearRay(double dx1, double dy1, double dx2, double dy2);
     SkDPoint ptAtT(double t) const;
+    SkDLine subDivide(double t1, double t2) const;
 
     void dump() const;
+private:
+    SkDVector tangent() const { return fPts[0] - fPts[1]; }
 };
 
 #endif
index 77ae2de778f7bb38c3b688774992108f6d6836fa..f2b25c04ecb52ab303519752d2db61ccd29168d3 100644 (file)
@@ -5,29 +5,27 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
-        SkOpSpanBase** endPtr) {
+static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
     while (chase.count()) {
-        SkOpSpanBase* span;
+        SkOpSpan* span;
         chase.pop(&span);
-        // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
-        *startPtr = span->ptT()->prev()->span();
-        SkOpSegment* segment = (*startPtr)->segment();
+        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
+        SkOpSegment* segment = backPtr.fOther;
+        *tIndex = backPtr.fOtherIndex;
         bool sortable = true;
         bool done = true;
-        *endPtr = NULL;
-        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
+        *endIndex = -1;
+        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
                 &sortable)) {
             if (last->unorderable()) {
                 continue;
             }
-            *startPtr = last->start();
-            *endPtr = last->end();
+            *tIndex = last->start();
+            *endIndex = last->end();
    #if TRY_ROTATE
             *chase.insert(0) = span;
    #else
@@ -42,7 +40,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
             continue;
         }
         // find first angle, initialize winding to computed fWindSum
-        const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
+        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
         if (!angle) {
             continue;
         }
@@ -67,25 +65,33 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
             SkTSwap<int>(sumMiWinding, sumSuWinding);
         }
         SkOpSegment* first = NULL;
-        firstAngle = angle;
-        while ((angle = angle->next()) != firstAngle) {
+        bool badData = false;
+        while ((angle = angle->next()) != firstAngle && !badData) {
             segment = angle->segment();
-            SkOpSpanBase* start = angle->start();
-            SkOpSpanBase* end = angle->end();
+            int start = angle->start();
+            int end = angle->end();
             int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
             segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
                     &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
             if (!segment->done(angle)) {
                 if (!first) {
                     first = segment;
-                    *startPtr = start;
-                    *endPtr = end;
+                    *tIndex = start;
+                    *endIndex = end;
+                }
+                if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
+                        oppSumWinding, angle)) {
+                    badData = true;
+                    break;
                 }
                 // OPTIMIZATION: should this also add to the chase?
                 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
                     oppSumWinding, angle);
             }
         }
+        if (badData) {
+            continue;
+        }
         if (first) {
        #if TRY_ROTATE
             *chase.insert(0) = span;
@@ -98,8 +104,36 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
     return NULL;
 }
 
-static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
-        const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
+/*
+static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
+        bool windingIsOp, PathOp op) {
+    bool active = windingIsActive(winding, spanWinding);
+    if (!active) {
+        return false;
+    }
+    if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
+        switch (op) {
+            case kIntersect_Op:
+            case kUnion_Op:
+                return true;
+            case kDifference_Op: {
+                int absSpan = abs(spanWinding);
+                int absOpp = abs(oppSpanWinding);
+                return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
+            }
+            case kXor_Op:
+                return spanWinding != oppSpanWinding;
+            default:
+                SkASSERT(0);
+        }
+    }
+    bool opActive = oppWinding != 0;
+    return gOpLookup[op][opActive][windingIsOp];
+}
+*/
+
+static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
+        const int xorMask, const int xorOpMask, SkPathWriter* simple) {
     bool firstContour = true;
     bool unsortable = false;
     bool topUnsortable = false;
@@ -107,14 +141,12 @@ static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
     SkPoint lastTopLeft;
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        SkOpSpanBase* start;
-        SkOpSpanBase* end;
+        int index, endIndex;
         bool topDone;
         bool onlyVertical = false;
         lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kBinarySingle,
-                &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
-                allocator);
+        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
+                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
         if (!current) {
             if ((!topUnsortable || firstPass) && !topDone) {
                 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
@@ -133,65 +165,69 @@ static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
             break;
         }
         firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpanBase*> chase;
+        SkTDArray<SkOpSpan*> chase;
         do {
-            if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
+            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
                 do {
                     if (!unsortable && current->done()) {
                         break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    SkOpSpanBase* nextStart = start;
-                    SkOpSpanBase* nextEnd = end;
+                    int nextStart = index;
+                    int nextEnd = endIndex;
                     SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
                             &unsortable, op, xorMask, xorOpMask);
                     if (!next) {
                         if (!unsortable && simple->hasMove()
                                 && current->verb() != SkPath::kLine_Verb
                                 && !simple->isClosed()) {
-                            current->addCurveTo(start, end, simple, true);
+                            current->addCurveTo(index, endIndex, simple, true);
                     #if DEBUG_ACTIVE_SPANS
                             if (!simple->isClosed()) {
                                 DebugShowActiveSpans(contourList);
                             }
                     #endif
+//                            SkASSERT(simple->isClosed());
                         }
                         break;
                     }
         #if DEBUG_FLOW
-                    SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                            current->debugID(), start->pt().fX, start->pt().fY,
-                            end->pt().fX, end->pt().fY);
+            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(start, end, simple, true);
+                    current->addCurveTo(index, endIndex, simple, true);
                     current = next;
-                    start = nextStart;
-                    end = nextEnd;
-                } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
-                if (current->activeWinding(start, end) && !simple->isClosed()) {
-                    SkOpSpan* spanStart = start->starter(end);
-                    if (!spanStart->done()) {
-                        current->addCurveTo(start, end, simple, true);
-                        current->markDone(spanStart);
+                    index = nextStart;
+                    endIndex = nextEnd;
+                } while (!simple->isClosed() && (!unsortable
+                        || !current->done(SkMin32(index, endIndex))));
+                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
+                    // FIXME : add to simplify, xor cpaths
+                    int min = SkMin32(index, endIndex);
+                    if (!unsortable && !simple->isEmpty()) {
+                        unsortable = current->checkSmall(min);
+                    }
+                    if (!current->done(min)) {
+                        current->addCurveTo(index, endIndex, simple, true);
+                        current->markDoneBinary(min);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpanBase* last = current->markAndChaseDone(start, end);
-                if (last && !last->chased()) {
-                    last->setChased(true);
+                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
+                if (last && !last->fChased && !last->fLoop) {
+                    last->fChased = true;
                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
                     *chase.append() = last;
 #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
-                    if (!last->final()) {
-                         SkDebugf(" windSum=%d", last->upCast()->windSum());
-                    }
-                    SkDebugf("\n");
+                    SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
+                            last->fSmall);
 #endif
                 }
             }
-            current = findChaseOp(chase, &start, &end);
+            current = findChaseOp(chase, &index, &endIndex);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -255,19 +291,16 @@ static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
     dump_path(file, two, false, true);
     fprintf(file, "    SkPath path2(path);\n");
     fprintf(file, "    testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
-    fprintf(file, "}\n");    
+    fprintf(file, "}\n");      
     fclose(file);
 }
 #endif
 
 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
-    SkOpContour contour;
-    SkOpCoincidence coincidence;
-    SkOpGlobalState globalState(&coincidence  PATH_OPS_DEBUG_PARAMS(&contour));
 #if DEBUGGING_PATHOPS_FROM_HOST
     dump_op(one, two, op);
-#endif    
-#if 0 && DEBUG_SHOW_TEST_NAME
+#endif 
+#if DEBUG_SHOW_TEST_NAME
     char* debugName = DEBUG_FILENAME_STRING;
     if (debugName && debugName[0]) {
         SkPathOpsDebug::BumpTestName(debugName);
@@ -288,54 +321,53 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
 #endif
     // turn path into list of segments
-    SkChunkAlloc allocator(4096);  // FIXME: add a constant expression here, tune
-    SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
+    SkTArray<SkOpContour> contours;
+    // FIXME: add self-intersecting cubics' T values to segment
+    SkOpEdgeBuilder builder(*minuend, contours);
     if (builder.unparseable()) {
         return false;
     }
     const int xorMask = builder.xorMask();
     builder.addOperand(*subtrahend);
-    if (!builder.finish(&allocator)) {
+    if (!builder.finish()) {
         return false;
     }
-#if !FORCE_RELEASE
-    contour.dumpSegments(op);
-#endif
-
     result->reset();
     result->setFillType(fillType);
     const int xorOpMask = builder.xorMask();
-    SkTDArray<SkOpContour* > contourList;
-    MakeContourList(&contour, contourList, xorMask == kEvenOdd_PathOpsMask,
+    SkTArray<SkOpContour*, true> contourList;
+    MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
             xorOpMask == kEvenOdd_PathOpsMask);
     SkOpContour** currentPtr = contourList.begin();
     if (!currentPtr) {
         return true;
     }
-    if ((*currentPtr)->count() == 0) {
-        SkASSERT((*currentPtr)->next() == NULL);
-        return true;
-    }
     SkOpContour** listEnd = contourList.end();
     // find all intersections between segments
     do {
         SkOpContour** nextPtr = currentPtr;
         SkOpContour* current = *currentPtr++;
+        if (current->containsCubics()) {
+            AddSelfIntersectTs(current);
+        }
         SkOpContour* next;
         do {
             next = *nextPtr++;
-        } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd);
+        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
     } while (currentPtr != listEnd);
-#if DEBUG_VALIDATE
-    globalState.setPhase(SkOpGlobalState::kWalking);
-#endif
     // eat through coincident edges
-    if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
+
+    int total = 0;
+    int index;
+    for (index = 0; index < contourList.count(); ++index) {
+        total += contourList[index]->segments().count();
+    }
+    if (!HandleCoincidence(&contourList, total)) {
         return false;
     }
     // construct closed contours
     SkPathWriter wrapper(*result);
-    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
+    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
index 2d07427783c669d4faee2c926dc71ef9129d175a..7ddfbfb5d15bce28b46643e2cd7b46122e22fcfc 100644 (file)
@@ -25,25 +25,21 @@ struct SkDVector {
 
     friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);
 
-    // only used by testing
     void operator+=(const SkDVector& v) {
         fX += v.fX;
         fY += v.fY;
     }
 
-    // only called by nearestT, which is currently only used by testing
     void operator-=(const SkDVector& v) {
         fX -= v.fX;
         fY -= v.fY;
     }
 
-    // only used by testing
     void operator/=(const double s) {
         fX /= s;
         fY /= s;
     }
 
-    // only used by testing
     void operator*=(const double s) {
         fX *= s;
         fY *= s;
@@ -54,7 +50,6 @@ struct SkDVector {
         return v;
     }
 
-    // only used by testing
     double cross(const SkDVector& a) const {
         return fX * a.fY - fY * a.fX;
     }
@@ -103,13 +98,11 @@ struct SkDPoint {
         fY = pt.fY;
     }
 
-    // only used by testing
     void operator+=(const SkDVector& v) {
         fX += v.fX;
         fY += v.fY;
     }
 
-    // only used by testing
     void operator-=(const SkDVector& v) {
         fX -= v.fX;
         fY -= v.fY;
@@ -129,7 +122,7 @@ struct SkDPoint {
         double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
         double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
         largest = SkTMax(largest, -tiniest);
-        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+        return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
     bool approximatelyEqual(const SkPoint& a) const {
@@ -152,10 +145,44 @@ struct SkDPoint {
         float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
         float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
         largest = SkTMax(largest, -tiniest);
-        return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+        return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+    }
+
+    static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
+        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
+            return true;
+        }
+        return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
+    }
+
+    bool approximatelyPEqual(const SkDPoint& a) const {
+        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
+            return true;
+        }
+        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
+            return false;
+        }
+        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
+        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+        largest = SkTMax(largest, -tiniest);
+        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+    }
+
+    bool approximatelyDEqual(const SkDPoint& a) const {
+        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
+            return true;
+        }
+        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
+            return false;
+        }
+        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
+        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+        largest = SkTMax(largest, -tiniest);
+        return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
-    // only used by testing
     bool approximatelyZero() const {
         return approximately_zero(fX) && approximately_zero(fY);
     }
@@ -182,7 +209,7 @@ struct SkDPoint {
         return result;
     }
 
-    bool roughlyEqual(const SkDPoint& a) const {
+    bool moreRoughlyEqual(const SkDPoint& a) const {
         if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
             return true;
         }
@@ -193,6 +220,10 @@ struct SkDPoint {
         return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
+    bool roughlyEqual(const SkDPoint& a) const {
+        return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
+    }
+
     // utilities callable by the user from the debugger when the implementation code is linked in
     void dump() const;
     static void Dump(const SkPoint& pt);
old mode 100755 (executable)
new mode 100644 (file)
index eb2d1ab..15a1900
@@ -17,8 +17,8 @@ SkOpContour* SkOpPtT::contour() const {
     return segment()->contour();
 }
 
-SkOpGlobalState* SkOpPtT::globalState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
+SkOpDebugState* SkOpPtT::debugState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
 }
 
 void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -28,7 +28,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
     fNext = this;
     fDuplicatePt = duplicate;
     fDeleted = false;
-    PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
+    PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
 }
 
 bool SkOpPtT::onEnd() const {
@@ -45,7 +45,7 @@ SkOpPtT* SkOpPtT::remove() {
     do {
         SkOpPtT* next = prev->fNext;
         if (next == this) {
-            prev->removeNext(this);
+            prev->removeNext();
             fDeleted = true;
             return prev;
         }
@@ -55,14 +55,14 @@ SkOpPtT* SkOpPtT::remove() {
     return NULL;
 }
 
-void SkOpPtT::removeNext(SkOpPtT* kept) {
+void SkOpPtT::removeNext() {
     SkASSERT(this->fNext);
     SkOpPtT* next = this->fNext;
     this->fNext = next->fNext;
     SkOpSpanBase* span = next->span();
     next->setDeleted();
     if (span->ptT() == next) {
-        span->upCast()->detach(kept);
+        span->upCast()->detach();
     }
 }
 
@@ -199,7 +199,7 @@ void SkOpSpanBase::alignInner() {
                 // omit aliases that alignment makes redundant
                 if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
                     SkASSERT(test->alias());
-                    prev->removeNext(ptT);
+                    prev->removeNext();
                     test = prev;
                 } else {
                     SkASSERT(ptT->alias());
@@ -239,8 +239,8 @@ SkOpContour* SkOpSpanBase::contour() const {
     return segment()->contour();
 }
 
-SkOpGlobalState* SkOpSpanBase::globalState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
+SkOpDebugState* SkOpSpanBase::debugState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
 }
 
 void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -252,7 +252,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
     fAligned = true;
     fChased = false;
     PATH_OPS_DEBUG_CODE(fCount = 1);
-    PATH_OPS_DEBUG_CODE(fID = ++globalState()->fSpanID);
+    PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
 }
 
 // this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -261,7 +261,7 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
     SkOpPtT* spanPtT = span->ptT();
     SkASSERT(this->t() != spanPtT->fT);
     SkASSERT(!zero_or_one(spanPtT->fT));
-    span->detach(this->ptT());
+    span->detach();
     SkOpPtT* remainder = spanPtT->next();
     ptT()->insert(spanPtT);
     while (remainder != spanPtT) {
@@ -304,7 +304,7 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
     return false;
 }
 
-void SkOpSpan::detach(SkOpPtT* kept) {
+void SkOpSpan::detach() {
     SkASSERT(!final());
     SkOpSpan* prev = this->prev();
     SkASSERT(prev);
@@ -313,9 +313,6 @@ void SkOpSpan::detach(SkOpPtT* kept) {
     prev->setNext(next);
     next->setPrev(prev);
     this->segment()->detach(this);
-    if (this->coincident()) {
-        this->globalState()->fCoincidence->fixUp(this->ptT(), kept);
-    }
     this->ptT()->setDeleted();
 }
 
index 4913c9f9f3fcb028c108073de8842aa096091e19..c1d068af345701e64c5251007d172d29e83fbeb3 100644 (file)
@@ -8,61 +8,7 @@
 #include "SkLineParameters.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsQuad.h"
-
-/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
-// Do a quick reject by rotating all points relative to a line formed by
-// a pair of one quad's points. If the 2nd quad's points
-// are on the line or on the opposite side from the 1st quad's 'odd man', the
-// curves at most intersect at the endpoints.
-/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
-   if returning false, check contains true if the the quad pair have only the end point in common
-*/
-bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
-    bool linear = true;
-    for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
-        const SkDPoint* endPt[2];
-        this->otherPts(oddMan, endPt);
-        double origX = endPt[0]->fX;
-        double origY = endPt[0]->fY;
-        double adj = endPt[1]->fX - origX;
-        double opp = endPt[1]->fY - origY;
-        double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
-        if (approximately_zero(sign)) {
-            continue;
-        }
-        linear = false;
-        bool foundOutlier = false;
-        for (int n = 0; n < kPointCount; ++n) {
-            double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
-            if (test * sign > 0 && !precisely_zero(test)) {
-                foundOutlier = true;
-                break;
-            }
-        }
-        if (!foundOutlier) {
-            return false;
-        }
-    }
-    *isLinear = linear;
-    return true;
-}
-
-/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
-oddMan    opp   x=oddMan^opp  x=x-oddMan  m=x>>2   x&~m
-    0       1         1            1         0       1
-            2         2            2         0       2
-    1       1         0           -1        -1       0
-            2         3            2         0       2
-    2       1         3            1         0       1
-            2         0           -2        -1       0
-*/
-void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
-    for (int opp = 1; opp < kPointCount; ++opp) {
-        int end = (oddMan ^ opp) - oddMan;  // choose a value not equal to oddMan
-        end &= ~(end >> 2);  // if the value went negative, set it to zero
-        endPt[opp - 1] = &fPts[end];
-    }
-}
+#include "SkPathOpsTriangle.h"
 
 // from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
 // (currently only used by testing)
@@ -97,6 +43,10 @@ double SkDQuad::nearestT(const SkDPoint& pt) const {
     return d0 < d2 ? 0 : 1;
 }
 
+bool SkDQuad::pointInHull(const SkDPoint& pt) const {
+    return ((const SkDTriangle&) fPts).contains(pt);
+}
+
 SkDPoint SkDQuad::top(double startT, double endT) const {
     SkDQuad sub = subDivide(startT, endT);
     SkDPoint topPt = sub[0];
@@ -190,12 +140,7 @@ bool SkDQuad::isLinear(int startIndex, int endIndex) const {
     // FIXME: maybe it's possible to avoid this and compare non-normalized
     lineParameters.normalize();
     double distance = lineParameters.controlPtDistance(*this);
-    double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
-            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
-    double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
-            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
-    largest = SkTMax(largest, -tiniest);
-    return approximately_zero_when_compared_to(distance, largest);
+    return approximately_zero(distance);
 }
 
 SkDCubic SkDQuad::toCubic() const {
@@ -295,6 +240,13 @@ void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
 SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
     SkASSERT(t1 != t2);
     SkDPoint b;
+#if 0
+    // this approach assumes that the control point computed directly is accurate enough
+    double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
+    double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
+    b.fX = 2 * dx - (a.fX + c.fX) / 2;
+    b.fY = 2 * dy - (a.fY + c.fY) / 2;
+#else
     SkDQuad sub = subDivide(t1, t2);
     SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
     SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
@@ -306,6 +258,7 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
         SkASSERT(i.used() <= 2);
         b = SkDPoint::Mid(b0[1], b1[1]);
     }
+#endif
     if (t1 == 0 || t2 == 0) {
         align(0, &b);
     }
index 81638cf0bc012c33d5a1b303680091fa410de25c..932c5fbe75d9b7519ff720eeae30aafca09692d4 100644 (file)
@@ -17,61 +17,43 @@ struct SkDQuadPair {
 };
 
 struct SkDQuad {
-    static const int kPointCount = 3;
-    static const int kPointLast = kPointCount - 1;
-    static const int kMaxIntersections = 4;
-
-    SkDPoint fPts[kPointCount];
-
-    bool collapsed() const {
-        return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2]);
-    }
-
-    bool controlsInside() const {
-        SkDVector v01 = fPts[0] - fPts[1];
-        SkDVector v02 = fPts[0] - fPts[2];
-        SkDVector v12 = fPts[1] - fPts[2];
-        return v02.dot(v01) > 0 && v02.dot(v12) > 0;
-    }
+    SkDPoint fPts[3];
 
     SkDQuad flip() const {
         SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
         return result;
     }
 
-    static bool IsCubic() { return false; }
-
-    void set(const SkPoint pts[kPointCount]) {
+    void set(const SkPoint pts[3]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
     }
 
-    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
-    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
+    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
 
     static int AddValidTs(double s[], int realRoots, double* t);
     void align(int endIndex, SkDPoint* dstPt) const;
     SkDQuadPair chopAt(double t) const;
     SkDVector dxdyAtT(double t) const;
     static int FindExtrema(double a, double b, double c, double tValue[1]);
-    bool hullIntersects(const SkDQuad& , bool* isLinear) const;
     bool isLinear(int startIndex, int endIndex) const;
     bool monotonicInY() const;
     double nearestT(const SkDPoint&) const;
-    void otherPts(int oddMan, const SkDPoint* endPt[2]) const;
+    bool pointInHull(const SkDPoint&) const;
     SkDPoint ptAtT(double t) const;
     static int RootsReal(double A, double B, double C, double t[2]);
     static int RootsValidT(const double A, const double B, const double C, double s[2]);
     static void SetABC(const double* quad, double* a, double* b, double* c);
     SkDQuad subDivide(double t1, double t2) const;
-    static SkDQuad SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
+    static SkDQuad SubDivide(const SkPoint a[3], double t1, double t2) {
         SkDQuad quad;
         quad.set(a);
         return quad.subDivide(t1, t2);
     }
     SkDPoint subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const;
-    static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
+    static SkDPoint SubDivide(const SkPoint pts[3], const SkDPoint& a, const SkDPoint& c,
                               double t1, double t2) {
         SkDQuad quad;
         quad.set(pts);
@@ -82,8 +64,7 @@ struct SkDQuad {
 
     // utilities callable by the user from the debugger when the implementation code is linked in
     void dump() const;
-    void dumpID(int id) const;
-    void dumpInner() const;
+    void dumpComma(const char*) const;
 
 private:
 //  static double Tangent(const double* quadratic, double t);  // uncalled
diff --git a/src/pathops/SkPathOpsQuadSect.h b/src/pathops/SkPathOpsQuadSect.h
new file mode 100644 (file)
index 0000000..57f1aa0
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkQuadSpan_DEFINE
+#define SkQuadSpan_DEFINE
+
+#include "SkChunkAlloc.h"
+#include "SkPathOpsRect.h"
+#include "SkPathOpsQuad.h"
+#include "SkTArray.h"
+
+class SkIntersections;
+
+class SkQuadCoincident {
+public:
+    bool isCoincident() const {
+        return fCoincident;
+    }
+
+    void init() {
+        fCoincident = false;
+        SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
+        SkDEBUGCODE(fPerpT = SK_ScalarNaN);
+    }
+
+    void markCoincident() {
+        if (!fCoincident) {
+            fPerpT = -1;
+        }
+        fCoincident = true;
+    }
+
+    const SkDPoint& perpPt() const {
+        return fPerpPt;
+    }
+
+    double perpT() const {
+        return fPerpT;
+    }
+
+    void setPerp(const SkDQuad& quad1, double t, const SkDPoint& qPt, const SkDQuad& quad2);
+
+private:
+    SkDPoint fPerpPt;
+    double fPerpT;  // perpendicular intersection on opposite quad
+    bool fCoincident;
+};
+
+class SkQuadSect;  // used only by debug id
+
+class SkQuadSpan {
+public:
+    void init(const SkDQuad& quad);
+    void initBounds(const SkDQuad& quad);
+
+    bool contains(double t) const {
+        return !! const_cast<SkQuadSpan*>(this)->innerFind(t);
+    }
+
+    bool contains(const SkQuadSpan* span) const;
+
+    SkQuadSpan* find(double t) {
+        SkQuadSpan* result = innerFind(t);
+        SkASSERT(result);
+        return result;
+    }
+
+    bool intersects(const SkQuadSpan* span) const;
+
+    const SkQuadSpan* next() const {
+        return fNext;
+    }
+
+    void reset() {
+        fBounded.reset();
+    }
+
+    bool split(SkQuadSpan* work) {
+        return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+    }
+
+    bool splitAt(SkQuadSpan* work, double t);
+    bool tightBoundsIntersects(const SkQuadSpan* span) const;
+
+    // implementation is for testing only
+    void dump() const;
+
+private:
+    bool hullIntersects(const SkDQuad& q2) const;
+    SkQuadSpan* innerFind(double t);
+    bool linearIntersects(const SkDQuad& q2) const;
+
+    // implementation is for testing only
+#if DEBUG_BINARY_QUAD
+    int debugID(const SkQuadSect* ) const { return fDebugID; }
+#else
+    int debugID(const SkQuadSect* ) const;
+#endif
+    void dump(const SkQuadSect* ) const;
+    void dumpID(const SkQuadSect* ) const;
+
+#if DEBUG_BINARY_QUAD
+    void validate() const;
+#endif
+
+    SkDQuad fPart;
+    SkQuadCoincident fCoinStart;
+    SkQuadCoincident fCoinEnd;
+    SkSTArray<4, SkQuadSpan*, true> fBounded;
+    SkQuadSpan* fPrev;
+    SkQuadSpan* fNext;
+    SkDRect fBounds;
+    double fStartT;
+    double fEndT;
+    double fBoundsMax;
+    bool fCollapsed;
+    bool fHasPerp;
+    mutable bool fIsLinear;
+#if DEBUG_BINARY_QUAD
+    int fDebugID;
+    bool fDebugDeleted;
+#endif
+    friend class SkQuadSect;
+};
+
+class SkQuadSect {
+public:
+    SkQuadSect(const SkDQuad& quad PATH_OPS_DEBUG_PARAMS(int id));
+    static void BinarySearch(SkQuadSect* sect1, SkQuadSect* sect2, SkIntersections* intersections);
+
+    // for testing only
+    void dumpQuads() const;
+private:
+    SkQuadSpan* addOne();
+    bool binarySearchCoin(const SkQuadSect& , double tStart, double tStep, double* t, double* oppT);
+    SkQuadSpan* boundsMax() const;
+    void coincidentCheck(SkQuadSect* sect2);
+    bool intersects(const SkQuadSpan* span, const SkQuadSect* opp, const SkQuadSpan* oppSpan) const;
+    void onCurveCheck(SkQuadSect* sect2, SkQuadSpan* first, SkQuadSpan* last);
+    void recoverCollapsed();
+    void removeSpan(SkQuadSpan* span);
+    void removeOne(const SkQuadSpan* test, SkQuadSpan* span);
+    void removeSpans(SkQuadSpan* span, SkQuadSect* opp);
+    void setPerp(const SkDQuad& opp, SkQuadSpan* first, SkQuadSpan* last);
+    const SkQuadSpan* tail() const;
+    void trim(SkQuadSpan* span, SkQuadSect* opp);
+
+    // for testing only
+    void dump() const;
+    void dumpBoth(const SkQuadSect& opp) const;
+    void dumpBoth(const SkQuadSect* opp) const;
+
+#if DEBUG_BINARY_QUAD
+    int debugID() const { return fDebugID; }
+    void validate() const;
+#else
+    int debugID() const { return 0; }
+#endif
+    const SkDQuad& fQuad;
+    SkChunkAlloc fHeap;
+    SkQuadSpan* fHead;
+    SkQuadSpan* fDeleted;
+    int fActiveCount;
+#if DEBUG_BINARY_QUAD
+    int fDebugID;
+    int fDebugCount;
+    int fDebugAllocatedCount;
+#endif
+    friend class SkQuadSpan;  // only used by debug id
+};
+
+#endif
index 5dd3d8def5d0c2d295408450d2d22aeb8f40bf48..2ceed32900a3f3b97c329696e854e5b0aca735f0 100644 (file)
@@ -9,6 +9,11 @@
 #include "SkPathOpsQuad.h"
 #include "SkPathOpsRect.h"
 
+void SkDRect::setBounds(const SkDLine& line) {
+    set(line[0]);
+    add(line[1]);
+}
+
 void SkDRect::setBounds(const SkDQuad& quad) {
     set(quad[0]);
     add(quad[2]);
@@ -25,6 +30,13 @@ void SkDRect::setBounds(const SkDQuad& quad) {
     }
 }
 
+void SkDRect::setRawBounds(const SkDQuad& quad) {
+    set(quad[0]);
+    for (int x = 1; x < 3; ++x) {
+        add(quad[x]);
+    }
+}
+
 static bool is_bounded_by_end_points(double a, double b, double c, double d) {
     return between(a, b, d) && between(a, c, d);
 }
@@ -44,3 +56,10 @@ void SkDRect::setBounds(const SkDCubic& c) {
         add(c.ptAtT(tValues[x]));
     }
 }
+
+void SkDRect::setRawBounds(const SkDCubic& cubic) {
+    set(cubic[0]);
+    for (int x = 1; x < 4; ++x) {
+        add(cubic[x]);
+    }
+}
index 2b37a5f0980a4fd3c26ae8292844a720ce2cbe09..2c47f43b88798d995c2258774105e9cc2afad538 100644 (file)
@@ -13,10 +13,18 @@ struct SkDRect {
     double fLeft, fTop, fRight, fBottom;
 
     void add(const SkDPoint& pt) {
-        fLeft = SkTMin(fLeft, pt.fX);
-        fTop = SkTMin(fTop, pt.fY);
-        fRight = SkTMax(fRight, pt.fX);
-        fBottom = SkTMax(fBottom, pt.fY);
+        if (fLeft > pt.fX) {
+            fLeft = pt.fX;
+        }
+        if (fTop > pt.fY) {
+            fTop = pt.fY;
+        }
+        if (fRight < pt.fX) {
+            fRight = pt.fX;
+        }
+        if (fBottom < pt.fY) {
+            fBottom = pt.fY;
+        }
     }
 
     bool contains(const SkDPoint& pt) const {
@@ -24,15 +32,12 @@ struct SkDRect {
                 && approximately_between(fTop, pt.fY, fBottom);
     }
 
-    bool intersects(const SkDRect& r) const {
-        if (fLeft > fRight) {
-            SkDebugf("!");
-        }
+    bool intersects(SkDRect* r) const {
         SkASSERT(fLeft <= fRight);
         SkASSERT(fTop <= fBottom);
-        SkASSERT(r.fLeft <= r.fRight);
-        SkASSERT(r.fTop <= r.fBottom);
-        return r.fLeft <= fRight && fLeft <= r.fRight && r.fTop <= fBottom && fTop <= r.fBottom;
+        SkASSERT(r->fLeft <= r->fRight);
+        SkASSERT(r->fTop <= r->fBottom);
+        return r->fLeft <= fRight && fLeft <= r->fRight && r->fTop <= fBottom && fTop <= r->fBottom;
     }
 
     void set(const SkDPoint& pt) {
@@ -48,8 +53,11 @@ struct SkDRect {
         return fBottom - fTop;
     }
 
+    void setBounds(const SkDLine&);
     void setBounds(const SkDCubic&);
     void setBounds(const SkDQuad&);
+    void setRawBounds(const SkDCubic&);
+    void setRawBounds(const SkDQuad&);
 };
 
 #endif
index 7a234ecb01ecb61a751b49368ca6391b90aac61f..57090ac4238bd658cf8bdc60c4cbee8113679ad4 100644 (file)
@@ -5,13 +5,11 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
-        SkChunkAlloc* allocator) {
+static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
     bool firstContour = true;
     bool unsortable = false;
     bool topUnsortable = false;
@@ -19,24 +17,15 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
     SkPoint lastTopLeft;
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        SkOpSpanBase* start;
-        SkOpSpanBase* end;
+        int index, endIndex;
         bool topDone;
         bool onlyVertical = false;
         lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
-                &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
-                allocator);
+        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
+                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
         if (!current) {
             if ((!topUnsortable || firstPass) && !topDone) {
                 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
-                if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
-                    if (firstPass) {
-                        firstPass = false;
-                    } else {
-                        break;
-                    }
-                }
                 topLeft.fX = topLeft.fY = SK_ScalarMin;
                 continue;
             }
@@ -45,66 +34,62 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
             break;
         }
         firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpanBase*> chase;
+        SkTDArray<SkOpSpan*> chase;
         do {
-            if (current->activeWinding(start, end)) {
+            if (current->activeWinding(index, endIndex)) {
                 do {
                     if (!unsortable && current->done()) {
                           break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    SkOpSpanBase* nextStart = start;
-                    SkOpSpanBase* nextEnd = end;
+                    int nextStart = index;
+                    int nextEnd = endIndex;
                     SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
                             &unsortable);
                     if (!next) {
                         if (!unsortable && simple->hasMove()
                                 && current->verb() != SkPath::kLine_Verb
                                 && !simple->isClosed()) {
-                            current->addCurveTo(start, end, simple, true);
-                    #if DEBUG_ACTIVE_SPANS
-                            if (!simple->isClosed()) {
-                                DebugShowActiveSpans(contourList);
-                            }
-                    #endif
+                            current->addCurveTo(index, endIndex, simple, true);
+                            SkASSERT(simple->isClosed());
                         }
                         break;
                     }
         #if DEBUG_FLOW
             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                    current->debugID(), start->pt().fX, start->pt().fY,
-                    end->pt().fX, end->pt().fY);
+                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
+                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
         #endif
-                    current->addCurveTo(start, end, simple, true);
+                    current->addCurveTo(index, endIndex, simple, true);
                     current = next;
-                    start = nextStart;
-                    end = nextEnd;
-                } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
-                if (current->activeWinding(start, end) && !simple->isClosed()) {
-                    SkOpSpan* spanStart = start->starter(end);
-                    if (!spanStart->done()) {
-                        current->addCurveTo(start, end, simple, true);
-                        current->markDone(spanStart);
+                    index = nextStart;
+                    endIndex = nextEnd;
+                } while (!simple->isClosed() && (!unsortable
+                        || !current->done(SkMin32(index, endIndex))));
+                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
+//                    SkASSERT(unsortable || simple->isEmpty());
+                    int min = SkMin32(index, endIndex);
+                    if (!current->done(min)) {
+                        current->addCurveTo(index, endIndex, simple, true);
+                        current->markDoneUnary(min);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpanBase* last = current->markAndChaseDone(start, end);
-                if (last && !last->chased()) {
-                    last->setChased(true);
+                SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
+                if (last && !last->fChased && !last->fLoop) {
+                    last->fChased = true;
                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
                     // assert that last isn't already in array
                     *chase.append() = last;
 #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
-                    if (!last->final()) {
-                         SkDebugf(" windSum=%d", last->upCast()->windSum());
-                    }
-                    SkDebugf("\n");
+                    SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
+                            last->fSmall);
 #endif
                 }
             }
-            current = FindChase(&chase, &start, &end);
+            current = FindChase(&chase, &index, &endIndex);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -117,11 +102,9 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
 }
 
 // returns true if all edges were processed
-static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
-        SkChunkAlloc* allocator) {
+static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
     SkOpSegment* current;
-    SkOpSpanBase* start;
-    SkOpSpanBase* end;
+    int start, end;
     bool unsortable = false;
     bool closable = true;
     while ((current = FindUndone(contourList, &start, &end))) {
@@ -132,38 +115,34 @@ static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simpl
             }
     #endif
             SkASSERT(unsortable || !current->done());
-            SkOpSpanBase* nextStart = start;
-            SkOpSpanBase* nextEnd = end;
+            int nextStart = start;
+            int nextEnd = end;
             SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
             if (!next) {
                 if (!unsortable && simple->hasMove()
                         && current->verb() != SkPath::kLine_Verb
                         && !simple->isClosed()) {
                     current->addCurveTo(start, end, simple, true);
-            #if DEBUG_ACTIVE_SPANS
-                    if (!simple->isClosed()) {
-                        DebugShowActiveSpans(contourList);
-                    }
-            #endif
+                    SkASSERT(simple->isClosed());
                 }
                 break;
             }
         #if DEBUG_FLOW
             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                    current->debugID(), start->pt().fX, start->pt().fY,
-                    end->pt().fX, end->pt().fY);
+                    current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
+                    current->xyAtT(end).fX, current->xyAtT(end).fY);
         #endif
             current->addCurveTo(start, end, simple, true);
             current = next;
             start = nextStart;
             end = nextEnd;
-        } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
+        } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
         if (!simple->isClosed()) {
             SkASSERT(unsortable);
-            SkOpSpan* spanStart = start->starter(end);
-            if (!spanStart->done()) {
+            int min = SkMin32(start, end);
+            if (!current->done(min)) {
                 current->addCurveTo(start, end, simple, true);
-                current->markDone(spanStart);
+                current->markDone(min, 1);
             }
             closable = false;
         }
@@ -177,68 +156,52 @@ static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simpl
 
 // FIXME : add this as a member of SkPath
 bool Simplify(const SkPath& path, SkPath* result) {
+#if DEBUG_SORT || DEBUG_SWAP_TOP
+    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
+#endif
     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
     SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
             : SkPath::kEvenOdd_FillType;
-    if (path.isConvex()) {
-        if (result != &path) {
-            *result = path;
-        }
-        result->setFillType(fillType);
-        return true;
-    }
+
     // turn path into list of segments
-    SkOpCoincidence coincidence;
-    SkOpContour contour;
-    SkOpGlobalState globalState(&coincidence  PATH_OPS_DEBUG_PARAMS(&contour));
-#if DEBUG_SORT || DEBUG_SWAP_TOP
-    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
-#endif
-    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
-    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
-    if (!builder.finish(&allocator)) {
+    SkTArray<SkOpContour> contours;
+    SkOpEdgeBuilder builder(path, contours);
+    if (!builder.finish()) {
         return false;
     }
-#if !FORCE_RELEASE
-    contour.dumpSegments((SkPathOp) -1);
-#endif
+    SkTArray<SkOpContour*, true> contourList;
+    MakeContourList(contours, contourList, false, false);
+    SkOpContour** currentPtr = contourList.begin();
     result->reset();
     result->setFillType(fillType);
-    SkTDArray<SkOpContour* > contourList;
-    MakeContourList(&contour, contourList, false, false);
-    SkOpContour** currentPtr = contourList.begin();
     if (!currentPtr) {
         return true;
     }
-    if ((*currentPtr)->count() == 0) {
-        SkASSERT((*currentPtr)->next() == NULL);
-        return true;
-    }
-    SkOpContour** listEnd2 = contourList.end();
+    SkOpContour** listEnd = contourList.end();
     // find all intersections between segments
     do {
         SkOpContour** nextPtr = currentPtr;
         SkOpContour* current = *currentPtr++;
+        if (current->containsCubics()) {
+            AddSelfIntersectTs(current);
+        }
         SkOpContour* next;
         do {
             next = *nextPtr++;
-        } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd2);
-    } while (currentPtr != listEnd2);
-#if DEBUG_VALIDATE
-    globalState.setPhase(SkOpGlobalState::kWalking);
-#endif
-    if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
+        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
+    } while (currentPtr != listEnd);
+    if (!HandleCoincidence(&contourList, 0)) {
         return false;
     }
     // construct closed contours
-    SkPathWriter wrapper(*result);
-    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
-                : !bridgeXor(contourList, &wrapper, &allocator))
+    SkPathWriter simple(*result);
+    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
+                : !bridgeXor(contourList, &simple))
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
         SkPathWriter assembled(temp);
-        Assemble(wrapper, &assembled);
+        Assemble(simple, &assembled);
         *result = *assembled.nativePath();
         result->setFillType(fillType);
     }
index 10a84a384346b73ad4708500811b0c3c6baffb08..0b3ddd7d0d5db4de34915c40710982afd5d38de3 100644 (file)
@@ -7,9 +7,9 @@
 
 #include "SkPathOpsTSect.h"
 
-int SkIntersections::intersect(const SkDCubic& cubic1, const SkDCubic& cubic2) {
-    SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
-    SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+int SkIntersections::intersectB(const SkDCubic& cubic1, const SkDCubic& cubic2) {
+    SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_PARAMS(1));
+    SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_PARAMS(2));
     SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
index 06b5f2f8e9689c6784624395232d841ae0a0d7f7..46ce5cfdbb7d729b89fd24bb9b2b9f616eb53939 100644 (file)
@@ -7,9 +7,9 @@
 
 #include "SkPathOpsTSect.h"
 
-int SkIntersections::intersect(const SkDQuad& quad1, const SkDQuad& quad2) {
-    SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
-    SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+int SkIntersections::intersectB(const SkDQuad& quad1, const SkDQuad& quad2) {
+    SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_PARAMS(1));
+    SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_PARAMS(2));
     SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
index 5c76da7e83572a40047b66b771f650d2038aa6bd..4e7d3b1795c765ae964391e88990d1d1d548dae3 100644 (file)
@@ -6,25 +6,15 @@
  */
 
 #include "SkChunkAlloc.h"
-#include "SkPathOpsBounds.h"
 #include "SkPathOpsRect.h"
 #include "SkPathOpsQuad.h"
 #include "SkIntersections.h"
-#include "SkTSort.h"
+#include "SkTArray.h"
 
 /* TCurve is either SkDQuadratic or SkDCubic */
 template<typename TCurve>
 class SkTCoincident {
 public:
-    SkTCoincident()
-        : fCoincident(false) {
-    }
-
-    void clear() {
-        fPerpT = -1;
-        fCoincident = false;
-    }
-
     bool isCoincident() const {
         return fCoincident;
     }
@@ -64,73 +54,41 @@ template<typename TCurve> class SkTSect;
 template<typename TCurve>
 class SkTSpan {
 public:
-    void addBounded(SkTSpan* );
+    void init(const TCurve& );
+    void initBounds(const TCurve& );
+
     double closestBoundedT(const SkDPoint& pt) const;
-    bool contains(double t) const;
 
-    const SkTSect<TCurve>* debugOpp() const;
-    const SkTSpan* debugSpan(int ) const;
-    const SkTSpan* debugT(double t) const;
-#ifdef SK_DEBUG
-    bool debugIsBefore(const SkTSpan* span) const;
-#endif
-    void dump() const;
-    void dumpBounds(int id) const;
+    bool contains(double t) const {
+        return !! const_cast<SkTSpan*>(this)->innerFind(t);
+    }
+
+    bool contains(const SkTSpan* span) const;
 
     double endT() const {
         return fEndT;
     }
 
-    SkTSpan* findOppSpan(const SkTSpan* opp) const;
-
-    SkTSpan* findOppT(double t) const {
-        SkTSpan* result = oppT(t);
+    SkTSpan* find(double t) {
+        SkTSpan* result = innerFind(t);
         SkASSERT(result);
         return result;
     }
 
-    bool hasOppT(double t) const {
-        return SkToBool(oppT(t));
-    }
-
-    int hullsIntersect(SkTSpan* span, bool* start, bool* oppStart);
-    void init(const TCurve& );
-    void initBounds(const TCurve& );
-
-    bool isBounded() const {
-        return fBounded.count() > 0;
-    }
-
-    bool linearsIntersect(SkTSpan* span);
-    double linearT(const SkDPoint& ) const;
-
-    void markCoincident() {
-        fCoinStart.markCoincident();
-        fCoinEnd.markCoincident();
-    }
+    bool intersects(const SkTSpan* span, bool* check);
 
     const SkTSpan* next() const {
         return fNext;
     }
 
-    bool onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart, bool* ptsInCommon);
-
     const TCurve& part() const {
         return fPart;
     }
 
-    bool removeAllBounded();
-    bool removeBounded(const SkTSpan* opp);
-
     void reset() {
         fBounded.reset();
     }
 
-    void resetBounds(const TCurve& curve) {
-        fIsLinear = fIsLine = false;
-        initBounds(curve);
-    }
-
     bool split(SkTSpan* work) {
         return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
     }
@@ -141,23 +99,29 @@ public:
         return fStartT;
     }
 
-private:
+    bool tightBoundsIntersects(const SkTSpan* span) const;
 
     // implementation is for testing only
-    int debugID() const {
-        return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
+    void dump() const {
+        dump(NULL);
     }
 
-    void dumpID() const;
+private:
+    SkTSpan* innerFind(double t);
+    bool linearIntersects(const TCurve& ) const;
 
-    int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart);
-    int linearIntersects(const TCurve& ) const;
-    SkTSpan* oppT(double t) const;
+    // implementation is for testing only
+#if DEBUG_T_SECT
+    int debugID(const SkTSect<TCurve>* ) const { return fDebugID; }
+#else
+    int debugID(const SkTSect<TCurve>* ) const;
+#endif
+    void dump(const SkTSect<TCurve>* ) const;
+    void dumpID(const SkTSect<TCurve>* ) const;
 
+#if DEBUG_T_SECT
     void validate() const;
-    void validateBounded() const;
-    void validatePerpT(double oppT) const;
-    void validatePerpPt(double t, const SkDPoint& ) const;
+#endif
 
     TCurve fPart;
     SkTCoincident<TCurve> fCoinStart;
@@ -172,33 +136,23 @@ private:
     bool fCollapsed;
     bool fHasPerp;
     bool fIsLinear;
-    bool fIsLine;
-    bool fDeleted;
-    PATH_OPS_DEBUG_CODE(SkTSect<TCurve>* fDebugSect);
-    PATH_OPS_DEBUG_T_SECT_CODE(int fID);
+#if DEBUG_T_SECT
+    int fDebugID;
+    bool fDebugDeleted;
+#endif
     friend class SkTSect<TCurve>;
 };
 
 template<typename TCurve>
 class SkTSect {
 public:
-    SkTSect(const TCurve& c  PATH_OPS_DEBUG_T_SECT_PARAMS(int id));
+    SkTSect(const TCurve& c  PATH_OPS_DEBUG_PARAMS(int id));
     static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections);
 
     // for testing only
-    bool debugHasBounded(const SkTSpan<TCurve>* ) const;
-
-    const SkTSect* debugOpp() const {
-        return PATH_OPS_DEBUG_RELEASE(fOppSect, NULL);
-    }
-
-    const SkTSpan<TCurve>* debugSpan(int id) const;
-    const SkTSpan<TCurve>* debugT(double t) const;
     void dump() const;
-    void dumpBoth(SkTSect* ) const;
-    void dumpBounds(int id) const;
-    void dumpCoin() const;
-    void dumpCoinCurves() const;
+    void dumpBoth(const SkTSect& opp) const;
+    void dumpBoth(const SkTSect* opp) const;
     void dumpCurves() const;
 
 private:
@@ -209,72 +163,36 @@ private:
         kOneS2Set = 8
     };
 
-    SkTSpan<TCurve>* addFollowing(SkTSpan<TCurve>* prior);
-    void addForPerp(SkTSpan<TCurve>* span, double t);
     SkTSpan<TCurve>* addOne();
-    
-    SkTSpan<TCurve>* addSplitAt(SkTSpan<TCurve>* span, double t) {
-        SkTSpan<TCurve>* result = this->addOne();
-        result->splitAt(span, t);
-        result->initBounds(fCurve);
-        span->initBounds(fCurve);
-        return result;
-    }
-
-    bool binarySearchCoin(SkTSect* , double tStart, double tStep, double* t, double* oppT);
+    bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
     SkTSpan<TCurve>* boundsMax() const;
     void coincidentCheck(SkTSect* sect2);
-    bool coincidentHasT(double t);
-    void computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
-    int countConsecutiveSpans(SkTSpan<TCurve>* first, SkTSpan<TCurve>** last) const;
-
-    int debugID() const {
-        return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
-    }
-
-    void deleteEmptySpans();
-    void dumpCommon(const SkTSpan<TCurve>* ) const;
-    void dumpCommonCurves(const SkTSpan<TCurve>* ) const;
     static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
-    SkTSpan<TCurve>* extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
-                                       SkTSpan<TCurve>* last);
-    SkTSpan<TCurve>* findCoincidentRun(SkTSpan<TCurve>* first, SkTSpan<TCurve>** lastPtr,
-                                       const SkTSect* sect2);
-    int intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
-                   SkTSpan<TCurve>* oppSpan, int* oppResult) const;
-    int linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
-                       const SkTSpan<TCurve>* oppSpan, SkIntersections* ) const;
-    void markSpanGone(SkTSpan<TCurve>* span);
-    bool matchedDirection(double t, const SkTSect* sect2, double t2) const;
-    void matchedDirCheck(double t, const SkTSect* sect2, double t2,
-                         bool* calcMatched, bool* oppMatched) const;
-    void mergeCoincidence(SkTSect* sect2);
-    SkTSpan<TCurve>* prev(SkTSpan<TCurve>* ) const;
-    void removeByPerpendicular(SkTSect* opp);
+    bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+            const SkTSpan<TCurve>* oppSpan) const;
+    void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
     void recoverCollapsed();
-    void removeCoincident(SkTSpan<TCurve>* span, bool isBetween);
-    void removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span, SkTSect* opp);
     void removeSpan(SkTSpan<TCurve>* span);
-    void removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+    void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
     void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
-    SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan);
-    SkTSpan<TCurve>* tail();
+    void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+    const SkTSpan<TCurve>* tail() const;
     void trim(SkTSpan<TCurve>* span, SkTSect* opp);
-    void unlinkSpan(SkTSpan<TCurve>* span);
-    bool updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, SkTSpan<TCurve>* oppFirst);
-    void validate() const;
-    void validateBounded() const;
 
+#if DEBUG_T_SECT
+    int debugID() const { return fDebugID; }
+    void validate() const;
+#else
+    int debugID() const { return 0; }
+#endif
     const TCurve& fCurve;
     SkChunkAlloc fHeap;
     SkTSpan<TCurve>* fHead;
-    SkTSpan<TCurve>* fCoincident;
     SkTSpan<TCurve>* fDeleted;
     int fActiveCount;
-    PATH_OPS_DEBUG_CODE(SkTSect* fOppSect);
-    PATH_OPS_DEBUG_T_SECT_CODE(int fID);
-    PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount);
 #if DEBUG_T_SECT
+    int fDebugID;
+    int fDebugCount;
     int fDebugAllocatedCount;
 #endif
     friend class SkTSpan<TCurve>;  // only used by debug id
@@ -290,8 +208,8 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
     SkIntersections i;
     int used = i.intersectRay(c2, perp);
     // only keep closest
-    if (used == 0 || used == 3) {
-        this->clear();
+    if (used == 0) {
+        fPerpT = -1;
         return;
     } 
     fPerpT = i[0][0];
@@ -305,10 +223,6 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
             fPerpPt = i.pt(1);
         }
     }
-#if DEBUG_T_SECT
-    SkDebugf("%s cPt=(%1.9g,%1.9g) %s fPerpPt=(%1.9g,%1.9g)\n", __FUNCTION__, cPt.fX, cPt.fY,
-            cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpPt.fX, fPerpPt.fY);
-#endif
     fCoincident = cPt.approximatelyEqual(fPerpPt);
 #if DEBUG_T_SECT
     if (fCoincident) {
@@ -318,55 +232,29 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
 }
 
 template<typename TCurve>
-void SkTSpan<TCurve>::addBounded(SkTSpan* span) {
-    if (this->findOppSpan(span)) {
-        return;
-    }
-    fBounded.push_back() = span;
+void SkTSpan<TCurve>::init(const TCurve& c) {
+    fPrev = fNext = NULL;
+    fIsLinear = false;
+    fStartT = 0;
+    fEndT = 1;
+    initBounds(c);
 }
 
 template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::addFollowing(SkTSpan<TCurve>* prior) {
-    SkTSpan<TCurve>* result = this->addOne();
-    result->fStartT = prior ? prior->fEndT : 0;
-    SkTSpan<TCurve>* next = prior ? prior->fNext : fHead;
-    result->fEndT = next ? next->fStartT : 1;
-    result->fPrev = prior;
-    result->fNext = next;
-    if (prior) {
-        prior->fNext = result;
-    } else {
-        fHead = result;
-    }
-    if (next) {
-        next->fPrev = result;
+void SkTSpan<TCurve>::initBounds(const TCurve& c) {
+    fPart = c.subDivide(fStartT, fEndT);
+    fBounds.setBounds(fPart);
+    fCoinStart.init();
+    fCoinEnd.init();
+    fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
+    fCollapsed = fPart.collapsed();
+    fHasPerp = false;
+#if DEBUG_T_SECT
+    fDebugDeleted = false;
+    if (fCollapsed) {
+        SkDebugf("");  // for convenient breakpoints
     }
-    result->resetBounds(fCurve);
-    return result;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::addForPerp(SkTSpan<TCurve>* span, double t) {
-    if (!span->hasOppT(t)) {
-        SkTSpan<TCurve>* priorSpan;
-        SkTSpan<TCurve>* opp = this->spanAtT(t, &priorSpan);
-        if (!opp) {
-            opp = this->addFollowing(priorSpan);
-#if DEBUG_PERP
-            SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan->debugID(), t,
-                    opp->debugID());
-#endif
-        }
-#if DEBUG_PERP
-        opp->dump(); SkDebugf("\n");
-        SkDebugf("%s fBounded.push_back span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(),
-                opp->debugID());
 #endif
-        opp->fBounded.push_back(span);
-        span->fBounded.push_back(opp);
-    }
-    this->validate();
-    span->validatePerpT(t);
 }
 
 template<typename TCurve>
@@ -391,143 +279,55 @@ double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
     return result;
 }
 
-#ifdef SK_DEBUG
 template<typename TCurve>
-bool SkTSpan<TCurve>::debugIsBefore(const SkTSpan* span) const {
-    const SkTSpan* work = this;
-    do {
-        if (span == work) {
+bool SkTSpan<TCurve>::contains(const SkTSpan* span) const {
+    int count = fBounded.count();
+    for (int index = 0; index < count; ++index) {
+        const SkTSpan* test = fBounded[index];
+        if (span == test) {
             return true;
         }
-    } while ((work = work->fNext));
+    }
     return false;
 }
-#endif
 
 template<typename TCurve>
-bool SkTSpan<TCurve>::contains(double t) const {
-    const SkTSpan* work = this;
+SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
+    SkTSpan* work = this;
     do {
         if (between(work->fStartT, t, work->fEndT)) {
-            return true;
+            return work;
         }
     } while ((work = work->fNext));
-    return false;
-}
-
-template<typename TCurve>
-const SkTSect<TCurve>* SkTSpan<TCurve>::debugOpp() const {
-    return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugOpp(), NULL);
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::findOppSpan(const SkTSpan* opp) const {
-    int count = fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        SkTSpan* test = fBounded[index];
-        if (opp == test) {
-            return test;
-        }
-    }
     return NULL;
 }
 
-// returns 0 if no hull intersection
-//         1 if hulls intersect
-//         2 if hulls only share a common endpoint
-//        -1 if linear and further checking is required
-template<typename TCurve>
-int SkTSpan<TCurve>::hullCheck(const SkTSpan* opp, bool* start, bool* oppStart) {
-    if (fIsLinear) {
-        return -1;
-    }
-    bool ptsInCommon;
-    if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
-        SkASSERT(ptsInCommon);
-        return 2;
-    }
-    bool linear;
-    if (fPart.hullIntersects(opp->fPart, &linear)) {
-        if (!linear) {  // check set true if linear
-            return 1;
-        }
-        fIsLinear = true;
-        fIsLine = fPart.controlsInside();
-        return ptsInCommon ? 2 : -1;
-    } else {  // hull is not linear; check set true if intersected at the end points
-        return ((int) ptsInCommon) << 1;  // 0 or 2
-    }
-    return 0;
-}
-
 // OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
 // use line intersection to guess a better split than 0.5
 // OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
 template<typename TCurve>
-int SkTSpan<TCurve>::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) {
-    if (!fBounds.intersects(opp->fBounds)) {
-        return 0;
-    }
-    int hullSect = this->hullCheck(opp, start, oppStart);
-    if (hullSect >= 0) {
-        return hullSect;
+bool SkTSpan<TCurve>::intersects(const SkTSpan* span, bool* check) {
+    if (!fBounds.intersects(span->fBounds)) {
+        *check = false;  // no need to check to see if the bounds have end points in common
+        return false;
     }
-    hullSect = opp->hullCheck(this, oppStart, start);
-    if (hullSect >= 0) {
-        return hullSect;
+    if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
+        if (!*check) {
+            return true;
+        }
+        fIsLinear = true;
     }
-    return -1;
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::init(const TCurve& c) {
-    fPrev = fNext = NULL;
-    fStartT = 0;
-    fEndT = 1;
-    resetBounds(c);
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::initBounds(const TCurve& c) {
-    fPart = c.subDivide(fStartT, fEndT);
-    fBounds.setBounds(fPart);
-    fCoinStart.init();
-    fCoinEnd.init();
-    fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
-    fCollapsed = fPart.collapsed();
-    fHasPerp = false;
-    fDeleted = false;
-#if DEBUG_T_SECT
-    if (fCollapsed) {
-        SkDebugf("");  // for convenient breakpoints
+    if (fIsLinear) {
+        *check = false;
+        return linearIntersects(span->fPart);
     }
-#endif
+    return *check; 
 }
 
 template<typename TCurve>
-bool SkTSpan<TCurve>::linearsIntersect(SkTSpan* span) {
-    int result = this->linearIntersects(span->fPart);
-    if (result <= 1) {
-        return SkToBool(result);
-    }
-    SkASSERT(span->fIsLinear);
-    result = span->linearIntersects(this->fPart);
-//    SkASSERT(result <= 1);
-    return SkToBool(result);
-}
-
-template<typename TCurve>
-double SkTSpan<TCurve>::linearT(const SkDPoint& pt) const {
-    SkDVector len = fPart[TCurve::kPointLast] - fPart[0];
-    return fabs(len.fX) > fabs(len.fY)
-            ? (pt.fX - fPart[0].fX) / len.fX
-            : (pt.fY - fPart[0].fY) / len.fY;
-}
-
-template<typename TCurve>
-int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
     // looks like q1 is near-linear
-    int start = 0, end = TCurve::kPointLast;  // the outside points are usually the extremes
+    int start = 0, end = TCurve::kPointCount - 1;  // the outside points are usually the extremes
     if (!fPart.controlsInside()) {
         double dist = 0;  // if there's any question, compute distance to find best outsiders
         for (int outer = 0; outer < TCurve::kPointCount - 1; ++outer) {
@@ -547,116 +347,20 @@ int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
     double origY = fPart[start].fY;
     double adj = fPart[end].fX - origX;
     double opp = fPart[end].fY - origY;
-    double maxPart = SkTMax(fabs(adj), fabs(opp));
-    double sign = 0;  // initialization to shut up warning in release build
+    double sign;
     for (int n = 0; n < TCurve::kPointCount; ++n) {
-        double dx = q2[n].fY - origY;
-        double dy = q2[n].fX - origX;
-        double maxVal = SkTMax(maxPart, SkTMax(fabs(dx), fabs(dy)));
         double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
-        if (precisely_zero_when_compared_to(test, maxVal)) {
-            return 1;
-        }
-        if (approximately_zero_when_compared_to(test, maxVal)) {
-            return 3;
+        if (precisely_zero(test)) {
+            return true;
         }
         if (n == 0) {
             sign = test;
             continue;
         }
         if (test * sign < 0) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart,
-        bool* ptsInCommon) {
-    if (opp->fPart[0] == fPart[0]) {
-        *start = *oppStart = true;
-    } else if (opp->fPart[0] == fPart[TCurve::kPointLast]) {
-        *start = false;
-        *oppStart = true;
-    } else if (opp->fPart[TCurve::kPointLast] == fPart[0]) {
-        *start = true;
-        *oppStart = false;
-    } else if (opp->fPart[TCurve::kPointLast] == fPart[TCurve::kPointLast]) {
-        *start = *oppStart = false;
-    } else {
-        *ptsInCommon = false;
-        return false;
-    }
-    *ptsInCommon = true;
-    const SkDPoint* o1Pts[TCurve::kPointCount - 1], * o2Pts[TCurve::kPointCount - 1];
-    int baseIndex = *start ? 0 : TCurve::kPointLast;
-    fPart.otherPts(baseIndex, o1Pts);
-    opp->fPart.otherPts(*oppStart ? 0 : TCurve::kPointLast, o2Pts);
-    const SkDPoint& base = fPart[baseIndex];
-    for (int o1 = 0; o1 < (int) SK_ARRAY_COUNT(o1Pts); ++o1) {
-        SkDVector v1 = *o1Pts[o1] - base;
-        for (int o2 = 0; o2 < (int) SK_ARRAY_COUNT(o2Pts); ++o2) {
-            SkDVector v2 = *o2Pts[o2] - base;
-            if (v2.dot(v1) >= 0) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::oppT(double t) const {
-    int count = fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        SkTSpan* test = fBounded[index];
-        if (between(test->fStartT, t, test->fEndT)) {
-            return test;
-        }
-    }
-    return NULL;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::removeAllBounded() {
-    bool deleteSpan = false;
-    int count = fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        SkTSpan* opp = fBounded[index];
-        deleteSpan |= opp->removeBounded(this);
-    }
-    return deleteSpan;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::removeBounded(const SkTSpan* opp) {
-    int count = fBounded.count();
-    if (fHasPerp) {
-        bool foundStart = false;
-        bool foundEnd = false;
-        for (int index = 0; index < count; ++index) {
-            const SkTSpan* test = fBounded[index];
-            if (opp == test) {
-                continue;
-            }
-            foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
-            foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
-        }
-        if (!foundStart || !foundEnd) {
-            fHasPerp = false;
-            fCoinStart.init();
-            fCoinEnd.init();
-        }
-    }
-    for (int index = 0; index < count; ++index) {
-        if (opp == fBounded[index]) {
-            fBounded.removeShuffle(index);
-            SkASSERT((count == 1) == (fBounded.count() == 0));
-            return count == 1;
+            return true;
         }
     }
-    SkASSERT(0);
     return false;
 }
 
@@ -676,8 +380,6 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
     fPrev = work;
     fNext = work->fNext;
     fIsLinear = work->fIsLinear;
-    fIsLine = work->fIsLine;
-
     work->fNext = this;
     if (fNext) {
         fNext->fPrev = this;
@@ -691,74 +393,102 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
 }
 
 template<typename TCurve>
-void SkTSpan<TCurve>::validate() const {
-#if DEBUG_T_SECT
-    SkASSERT(fNext == NULL || fNext != fPrev);
-    SkASSERT(fNext == NULL || this == fNext->fPrev);
-    SkASSERT(fPrev == NULL || this == fPrev->fNext);
-    SkASSERT(fBounds.width() || fBounds.height() || fCollapsed);
-    SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
-    SkASSERT(0 <= fStartT);
-    SkASSERT(fEndT <= 1);
-    SkASSERT(fStartT <= fEndT);
-    SkASSERT(fBounded.count() > 0);
-    this->validateBounded();
-    if (fHasPerp) {
-        if (fCoinStart.isCoincident()) {
-            validatePerpT(fCoinStart.perpT());
-            validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
+bool SkTSpan<TCurve>::tightBoundsIntersects(const SkTSpan* span) const {
+    // skew all to an axis
+    SkDVector v2_0 = fPart[TCurve::kPointLast] - fPart[0];
+    bool skewToXAxis = fabs(v2_0.fX) > fabs(v2_0.fY);
+    double ratio = skewToXAxis ? v2_0.fY / v2_0.fX : v2_0.fX / v2_0.fY;
+    TCurve r1 = fPart;
+    if (skewToXAxis) {
+        r1[1].fY -= (fPart[1].fX - r1[0].fX) * ratio;
+        if (TCurve::IsCubic()) {
+            r1[2].fY -= (fPart[2].fX - r1[0].fX) * ratio;
+            r1[3].fY = r1[0].fY;
+        } else {
+            r1[2].fY = r1[0].fY;
+        }
+    } else {
+        r1[1].fX -= (fPart[1].fY - r1[0].fY) * ratio;
+        if (TCurve::IsCubic()) {
+            r1[2].fX -= (fPart[2].fY - r1[0].fY) * ratio;
+            r1[3].fX = r1[0].fX;
+        } else {
+            r1[2].fX = r1[0].fX;
+        }
+    }
+    // compute the tight skewed bounds
+    SkDRect bounds;
+    bounds.setBounds(r1);
+    // see if opposite ends are within range of tight skewed bounds
+    TCurve r2 = span->fPart;
+    for (int i = 0; i < TCurve::kPointCount; i += 2) {
+        if (skewToXAxis) {
+            r2[i].fY -= (r2[i].fX - r1[0].fX) * ratio;
+            if (between(bounds.fTop, r2[i].fY, bounds.fBottom)) {
+                return true;
+            }
+        } else {
+            r2[i].fX -= (r2[i].fY - r1[0].fY) * ratio;
+            if (between(bounds.fLeft, r2[i].fX, bounds.fRight)) {
+                return true;
+            }
+        }
+    }
+    // see if opposite ends are on either side of tight skewed bounds
+    if ((skewToXAxis ? (r2[0].fY - r1[0].fY) * (r2[TCurve::kPointLast].fY - r1[0].fY)
+                        : (r2[0].fX - r1[0].fX) * (r2[TCurve::kPointLast].fX - r1[0].fX)) < 0) {
+        return true;
+    }
+    // compute opposite tight skewed bounds
+    if (skewToXAxis) {
+        r2[1].fY -= (r2[1].fX - r1[0].fX) * ratio;
+        if (TCurve::IsCubic()) {
+            r2[2].fY -= (r2[2].fX - r1[0].fX) * ratio;
         }
-        if (fCoinEnd.isCoincident()) {
-            validatePerpT(fCoinEnd.perpT());
-            validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
+    } else {
+        r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
+        if (TCurve::IsCubic()) {
+            r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
         }
     }
-#endif
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::validateBounded() const {
-#if DEBUG_VALIDATE
-    for (int index = 0; index < fBounded.count(); ++index) {
-        const SkTSpan* overlap = fBounded[index];
-        SkASSERT(!overlap->fDeleted);
-        SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
-        SkASSERT(overlap->findOppSpan(this));
+    SkDRect sBounds;
+    sBounds.setBounds(r2);
+    // see if tight bounds overlap
+    if (skewToXAxis) {
+        return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom;  
+    } else {
+        return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight;  
     }
-#endif
 }
 
+#if DEBUG_T_SECT
 template<typename TCurve>
-void SkTSpan<TCurve>::validatePerpT(double oppT) const {
-#if DEBUG_VALIDATE
+void SkTSpan<TCurve>::validate() const {
+    SkASSERT(fNext == NULL || fNext != fPrev);
+    SkASSERT(fNext == NULL || this == fNext->fPrev);
+    SkASSERT(fBounds.width() || fBounds.height());
+    SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
+    SkASSERT(0 <= fStartT);
+    SkASSERT(fEndT <= 1);
+    SkASSERT(fStartT < fEndT);
+    SkASSERT(fBounded.count() > 0);
     for (int index = 0; index < fBounded.count(); ++index) {
         const SkTSpan* overlap = fBounded[index];
-        if (between(overlap->fStartT, oppT, overlap->fEndT)) {
-            return;
-        }
+        SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
+        SkASSERT(overlap->contains(this));
     }
-    SkASSERT(0);
-#endif
 }
-
-template<typename TCurve>
-void SkTSpan<TCurve>::validatePerpPt(double t, const SkDPoint& pt) const {
-#if DEBUG_T_SECT
-    PATH_OPS_DEBUG_CODE(SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt));
 #endif
-}
-
 
 template<typename TCurve>
-SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
+SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
     : fCurve(c)
     , fHeap(sizeof(SkTSpan<TCurve>) * 4)
-    , fCoincident(NULL)
     , fDeleted(NULL)
     , fActiveCount(0)
-    PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
-    PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
-    PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0))
+    PATH_OPS_DEBUG_PARAMS(fDebugID(id))
+    PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
+    PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0))
 {
     fHead = addOne();
     fHead->init(c);
@@ -778,22 +508,22 @@ SkTSpan<TCurve>* SkTSect<TCurve>::addOne() {
 #endif
     }
     ++fActiveCount; 
-    PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
-    PATH_OPS_DEBUG_CODE(result->fDebugSect = this);
+#if DEBUG_T_SECT
+    result->fDebugID = fDebugCount++ * 2 + fDebugID;
+#endif
     return result;
 }
 
 template<typename TCurve>
-bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tStep,
+bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
         double* resultT, double* oppT) {
     SkTSpan<TCurve> work;
     double result = work.fStartT = work.fEndT = tStart;
-    PATH_OPS_DEBUG_CODE(work.fDebugSect = this);
     SkDPoint last = fCurve.ptAtT(tStart);
     SkDPoint oppPt;
     bool flip = false;
     SkDEBUGCODE(bool down = tStep < 0);
-    const TCurve& opp = sect2->fCurve;
+    const TCurve& opp = sect2.fCurve;
     do {
         tStep *= 0.5;
         work.fStartT += tStep;
@@ -811,11 +541,8 @@ bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tSt
         last = work.fPart[0];
         work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp);
         if (work.fCoinStart.isCoincident()) {
-#if DEBUG_T_SECT
-            work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt());
-#endif
             double oppTTest = work.fCoinStart.perpT();
-            if (sect2->fHead->contains(oppTTest)) {
+            if (sect2.fHead->contains(oppTTest)) {
                 *oppT = oppTTest;
                 oppPt = work.fCoinStart.perpPt();
                 SkASSERT(down ? result > work.fStartT : result < work.fStartT);
@@ -847,474 +574,196 @@ template<typename TCurve>
 SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
     SkTSpan<TCurve>* test = fHead;
     SkTSpan<TCurve>* largest = fHead;
-    bool lCollapsed = largest->fCollapsed;
+    bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
     while ((test = test->fNext)) {
-        bool tCollapsed = test->fCollapsed;
-        if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
-                largest->fBoundsMax < test->fBoundsMax)) {
+        bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
+        if ((largestCoin && !testCoin) || (largestCoin == testCoin
+                && (largest->fBoundsMax < test->fBoundsMax
+                || (largest->fCollapsed && !test->fCollapsed)))) {
             largest = test;
+            largestCoin = testCoin;
         }
     }
-    return largest;
+    return largestCoin ? NULL : largest;
 }
 
 template<typename TCurve>
 void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
     SkTSpan<TCurve>* first = fHead;
-    SkTSpan<TCurve>* last, * next;
+    SkTSpan<TCurve>* next;
     do {
-        int consecutive = this->countConsecutiveSpans(first, &last);
-        next = last->fNext;
+        int consecutive = 1;
+        SkTSpan<TCurve>* last = first;
+        do {
+            next = last->fNext;
+            if (!next) {
+                break;
+            }
+            if (next->fStartT > last->fEndT) {
+                break;
+            }
+            ++consecutive;
+            last = next;
+        } while (true);
         if (consecutive < COINCIDENT_SPAN_COUNT) {
             continue;
         }
-        this->validate();
-        sect2->validate();
-        this->computePerpendiculars(sect2, first, last);
-        this->validate();
-        sect2->validate();
+        setPerp(sect2->fCurve, first, last);
         // check to see if a range of points are on the curve
-        SkTSpan<TCurve>* coinStart = first;
-        do {
-            coinStart = this->extractCoincident(sect2, coinStart, last);
-        } while (coinStart && !last->fDeleted);
+        onCurveCheck(sect2, first, last);
+        SkTSpan<TCurve>* removalCandidate = NULL;
+        if (!first->fCoinStart.isCoincident()) {
+            SkTSpan<TCurve>* firstCoin = first->fNext;
+            removalCandidate = first;
+            first = firstCoin;
+        }
+        if (!first->fCoinStart.isCoincident()) {
+            continue;
+        }
+        if (removalCandidate) {
+            removeSpans(removalCandidate, sect2);
+        }
+        if (!last->fCoinStart.isCoincident()) {
+            continue;
+        }
+        if (!last->fCoinEnd.isCoincident()) {
+            if (--consecutive < COINCIDENT_SPAN_COUNT) {
+                continue;
+            }
+            last = last->fPrev;
+            SkASSERT(last->fCoinStart.isCoincident());
+            SkASSERT(last->fCoinEnd.isCoincident());
+        }
+        SkASSERT(between(0, first->fCoinStart.perpT(), 1) || first->fCoinStart.perpT() == -1);
+        if (first->fCoinStart.perpT() < 0) {
+            first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
+        }
+        SkASSERT(between(0, last->fCoinEnd.perpT(), 1) || last->fCoinEnd.perpT() == -1);
+        if (last->fCoinEnd.perpT() < 0) {
+            last->fCoinEnd.setPerp(fCurve, last->fEndT, last->fPart[TCurve::kPointLast],
+                    sect2->fCurve);
+        }
+        SkTSpan<TCurve>* removeMe = first->fNext;
+        while (removeMe != last) {
+            SkTSpan<TCurve>* removeNext = removeMe->fNext;
+            removeSpans(removeMe, sect2);
+            removeMe = removeNext;
+        }
     } while ((first = next));
 }
 
 template<typename TCurve>
-bool SkTSect<TCurve>::coincidentHasT(double t) {
-    SkTSpan<TCurve>* test = fCoincident;
-    while (test) {
-        if (between(test->fStartT, t, test->fEndT)) {
-            return true;
-        }
-        test = test->fNext;
+bool SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+        const SkTSpan<TCurve>* oppSpan) const {
+    bool check;  // we ignore whether the end points are in common or not
+    if (!span->intersects(oppSpan, &check)) {
+        return false;
     }
-    return false;
+    if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
+        return true;
+    }
+    return span->tightBoundsIntersects(oppSpan);
 }
 
 template<typename TCurve>
-void SkTSect<TCurve>::computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first,
-        SkTSpan<TCurve>* last) {
-    const TCurve& opp = sect2->fCurve;
-        SkTSpan<TCurve>* work = first;
-    SkTSpan<TCurve>* prior = NULL;
+void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+    SkTSpan<TCurve>* work = first;
+    first = NULL;
     do {
-        if (!work->fHasPerp && !work->fCollapsed) {
-            if (prior) {
-                work->fCoinStart = prior->fCoinEnd;
-            } else {
-                work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
-            }
-            if (work->fCoinStart.isCoincident()) {
-                double perpT = work->fCoinStart.perpT();
-                if (sect2->coincidentHasT(perpT)) {
-                    work->fCoinStart.clear();
-                } else {
-                    sect2->addForPerp(work, perpT);
-                }
-            }
-            work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
-            if (work->fCoinEnd.isCoincident()) {
-                double perpT = work->fCoinEnd.perpT();
-                if (sect2->coincidentHasT(perpT)) {
-                    work->fCoinEnd.clear();
-                } else {
-                    sect2->addForPerp(work, perpT);
-                }
+        if (work->fCoinStart.isCoincident()) {
+            if (!first) {
+                first = work;
             }
-            work->fHasPerp = true;
+        } else if (first) {
+            break;
         }
         if (work == last) {
             break;
         }
-        prior = work;
         work = work->fNext;
         SkASSERT(work);
     } while (true);
-}
-
-template<typename TCurve>
-int SkTSect<TCurve>::countConsecutiveSpans(SkTSpan<TCurve>* first,
-        SkTSpan<TCurve>** lastPtr) const {
-    int consecutive = 1;
-    SkTSpan<TCurve>* last = first;
-    do {
-        SkTSpan<TCurve>* next = last->fNext;
-        if (!next) {
-            break;
-        }
-        if (next->fStartT > last->fEndT) {
-            break;
-        }
-        ++consecutive;
-        last = next;
-    } while (true);
-    *lastPtr = last;
-    return consecutive;
-}
-
-template<typename TCurve>
-bool SkTSect<TCurve>::debugHasBounded(const SkTSpan<TCurve>* span) const {
-    const SkTSpan<TCurve>* test = fHead;
-    if (!test) {
-        return false;
-    }
-    do {
-        if (test->findOppSpan(span)) {
-            return true;
-        }
-    } while ((test = test->next()));
-    return false;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::deleteEmptySpans() {
-    SkTSpan<TCurve>* test;
-    SkTSpan<TCurve>* next = fHead;
-    while ((test = next)) {
-        next = test->fNext;
-        if (test->fBounded.count() == 0) {
-            this->removeSpan(test);
-        }
-    }
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
-        SkTSpan<TCurve>* last) {
-    first = findCoincidentRun(first, &last, sect2);
     if (!first) {
-        return NULL;
+        return;
     }
     // march outwards to find limit of coincidence from here to previous and next spans
     double startT = first->fStartT;
-    double oppStartT;
-    double oppEndT SK_INIT_TO_AVOID_WARNING;
+    double oppT;
     SkTSpan<TCurve>* prev = first->fPrev;
-    SkASSERT(first->fCoinStart.isCoincident());
-    SkTSpan<TCurve>* oppFirst = first->findOppT(first->fCoinStart.perpT());
-    SkASSERT(last->fCoinEnd.isCoincident());
-    bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
-    double coinStart;
-    SkDEBUGCODE(double coinEnd);
-    if (prev && prev->fEndT == startT
-            && this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
-                                      &oppStartT)
-            && prev->fStartT < coinStart && coinStart < startT) {
-        oppFirst = prev->findOppT(oppStartT);  // find opp start before splitting prev
-        SkASSERT(oppFirst);
-        first = this->addSplitAt(prev, coinStart);
-        first->markCoincident();
-        prev->fCoinEnd.markCoincident();
-        if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
-            SkTSpan<TCurve>* oppHalf = sect2->addSplitAt(oppFirst, oppStartT);
-            if (oppMatched) {
-                oppFirst->fCoinEnd.markCoincident();
-                oppHalf->markCoincident();
-                oppFirst = oppHalf;
-            } else {
-                oppFirst->markCoincident();
-                oppHalf->fCoinStart.markCoincident();
-            }
-        }
-    } else {
-        SkDEBUGCODE(coinStart = first->fStartT);
-        SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
-    }
-    SkTSpan<TCurve>* oppLast;
-    SkASSERT(last->fCoinEnd.isCoincident());
-    oppLast = last->findOppT(last->fCoinEnd.perpT());
-    SkDEBUGCODE(coinEnd = last->fEndT);
-    SkDEBUGCODE(oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT);
-    if (!oppMatched) {
-        SkTSwap(oppFirst, oppLast);
-        SkTSwap(oppStartT, oppEndT);
-    }
-    SkASSERT(oppStartT < oppEndT);
-    SkASSERT(coinStart == first->fStartT);
-    SkASSERT(coinEnd == last->fEndT);
-    SkASSERT(oppStartT == oppFirst->fStartT);
-    SkASSERT(oppEndT == oppLast->fEndT);
-    // reduce coincident runs to single entries
-    this->validate();
-    sect2->validate();
-    bool deleteThisSpan = this->updateBounded(first, last, oppFirst);
-    bool deleteSect2Span = sect2->updateBounded(oppFirst, oppLast, first);
-    this->removeSpanRange(first, last);
-    sect2->removeSpanRange(oppFirst, oppLast);
-    first->fEndT = last->fEndT;
-    first->resetBounds(this->fCurve);
-    first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
-    first->fCoinEnd.setPerp(fCurve, first->fEndT, first->fPart[TCurve::kPointLast], sect2->fCurve);
-    oppStartT = first->fCoinStart.perpT();
-    oppEndT = first->fCoinEnd.perpT();
-    if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) {
-        if (!oppMatched) {
-            SkTSwap(oppStartT, oppEndT);
-        }
-        oppFirst->fStartT = oppStartT;
-        oppFirst->fEndT = oppEndT;
-        oppFirst->resetBounds(sect2->fCurve);
-    }
-    this->validateBounded();
-    sect2->validateBounded();
-    last = first->fNext;
-    this->removeCoincident(first, false);
-    sect2->removeCoincident(oppFirst, true);
-    if (deleteThisSpan) {
-        this->deleteEmptySpans();
-    }
-    if (deleteSect2Span) {
-        sect2->deleteEmptySpans();
-    }
-    this->validate();
-    sect2->validate();
-    return last && !last->fDeleted ? last : NULL;
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::findCoincidentRun(SkTSpan<TCurve>* first,
-        SkTSpan<TCurve>** lastPtr, const SkTSect* sect2) {
-    SkTSpan<TCurve>* work = first;
-    SkTSpan<TCurve>* lastCandidate = NULL;
-    first = NULL;
-    // find the first fully coincident span
-    do {
-        if (work->fCoinStart.isCoincident()) {
-            work->validatePerpT(work->fCoinStart.perpT());
-            work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
-            SkASSERT(work->hasOppT(work->fCoinStart.perpT()));
-            if (!work->fCoinEnd.isCoincident()) {
-                break;
-            }
-            lastCandidate = work;
-            if (!first) {
-                first = work;
-            }
-        } else {
-            lastCandidate = NULL;
-            SkASSERT(!first);
-        }
-        if (work == *lastPtr) {
-            return first;
-        }
-        work = work->fNext;
-        SkASSERT(work);
-    } while (true);
-    if (lastCandidate) {
-        *lastPtr = lastCandidate;
-    }
-    return first;
-}
-
-template<typename TCurve>
-int SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
-        SkTSpan<TCurve>* oppSpan, int* oppResult) const {
-    bool spanStart, oppStart;
-    int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
-    if (hullResult >= 0) {
-        if (hullResult == 2) {  // hulls have one point in common
-            if (span->fBounded.count() <= 1) {
-                SkASSERT(span->fBounded.count() == 0 || span->fBounded[0] == oppSpan);
-                if (spanStart) {
-                    span->fEndT = span->fStartT;
-                } else {
-                    span->fStartT = span->fEndT;
-                }
-            } else {
-                hullResult = 1;
-            }
-            if (oppSpan->fBounded.count() <= 1) {
-                SkASSERT(span->fBounded.count() == 0 || oppSpan->fBounded[0] == span);
-                if (oppStart) {
-                    oppSpan->fEndT = oppSpan->fStartT;
+    if (prev) {
+        double coinStart;
+        if (binarySearchCoin(*sect2, startT, prev->fStartT - startT, &coinStart, &oppT)) {
+            if (coinStart < startT) {
+                SkASSERT(prev->fStartT < coinStart && coinStart < prev->fEndT);
+                SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
+                if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
+                    // split prev at coinStart if needed
+                    SkTSpan<TCurve>* half2 = addOne();
+                    half2->splitAt(prev, coinStart);
+                    half2->initBounds(fCurve);
+                    prev->initBounds(fCurve);
+                    prev->fCoinEnd.markCoincident();
+                    half2->fCoinStart.markCoincident();
+                    half2->fCoinEnd.markCoincident();
+                    // find span containing opposite t, and split that too
+                    SkTSpan<TCurve>* oppHalf = sect2->addOne();
+                    oppHalf->splitAt(oppStart, oppT);
+                    oppHalf->initBounds(sect2->fCurve);
+                    oppStart->initBounds(sect2->fCurve);
                 } else {
-                    oppSpan->fStartT = oppSpan->fEndT;
+                    SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
+                    first->fStartT = coinStart;
+                    prev->fEndT = coinStart;
+                    first->initBounds(fCurve);
+                    prev->initBounds(fCurve);
+                    first->fCoinStart.markCoincident();
+                    first->fCoinEnd.markCoincident();
                 }
-                *oppResult = 2;
-            } else {
-                *oppResult = 1;
             }
-        } else {
-            *oppResult = 1;
-        }
-        return hullResult;
-    }
-    if (span->fIsLine && oppSpan->fIsLine) {
-        SkIntersections i;
-        int sects = this->linesIntersect(span, opp, oppSpan, &i);
-        if (!sects) {
-            return -1;
         }
-        span->fStartT = span->fEndT = i[0][0];
-        oppSpan->fStartT = oppSpan->fEndT = i[1][0];
-        return *oppResult = 2;
-    }
-    if (span->fIsLinear || oppSpan->fIsLinear) {
-        return *oppResult = (int) span->linearsIntersect(oppSpan);
     }
-    return *oppResult = 1;
-}
-
-// while the intersection points are sufficiently far apart:
-// construct the tangent lines from the intersections
-// find the point where the tangent line intersects the opposite curve
-template<typename TCurve>
-int SkTSect<TCurve>::linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
-            const SkTSpan<TCurve>* oppSpan, SkIntersections* i) const {
-    SkIntersections thisRayI, oppRayI;
-    SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
-    SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[TCurve::kPointLast] }};
-    int loopCount = 0;
-    double bestDistSq = DBL_MAX;
-    do {
-        if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
-            return 0;
-        }
-        if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
-            return 0;
-        }
-        // pick the closest pair of points
-        double closest = DBL_MAX;
-        int closeIndex SK_INIT_TO_AVOID_WARNING;
-        int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
-        for (int index = 0; index < oppRayI.used(); ++index) {
-            if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
-                continue;
-            }
-            for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
-                if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
-                    continue;
-                }
-                double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
-                if (closest > distSq) {
-                    closest = distSq;
-                    closeIndex = index;
-                    oppCloseIndex = oIndex;
+    if (!work->fCoinEnd.isCoincident()) {
+        if (work->fEndT == 1) {
+            SkDebugf("!");
+        }
+//        SkASSERT(work->fEndT < 1);
+        startT = work->fStartT;
+        double coinEnd;
+        if (binarySearchCoin(*sect2, startT, work->fEndT - startT, &coinEnd, &oppT)) {
+            if (coinEnd > startT) {
+                SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
+                if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
+                    SkASSERT(coinEnd < work->fEndT);
+                    // split prev at coinEnd if needed
+                    SkTSpan<TCurve>* half2 = addOne();
+                    half2->splitAt(work, coinEnd);
+                    half2->initBounds(fCurve);
+                    work->initBounds(fCurve);
+                    work->fCoinStart.markCoincident();
+                    work->fCoinEnd.markCoincident();
+                    half2->fCoinStart.markCoincident();
+                    SkTSpan<TCurve>* oppHalf = sect2->addOne();
+                    oppHalf->splitAt(oppStart, oppT);
+                    oppHalf->initBounds(sect2->fCurve);
+                    oppStart->initBounds(sect2->fCurve);
+                } else {
+                    SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
+                    SkTSpan<TCurve>* next = work->fNext;
+                    bool hasNext = next && work->fEndT == next->fStartT;
+                    work->fEndT = coinEnd;
+                    work->initBounds(fCurve);
+                    work->fCoinStart.markCoincident();
+                    work->fCoinEnd.markCoincident();
+                    if (hasNext) { 
+                        next->fStartT = coinEnd;
+                        next->initBounds(fCurve);
+                    }
                 }
             }
         }
-        if (closest == DBL_MAX) {
-            return 0;
-        }
-        const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex);
-        const SkDPoint& iPt = oppRayI.pt(closeIndex);
-        if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
-                && between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
-                && oppIPt.approximatelyEqual(iPt)) {
-            i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex);
-            return i->used();
-        }
-        double distSq = oppIPt.distanceSquared(iPt);
-        if (bestDistSq < distSq || ++loopCount > 5) {
-            break;
-        }
-        bestDistSq = distSq;
-        thisLine[0] = fCurve.ptAtT(oppRayI[0][closeIndex]);
-        thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppRayI[0][closeIndex]);
-        oppLine[0] = opp->fCurve.ptAtT(thisRayI[0][oppCloseIndex]);
-        oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(thisRayI[0][oppCloseIndex]);
-    } while (true);
-    return false;
-}
-
-
-template<typename TCurve>
-void SkTSect<TCurve>::markSpanGone(SkTSpan<TCurve>* span) {
-    --fActiveCount;
-    span->fNext = fDeleted;
-    fDeleted = span;
-    SkASSERT(!span->fDeleted);
-    span->fDeleted = true;
-}
-
-template<typename TCurve>
-bool SkTSect<TCurve>::matchedDirection(double t, const SkTSect* sect2, double t2) const {
-    SkDVector dxdy = this->fCurve.dxdyAtT(t);
-    SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2);
-    return dxdy.dot(dxdy2) >= 0;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::matchedDirCheck(double t, const SkTSect* sect2, double t2,
-        bool* calcMatched, bool* oppMatched) const {
-    if (*calcMatched) {
-        SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2)); 
-    } else {
-        *oppMatched = this->matchedDirection(t, sect2, t2);
-        *calcMatched = true;
     }
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::mergeCoincidence(SkTSect* sect2) {
-    double smallLimit = 0;
-    do {
-        // find the smallest unprocessed span
-        SkTSpan<TCurve>* smaller = NULL;
-        SkTSpan<TCurve>* test = fCoincident;
-        do {
-            if (test->fStartT < smallLimit) {
-                continue;
-            }
-            if (smaller && smaller->fEndT < test->fStartT) {
-                continue;
-            }
-            smaller = test;
-        } while ((test = test->fNext));
-        if (!smaller) {
-            return;
-        }
-        smallLimit = smaller->fEndT;
-        // find next larger span
-        SkTSpan<TCurve>* prior = NULL;
-        SkTSpan<TCurve>* larger = NULL;
-        SkTSpan<TCurve>* largerPrior = NULL;
-        test = fCoincident;
-        do {
-            if (test->fStartT < smaller->fEndT) {
-                continue;
-            }
-            SkASSERT(test->fStartT != smaller->fEndT);
-            if (larger && larger->fStartT < test->fStartT) {
-                continue;
-            }
-            largerPrior = prior;
-            larger = test;
-        } while ((prior = test), (test = test->fNext));
-        if (!larger) {
-            continue;
-        }
-        // check middle t value to see if it is coincident as well
-        double midT = (smaller->fEndT + larger->fStartT) / 2;
-        SkDPoint midPt = fCurve.ptAtT(midT);
-        SkTCoincident<TCurve> coin;
-        coin.setPerp(fCurve, midT, midPt, sect2->fCurve);
-        if (coin.isCoincident()) {
-            smaller->fEndT = larger->fEndT;
-            smaller->fCoinEnd = larger->fCoinEnd;
-            if (largerPrior) {
-                largerPrior->fNext = larger->fNext;
-            } else {
-                fCoincident = larger->fNext;
-            }
-        }
-    } while (true);
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::prev(SkTSpan<TCurve>* span) const {
-    SkTSpan<TCurve>* result = NULL;
-    SkTSpan<TCurve>* test = fHead;
-    while (span != test) {
-        result = test;
-        test = test->fNext;
-        SkASSERT(test);
-    }
-    return result; 
-}
-
 template<typename TCurve>
 void SkTSect<TCurve>::recoverCollapsed() {
     SkTSpan<TCurve>* deleted = fDeleted;
@@ -1333,84 +782,41 @@ void SkTSect<TCurve>::recoverCollapsed() {
 }
 
 template<typename TCurve>
-void SkTSect<TCurve>::removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span,
-        SkTSect* opp) {
-    int count = span->fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        SkTSpan<TCurve>* bounded = span->fBounded[0];
-        if (bounded == keep) {
-            continue;
-        }
-        if (bounded->fDeleted) {  // may have been deleted when opp did 'remove all but'
-            continue;
+void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
+    SkTSpan<TCurve>* prev = span->fPrev;
+    SkTSpan<TCurve>* next = span->fNext;
+    if (prev) {
+        prev->fNext = next;
+        if (next) {
+            next->fPrev = prev;
         }
-        SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
-        if (bounded->removeBounded(span)) {
-            opp->removeSpan(bounded);
+    } else {
+        fHead = next;
+        if (next) {
+            next->fPrev = NULL;
         }
     }
-    SkASSERT(!span->fDeleted);
-    SkASSERT(span->findOppSpan(keep));
-    SkASSERT(keep->findOppSpan(span));
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeByPerpendicular(SkTSect<TCurve>* opp) {
-    SkTSpan<TCurve>* test = fHead;
-    SkTSpan<TCurve>* next;
-    do {
-        next = test->fNext;
-        if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) {
-            continue;
-        }
-        SkDVector startV = test->fCoinStart.perpPt() - test->fPart[0];
-        SkDVector endV = test->fCoinEnd.perpPt() - test->fPart[TCurve::kPointLast];
+    --fActiveCount;
+    span->fNext = fDeleted;
+    fDeleted = span;
 #if DEBUG_T_SECT
-        SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__,
-                startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV));
+    SkASSERT(!span->fDebugDeleted);
+    span->fDebugDeleted = true;
 #endif
-        if (startV.dot(endV) <= 0) {
-            continue;
-        }
-        this->removeSpans(test, opp);
-    } while ((test = next));
 }
 
 template<typename TCurve>
-void SkTSect<TCurve>::removeCoincident(SkTSpan<TCurve>* span, bool isBetween) {
-    this->unlinkSpan(span);
-    if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
-        --fActiveCount;
-        span->fNext = fCoincident;
-        fCoincident = span;
-    } else {
-        this->markSpanGone(span);
-    }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
-    this->unlinkSpan(span);
-    this->markSpanGone(span);
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
-    if (first == last) {
-        return;
-    }
-    SkTSpan<TCurve>* span = first;
-    SkASSERT(span);
-    SkTSpan<TCurve>* final = last->fNext;
-    SkTSpan<TCurve>* next = span->fNext;
-    while ((span = next) && span != final) {
-        next = span->fNext;
-        this->markSpanGone(span);
-    }
-    if (final) {
-        final->fPrev = first;
+void SkTSect<TCurve>::removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span) {
+    int last = span->fBounded.count() - 1;
+    for (int index = 0; index <= last; ++index) {
+        if (span->fBounded[index] == test) {
+            span->fBounded.removeShuffle(index);
+            if (!last) {
+                removeSpan(span);
+            }
+            return;
+        }
     }
-    first->fNext = final;
 }
 
 template<typename TCurve>
@@ -1418,33 +824,38 @@ void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) {
     int count = span->fBounded.count();
     for (int index = 0; index < count; ++index) {
         SkTSpan<TCurve>* bounded = span->fBounded[0];
-        if (span->removeBounded(bounded)) {  // shuffles last into position 0
-            this->removeSpan(span);
-        }
-        if (bounded->removeBounded(span)) {
-            opp->removeSpan(bounded);
-        }
-        SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
-
+        removeOne(bounded, span);  // shuffles last into position 0
+        opp->removeOne(span, bounded);
     }
 }
 
 template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::spanAtT(double t, SkTSpan<TCurve>** priorSpan) {
-    SkTSpan<TCurve>* test = fHead;
-    SkTSpan<TCurve>* prev = NULL;
-    while (test && test->fEndT < t) {
-        prev = test;
-        test = test->fNext;
+void SkTSect<TCurve>::setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+    SkTSpan<TCurve>* work = first;
+    if (!work->fHasPerp) {
+        work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
     }
-    *priorSpan = prev;
-    return test && test->fStartT <= t ? test : NULL;
+    do {
+        if (!work->fHasPerp) {
+            work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
+            work->fHasPerp = true;
+        }
+        if (work == last) {
+            break;
+        }
+        SkTSpan<TCurve>* last = work;
+        work = work->fNext;
+        SkASSERT(work);
+        if (!work->fHasPerp) {
+            work->fCoinStart = last->fCoinEnd;
+        }
+    } while (true);
 }
 
 template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
-    SkTSpan<TCurve>* result = fHead;
-    SkTSpan<TCurve>* next = fHead;
+const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
+    const SkTSpan<TCurve>* result = fHead;
+    const SkTSpan<TCurve>* next = fHead;
     while ((next = next->fNext)) {
         if (next->fEndT > result->fEndT) {
             result = next;
@@ -1458,75 +869,32 @@ SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
 template<typename TCurve>
 void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
     span->initBounds(fCurve);
-    for (int index = 0; index < span->fBounded.count(); ) {
+    int count = span->fBounded.count();
+    for (int index = 0; index < count; ) {
         SkTSpan<TCurve>* test = span->fBounded[index];
-        int oppSects, sects = this->intersects(span, opp, test, &oppSects);
-        if (sects >= 1) {
-            if (sects == 2) {
-                span->initBounds(fCurve);
-                this->removeAllBut(test, span, opp);
-            }
-            if (oppSects == 2) {
-                test->initBounds(opp->fCurve);
-                opp->removeAllBut(span, test, this);
-            }
+        bool sects = intersects(span, opp, test);
+        if (sects) {
             ++index;
         } else {
-            if (span->removeBounded(test)) {
-                this->removeSpan(span);
-            }
-            if (test->removeBounded(span)) {
-                opp->removeSpan(test);
-            }
-        }
-    }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::unlinkSpan(SkTSpan<TCurve>* span) {
-    SkTSpan<TCurve>* prev = span->fPrev;
-    SkTSpan<TCurve>* next = span->fNext;
-    if (prev) {
-        prev->fNext = next;
-        if (next) {
-            next->fPrev = prev;
-        }
-    } else {
-        fHead = next;
-        if (next) {
-            next->fPrev = NULL;
+            removeOne(test, span);
+            opp->removeOne(span, test);
+            --count;
         }
     }
 }
 
-template<typename TCurve>
-bool SkTSect<TCurve>::updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last,
-        SkTSpan<TCurve>* oppFirst) {
-    SkTSpan<TCurve>* test = first;
-    const SkTSpan<TCurve>* final = last->next();
-    bool deleteSpan = false;
-    do {
-        deleteSpan |= test->removeAllBounded();
-    } while ((test = test->fNext) != final);
-    first->fBounded.resize_back(1);
-    first->fBounded[0] = oppFirst;
-    // cannot call validate until remove span range is called
-    return deleteSpan;
-}
-
-
+#if DEBUG_T_SECT
 template<typename TCurve>
 void SkTSect<TCurve>::validate() const {
-#if DEBUG_T_SECT
     int count = 0;
     if (fHead) {
         const SkTSpan<TCurve>* span = fHead;
         SkASSERT(!span->fPrev);
-        SkDEBUGCODE(double last = 0);
+        double last = 0;
         do {
             span->validate();
             SkASSERT(span->fStartT >= last);
-            SkDEBUGCODE(last = span->fEndT);
+            last = span->fEndT;
             ++count;
         } while ((span = span->fNext) != NULL);
     }
@@ -1538,81 +906,54 @@ void SkTSect<TCurve>::validate() const {
         ++deletedCount;
         deleted = deleted->fNext;
     }
-    const SkTSpan<TCurve>* coincident = fCoincident;
-    while (coincident) {
-        ++deletedCount;
-        coincident = coincident->fNext;
-    }
     SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
-#endif
 }
-
-template<typename TCurve>
-void SkTSect<TCurve>::validateBounded() const {
-#if DEBUG_T_SECT
-    if (!fHead) {
-        return;
-    }
-    const SkTSpan<TCurve>* span = fHead;
-    do {
-        span->validateBounded();
-    } while ((span = span->fNext) != NULL);
 #endif
-}
 
 template<typename TCurve>
 int SkTSect<TCurve>::EndsEqual(const SkTSect* sect1, const SkTSect* sect2,
         SkIntersections* intersections) {
     int zeroOneSet = 0;
-    if (sect1->fCurve[0] == sect2->fCurve[0]) {
-        zeroOneSet |= kZeroS1Set | kZeroS2Set;
-        intersections->insert(0, 0, sect1->fCurve[0]);
-    }
-    if (sect1->fCurve[0] == sect2->fCurve[TCurve::kPointLast]) {
-        zeroOneSet |= kZeroS1Set | kOneS2Set;
-        intersections->insert(0, 1, sect1->fCurve[0]);
-    }
-    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) {
-        zeroOneSet |= kOneS1Set | kZeroS2Set;
-        intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
-    }
-    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[TCurve::kPointLast]) {
-        zeroOneSet |= kOneS1Set | kOneS2Set;
-            intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
-    }
     // check for zero
-    if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
-            && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+    if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
         zeroOneSet |= kZeroS1Set | kZeroS2Set;
-        intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
-    }
-    if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
-            && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
+        if (sect1->fCurve[0] != sect2->fCurve[0]) {
+            intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
+        } else {
+            intersections->insert(0, 0, sect1->fCurve[0]);
+        }
+    } 
+    if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
         zeroOneSet |= kZeroS1Set | kOneS2Set;
-        intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
-    }
+        if (sect1->fCurve[0] != sect2->fCurve[TCurve::kPointLast]) {
+            intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
+        } else {
+            intersections->insert(0, 1, sect1->fCurve[0]);
+        }
+    } 
     // check for one
-    if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
-            && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+    if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
         zeroOneSet |= kOneS1Set | kZeroS2Set;
-        intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
-    }
-    if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
-            && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
-            TCurve::kPointLast])) {
+        if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[0]) {
+            intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
+        } else {
+            intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
+        }
+    } 
+    if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
         zeroOneSet |= kOneS1Set | kOneS2Set;
-        intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
-                sect2->fCurve[TCurve::kPointLast]);
+        if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[TCurve::kPointLast]) {
+            intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
+                    sect2->fCurve[TCurve::kPointLast]);
+        } else {
+            intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
+        }
     }
     return zeroOneSet;
 }
 
 template<typename TCurve>
 struct SkClosestRecord {
-    bool operator<(const SkClosestRecord& rh) const {
-        return fClosest < rh.fClosest;
-    }
-
     void addIntersection(SkIntersections* intersections) const {
         double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT();
         double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT();
@@ -1692,14 +1033,14 @@ struct SkClosestSect {
         fClosest.push_back().reset();
     }
 
-    bool find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
+    void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
         SkClosestRecord<TCurve>* record = &fClosest[fUsed];
         record->findEnd(span1, span2, 0, 0);
         record->findEnd(span1, span2, 0, TCurve::kPointLast);
         record->findEnd(span1, span2, TCurve::kPointLast, 0);
         record->findEnd(span1, span2, TCurve::kPointLast, TCurve::kPointLast);
         if (record->fClosest == FLT_MAX) {
-            return false;
+            return;
         }
         for (int index = 0; index < fUsed; ++index) {
             SkClosestRecord<TCurve>* test = &fClosest[index];
@@ -1709,46 +1050,37 @@ struct SkClosestSect {
                 }
                 test->update(*record);
                 record->reset();
-                return false;
+                return;
             }
         }
         ++fUsed;
         fClosest.push_back().reset();
-        return true;
     }
 
     void finish(SkIntersections* intersections) const {
-        SkSTArray<TCurve::kMaxIntersections * 2, const SkClosestRecord<TCurve>*, true> closestPtrs;
-        for (int index = 0; index < fUsed; ++index) {
-            closestPtrs.push_back(&fClosest[index]);
-        }
-        SkTQSort<const SkClosestRecord<TCurve> >(closestPtrs.begin(), closestPtrs.end() - 1);
         for (int index = 0; index < fUsed; ++index) {
-            const SkClosestRecord<TCurve>* test = closestPtrs[index];
-            test->addIntersection(intersections);
+            const SkClosestRecord<TCurve>& test = fClosest[index];
+            test.addIntersection(intersections);
         }
     }
 
-    // this is oversized so that an extra records can merge into final one
-    SkSTArray<TCurve::kMaxIntersections * 2, SkClosestRecord<TCurve>, true> fClosest;
+    // this is oversized by one so that an extra record can merge into final one
+    SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
     int fUsed;
 };
 
 // returns true if the rect is too small to consider
 template<typename TCurve>
 void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) {
-    PATH_OPS_DEBUG_CODE(sect1->fOppSect = sect2);
-    PATH_OPS_DEBUG_CODE(sect2->fOppSect = sect1);
     intersections->reset();
-    intersections->setMax(TCurve::kMaxIntersections * 2);  // give extra for slop
+    intersections->setMax(TCurve::kMaxIntersections);
     SkTSpan<TCurve>* span1 = sect1->fHead;
     SkTSpan<TCurve>* span2 = sect2->fHead;
-    int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
-//    SkASSERT(between(0, sect, 2));
-    if (!sect) {
+    bool check;
+    if (!span1->intersects(span2, &check)) {
         return;
     }
-    if (sect == 2 && oppSect == 2) {
+    if (check) {
         (void) EndsEqual(sect1, sect2, intersections);
         return;
     }
@@ -1764,12 +1096,12 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
         bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
             || (!largest1->fCollapsed && largest2->fCollapsed)));
         // split it
+        SkTSect* splitSect = split1 ? sect1 : sect2;
         SkTSpan<TCurve>* half1 = split1 ? largest1 : largest2;
         SkASSERT(half1);
         if (half1->fCollapsed) {
             break;
         }
-        SkTSect* splitSect = split1 ? sect1 : sect2;
         // trim parts that don't intersect the opposite
         SkTSpan<TCurve>* half2 = splitSect->addOne();
         SkTSect* unsplitSect = split1 ? sect2 : sect1;
@@ -1778,52 +1110,50 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
         }
         splitSect->trim(half1, unsplitSect);
         splitSect->trim(half2, unsplitSect);
-        sect1->validate();
-        sect2->validate();
         // if there are 9 or more continuous spans on both sects, suspect coincidence
         if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
                 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
             sect1->coincidentCheck(sect2);
-            sect1->validate();
-            sect2->validate();
         }
-        if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
-                && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
-            sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
-            sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
-            sect1->removeByPerpendicular(sect2);
-            sect1->validate();
-            sect2->validate();
-        }
-#if DEBUG_T_SECT_DUMP
-        sect1->dumpBoth(sect2);
+#if DEBUG_T_SECT
+        sect1->validate();
+        sect2->validate();
+#endif
+#if DEBUG_T_SECT_DUMP > 1
+        sect1->dumpBoth(*sect2);
 #endif
         if (!sect1->fHead || !sect2->fHead) {
-            break;
+            return;
         }
     } while (true);
-    SkTSpan<TCurve>* coincident = sect1->fCoincident;
-    if (coincident) {
-        // if there is more than one coincident span, check loosely to see if they should be joined
-        if (coincident->fNext) {
-            sect1->mergeCoincidence(sect2);
-            coincident = sect1->fCoincident;
-        }
-        SkASSERT(sect2->fCoincident);  // courtesy check : coincidence only looks at sect 1
+    if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
+        // check for coincidence
+        SkTSpan<TCurve>* first = sect1->fHead;
         do {
-            SkASSERT(coincident->fCoinStart.isCoincident());
-            SkASSERT(coincident->fCoinEnd.isCoincident());
-            int index = intersections->insertCoincident(coincident->fStartT,
-                    coincident->fCoinStart.perpT(), coincident->fPart[0]);
-            if ((intersections->insertCoincident(coincident->fEndT,
-                    coincident->fCoinEnd.perpT(),
-                    coincident->fPart[TCurve::kPointLast]) < 0) && index >= 0) {
+            if (!first->fCoinStart.isCoincident()) {
+                continue;
+            }
+            int spanCount = 1;
+            SkTSpan<TCurve>* last = first;
+            while (last->fCoinEnd.isCoincident()) {
+                SkTSpan<TCurve>* next = last->fNext;
+                if (!next || !next->fCoinEnd.isCoincident()) {
+                    break;
+                }
+                last = next;
+                ++spanCount;
+            }
+            if (spanCount < 2) {
+                first = last;
+                continue;
+            }
+            int index = intersections->insertCoincident(first->fStartT, first->fCoinStart.perpT(),
+                    first->fPart[0]);
+            if (intersections->insertCoincident(last->fEndT, last->fCoinEnd.perpT(),
+                    last->fPart[TCurve::kPointLast]) < 0) {
                 intersections->clearCoincidence(index);
             }
-        } while ((coincident = coincident->fNext));
-    }
-    if (!sect1->fHead || !sect2->fHead) {
-        return;
+        } while ((first = first->fNext));
     }
     int zeroOneSet = EndsEqual(sect1, sect2, intersections);
     sect1->recoverCollapsed();
@@ -1833,41 +1163,33 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
     const SkTSpan<TCurve>* head1 = result1;
     if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
         const SkDPoint& start1 = sect1->fCurve[0];
-        if (head1->isBounded()) {
-            double t = head1->closestBoundedT(start1);
-            if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
-                intersections->insert(0, t, start1);
-            }
+        double t = head1->closestBoundedT(start1);
+        if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
+            intersections->insert(0, t, start1);
         }
     }
     const SkTSpan<TCurve>* head2 = sect2->fHead;
     if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
         const SkDPoint& start2 = sect2->fCurve[0];
-        if (head2->isBounded()) {
-            double t = head2->closestBoundedT(start2);
-            if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
-                intersections->insert(t, 0, start2);
-            }
+        double t = head2->closestBoundedT(start2);
+        if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
+            intersections->insert(t, 0, start2);
         }
     }
     const SkTSpan<TCurve>* tail1 = sect1->tail();
     if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) {
         const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast];
-        if (tail1->isBounded()) {
-            double t = tail1->closestBoundedT(end1);
-            if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
-                intersections->insert(1, t, end1);
-            }
+        double t = tail1->closestBoundedT(end1);
+        if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
+            intersections->insert(1, t, end1);
         }
     }
     const SkTSpan<TCurve>* tail2 = sect2->tail();
     if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) {
         const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast];
-        if (tail2->isBounded()) {
-            double t = tail2->closestBoundedT(end2);
-            if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
-                intersections->insert(t, 1, end2);
-            }
+        double t = tail2->closestBoundedT(end2);
+        if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
+            intersections->insert(t, 1, end2);
         }
     }
     SkClosestSect<TCurve> closest;
@@ -1879,39 +1201,11 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
             break;
         }
         SkTSpan<TCurve>* result2 = sect2->fHead;
-        bool found = false;
         while (result2) {
-            found |= closest.find(result1, result2);
+            closest.find(result1, result2);
             result2 = result2->fNext;
         }
+        
     } while ((result1 = result1->fNext));
     closest.finish(intersections);
-    // if there is more than one intersection and it isn't already coincident, check
-    int last = intersections->used() - 1;
-    for (int index = 0; index < last; ) {
-        if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
-            ++index;
-            continue;
-        }
-        double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
-        SkDPoint midPt = sect1->fCurve.ptAtT(midT);
-        // intersect perpendicular with opposite curve
-        SkTCoincident<TCurve> perp;
-        perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve);
-        if (!perp.isCoincident()) {
-            ++index;
-            continue;
-        }
-        if (intersections->isCoincident(index)) {
-            intersections->removeOne(index);
-            --last;
-        } else if (intersections->isCoincident(index + 1)) {
-            intersections->removeOne(index + 1);
-            --last;
-        } else {
-            intersections->setCoincident(index++);
-        }
-        intersections->setCoincident(index);
-    }
-    SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
 }
index d03efeb173e1d6ba08114686fab376c43a5c1b85..0f63f396e7edaf7c74901f1c97108e7273e8c453 100644 (file)
@@ -8,16 +8,14 @@
 #include "SkPathOpsCommon.h"
 
 bool TightBounds(const SkPath& path, SkRect* result) {
-    SkOpContour contour;
-    SkOpGlobalState globalState( NULL  PATH_OPS_DEBUG_PARAMS(&contour));
     // turn path into list of segments
-    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
-    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
-    if (!builder.finish(&allocator)) {
+    SkTArray<SkOpContour> contours;
+    SkOpEdgeBuilder builder(path, contours);
+    if (!builder.finish()) {
         return false;
     }
-    SkTDArray<SkOpContour* > contourList;
-    MakeContourList(&contour, contourList, false, false);
+    SkTArray<SkOpContour*, true> contourList;
+    MakeContourList(contours, contourList, false, false);
     SkOpContour** currentPtr = contourList.begin();
     result->setEmpty();
     if (!currentPtr) {
diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp
new file mode 100644 (file)
index 0000000..77845e0
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPathOpsTriangle.h"
+
+// http://www.blackpawn.com/texts/pointinpoly/default.html
+// return true if pt is inside triangle; false if outside or on the line
+bool SkDTriangle::contains(const SkDPoint& pt) const {
+// Compute vectors
+    SkDVector v0 = fPts[2] - fPts[0];
+    SkDVector v1 = fPts[1] - fPts[0];
+    SkDVector v2 = pt - fPts[0];
+
+// Compute dot products
+    double dot00 = v0.dot(v0);
+    double dot01 = v0.dot(v1);
+    double dot02 = v0.dot(v2);
+    double dot11 = v1.dot(v1);
+    double dot12 = v1.dot(v2);
+
+// original code doesn't handle degenerate input; isn't symmetric with inclusion of corner pts;
+// introduces error with divide; doesn't short circuit on early answer
+#if 0
+// Compute barycentric coordinates
+    double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+    double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+    double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+// Check if point is in triangle
+    return (u >= 0) && (v >= 0) && (u + v <= 1);
+#else
+    double w = dot00 * dot11 - dot01 * dot01;
+    if (w == 0) {
+        return false;
+    }
+    double wSign = w < 0 ? -1 : 1;
+    double u = (dot11 * dot02 - dot01 * dot12) * wSign;
+    if (u <= 0) {
+        return false;
+    }
+    double v = (dot00 * dot12 - dot01 * dot02) * wSign;
+    if (v <= 0) {
+        return false;
+    }
+    return u + v < w * wSign;
+#endif
+}
diff --git a/src/pathops/SkPathOpsTriangle.h b/src/pathops/SkPathOpsTriangle.h
new file mode 100644 (file)
index 0000000..8cc8c6d
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPathOpsTriangle_DEFINED
+#define SkPathOpsTriangle_DEFINED
+
+#include "SkPathOpsPoint.h"
+
+struct SkDTriangle {
+    SkDPoint fPts[3];
+
+    bool contains(const SkDPoint& pt) const;
+
+};
+
+#endif
index 0248e7115a6d0066c54af5fb21fd0ab8ecf10e48..01fec0d0b616a7d324dd2e1cd9d0e72cca9d204c 100644 (file)
@@ -22,111 +22,6 @@ enum SkPathOpsMask {
     kEvenOdd_PathOpsMask = 1
 };
 
-class SkOpCoincidence;
-class SkOpContour;
-
-class SkOpGlobalState {
-public:
-    SkOpGlobalState(SkOpCoincidence* coincidence  PATH_OPS_DEBUG_PARAMS(SkOpContour* head))
-        : fCoincidence(coincidence)
-        , fWindingFailed(false)
-        , fAngleCoincidence(false)
-#if DEBUG_VALIDATE
-        , fPhase(kIntersecting)
-#endif
-        PATH_OPS_DEBUG_PARAMS(fHead(head))
-        PATH_OPS_DEBUG_PARAMS(fAngleID(0))
-        PATH_OPS_DEBUG_PARAMS(fContourID(0))
-        PATH_OPS_DEBUG_PARAMS(fPtTID(0))
-        PATH_OPS_DEBUG_PARAMS(fSegmentID(0))
-        PATH_OPS_DEBUG_PARAMS(fSpanID(0)) {
-    }
-
-#if DEBUG_VALIDATE
-    enum Phase {
-        kIntersecting,
-        kWalking
-    };
-#endif
-
-    bool angleCoincidence() {
-        return fAngleCoincidence;
-    }
-
-    SkOpCoincidence* coincidence() {
-        return fCoincidence;
-    }
-
-#ifdef SK_DEBUG
-    const struct SkOpAngle* debugAngle(int id) const;
-    SkOpContour* debugContour(int id);
-    const class SkOpPtT* debugPtT(int id) const;
-    const class SkOpSegment* debugSegment(int id) const;
-    const class SkOpSpanBase* debugSpan(int id) const;
-
-    int nextAngleID() {
-        return ++fAngleID;
-    }
-
-    int nextContourID() {
-        return ++fContourID;
-    }
-    int nextPtTID() {
-        return ++fPtTID;
-    }
-
-    int nextSegmentID() {
-        return ++fSegmentID;
-    }
-
-    int nextSpanID() {
-        return ++fSpanID;
-    }
-#endif
-
-#if DEBUG_VALIDATE
-    Phase phase() const {
-        return fPhase;
-    }
-#endif
-
-    void setAngleCoincidence() {
-        fAngleCoincidence = true;
-    }
-    
-#if DEBUG_VALIDATE
-    void setPhase(Phase phase) {
-        SkASSERT(fPhase != phase);
-        fPhase = phase;
-    }
-#endif
-
-    // called in very rare cases where angles are sorted incorrectly -- signfies op will fail
-    void setWindingFailed() {
-        fWindingFailed = true;
-    }
-
-    bool windingFailed() const {
-        return fWindingFailed;
-    }
-
-private:
-    SkOpCoincidence* fCoincidence;
-    bool fWindingFailed;
-    bool fAngleCoincidence;
-#if DEBUG_VALIDATE
-    Phase fPhase;
-#endif
-#ifdef SK_DEBUG
-    SkOpContour* fHead;
-    int fAngleID;
-    int fContourID;
-    int fPtTID;
-    int fSegmentID;
-    int fSpanID;
-#endif
-};
-
 // Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
 bool AlmostEqualUlps(float a, float b);
 inline bool AlmostEqualUlps(double a, double b) {
@@ -197,7 +92,6 @@ const double DBL_EPSILON_SUBDIVIDE_ERR = DBL_EPSILON * 16;
 const double ROUGH_EPSILON = FLT_EPSILON * 64;
 const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
 const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
-const double BUMP_EPSILON = FLT_EPSILON * 4096;
 
 inline bool zero_or_one(double x) {
     return x == 0 || x == 1;
@@ -247,6 +141,12 @@ inline bool roughly_zero(double x) {
     return fabs(x) < ROUGH_EPSILON;
 }
 
+#if 0  // unused for now
+inline bool way_roughly_zero(double x) {
+    return fabs(x) < WAY_ROUGH_EPSILON;
+}
+#endif
+
 inline bool approximately_zero_inverse(double x) {
     return fabs(x) > FLT_EPSILON_INVERSE;
 }
@@ -256,10 +156,6 @@ inline bool approximately_zero_when_compared_to(double x, double y) {
     return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
 }
 
-inline bool precisely_zero_when_compared_to(double x, double y) {
-    return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
-}
-
 // 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) {
@@ -408,8 +304,7 @@ inline bool precisely_between(double a, double b, double c) {
 
 // returns true if (a <= b <= c) || (a >= b >= c)
 inline bool between(double a, double b, double c) {
-    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
-            || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
+    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
     return (a - b) * (c - b) <= 0;
 }
 
@@ -417,15 +312,6 @@ inline bool roughly_equal(double x, double y) {
     return fabs(x - y) < ROUGH_EPSILON;
 }
 
-inline bool roughly_negative(double x) {
-    return x < ROUGH_EPSILON;
-}
-
-inline bool roughly_between(double a, double b, double c) {
-    return a <= c ? roughly_negative(a - b) && roughly_negative(b - c)
-            : roughly_negative(b - a) && roughly_negative(c - b);
-}
-
 inline bool more_roughly_equal(double x, double y) {
     return fabs(x - y) < MORE_ROUGH_EPSILON;
 }
@@ -438,6 +324,7 @@ struct SkDPoint;
 struct SkDVector;
 struct SkDLine;
 struct SkDQuad;
+struct SkDTriangle;
 struct SkDCubic;
 struct SkDRect;
 
diff --git a/src/pathops/SkQuarticRoot.cpp b/src/pathops/SkQuarticRoot.cpp
new file mode 100644 (file)
index 0000000..f9a7bf5
--- /dev/null
@@ -0,0 +1,168 @@
+// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+/*
+ *  Roots3And4.c
+ *
+ *  Utility functions to find cubic and quartic roots,
+ *  coefficients are passed like this:
+ *
+ *      c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
+ *
+ *  The functions return the number of non-complex roots and
+ *  put the values into the s array.
+ *
+ *  Author:         Jochen Schwarze (schwarze@isa.de)
+ *
+ *  Jan 26, 1990    Version for Graphics Gems
+ *  Oct 11, 1990    Fixed sign problem for negative q's in SolveQuartic
+ *                  (reported by Mark Podlipec),
+ *                  Old-style function definitions,
+ *                  IsZero() as a macro
+ *  Nov 23, 1990    Some systems do not declare acos() and cbrt() in
+ *                  <math.h>, though the functions exist in the library.
+ *                  If large coefficients are used, EQN_EPS should be
+ *                  reduced considerably (e.g. to 1E-30), results will be
+ *                  correct but multiple roots might be reported more
+ *                  than once.
+ */
+
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsQuad.h"
+#include "SkQuarticRoot.h"
+
+int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+        const double t0, const bool oneHint, double roots[4]) {
+#ifdef SK_DEBUG
+    // create a string mathematica understands
+    // GDB set print repe 15 # if repeated digits is a bother
+    //     set print elements 400 # if line doesn't fit
+    char str[1024];
+    sk_bzero(str, sizeof(str));
+    SK_SNPRINTF(str, sizeof(str),
+            "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
+            t4, t3, t2, t1, t0);
+    SkPathOpsDebug::MathematicaIze(str, sizeof(str));
+#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
+    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)) {
+        if (approximately_zero_when_compared_to(t3, t0)
+            && approximately_zero_when_compared_to(t3, t1)
+            && approximately_zero_when_compared_to(t3, t2)) {
+            return SkDQuad::RootsReal(t2, t1, t0, roots);
+        }
+        if (approximately_zero_when_compared_to(t4, t3)) {
+            return SkDCubic::RootsReal(t3, t2, t1, t0, roots);
+        }
+    }
+    if ((approximately_zero_when_compared_to(t0, t1) || approximately_zero(t1))  // 0 is one root
+      //      && approximately_zero_when_compared_to(t0, t2)
+            && approximately_zero_when_compared_to(t0, t3)
+            && approximately_zero_when_compared_to(t0, t4)) {
+        int num = SkDCubic::RootsReal(t4, t3, t2, t1, roots);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_zero(roots[i])) {
+                return num;
+            }
+        }
+        roots[num++] = 0;
+        return num;
+    }
+    if (oneHint) {
+        SkASSERT(approximately_zero_double(t4 + t3 + t2 + t1 + t0) ||
+                approximately_zero_when_compared_to(t4 + t3 + t2 + t1 + t0,  // 1 is one root
+                SkTMax(fabs(t4), SkTMax(fabs(t3), SkTMax(fabs(t2), SkTMax(fabs(t1), fabs(t0)))))));
+        // note that -C == A + B + D + E
+        int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_equal(roots[i], 1)) {
+                return num;
+            }
+        }
+        roots[num++] = 1;
+        return num;
+    }
+    return -1;
+}
+
+int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
+        const double D, const double E, double s[4]) {
+    double  u, v;
+    /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
+    const double invA = 1 / A;
+    const double a = B * invA;
+    const double b = C * invA;
+    const double c = D * invA;
+    const double d = E * invA;
+    /*  substitute x = y - a/4 to eliminate cubic term:
+    x^4 + px^2 + qx + r = 0 */
+    const double a2 = a * a;
+    const double p = -3 * a2 / 8 + b;
+    const double q = a2 * a / 8 - a * b / 2 + c;
+    const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
+    int num;
+    double largest = SkTMax(fabs(p), fabs(q));
+    if (approximately_zero_when_compared_to(r, largest)) {
+    /* no absolute term: y(y^3 + py + q) = 0 */
+        num = SkDCubic::RootsReal(1, 0, p, q, s);
+        s[num++] = 0;
+    } else {
+        /* solve the resolvent cubic ... */
+        double cubicRoots[3];
+        int roots = SkDCubic::RootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
+        int index;
+        /* ... and take one real solution ... */
+        double z;
+        num = 0;
+        int num2 = 0;
+        for (index = firstCubicRoot; index < roots; ++index) {
+            z = cubicRoots[index];
+            /* ... to build two quadric equations */
+            u = z * z - r;
+            v = 2 * z - p;
+            if (approximately_zero_squared(u)) {
+                u = 0;
+            } else if (u > 0) {
+                u = sqrt(u);
+            } else {
+                continue;
+            }
+            if (approximately_zero_squared(v)) {
+                v = 0;
+            } else if (v > 0) {
+                v = sqrt(v);
+            } else {
+                continue;
+            }
+            num = SkDQuad::RootsReal(1, q < 0 ? -v : v, z - u, s);
+            num2 = SkDQuad::RootsReal(1, q < 0 ? v : -v, z + u, s + num);
+            if (!((num | num2) & 1)) {
+                break;  // prefer solutions without single quad roots
+            }
+        }
+        num += num2;
+        if (!num) {
+            return 0;  // no valid cubic root
+        }
+    }
+    /* resubstitute */
+    const double sub = a / 4;
+    for (int i = 0; i < num; ++i) {
+        s[i] -= sub;
+    }
+    // eliminate duplicates
+    for (int i = 0; i < num - 1; ++i) {
+        for (int j = i + 1; j < num; ) {
+            if (AlmostDequalUlps(s[i], s[j])) {
+                if (j < --num) {
+                    s[j] = s[num];
+                }
+            } else {
+                ++j;
+            }
+        }
+    }
+    return num;
+}
diff --git a/src/pathops/SkQuarticRoot.h b/src/pathops/SkQuarticRoot.h
new file mode 100644 (file)
index 0000000..6ce0867
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDQuarticRoot_DEFINED
+#define SkDQuarticRoot_DEFINED
+
+int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+        const double t0, const bool oneHint, double s[4]);
+
+int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
+        const double D, const double E, double s[4]);
+
+#endif
index c19cd3db4b0475ee2318a218657c2a4555fc37d9..6f06447a4715f5009d4241432e6ffba41d3dbb12 100644 (file)
@@ -272,11 +272,6 @@ SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
 }
 
 SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
-    if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
-            && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
-        reducePts[0] = a[0];
-        return SkPath::kMove_Verb;
-    }
     SkDCubic cubic;
     cubic.set(a);
     SkReduceOrder reducer;
index 397b58d927ef748833c7c2b9c7a28b2bb911f56d..4ff9a1d127e68ecdea02145d2f41ac8a3d9be77c 100644 (file)
@@ -7,9 +7,11 @@
 #ifndef SkReduceOrder_DEFINED
 #define SkReduceOrder_DEFINED
 
+#include "SkPath.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
+#include "SkTArray.h"
 
 union SkReduceOrder {
     enum Quadratics {
index 1a2bce77f208afbeefe824dc96c6b405b5f90811..901cab2bb5202a75dabc02ee922d766085752096 100755 (executable)
@@ -6,8 +6,8 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
-#include "SkOpContour.h"
 #include "SkOpSegment.h"
+#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
 #include "SkTArray.h"
 #include "SkTSort.h"
@@ -18,12 +18,12 @@ static bool gPathOpsAngleIdeasEnableBruteCheck = false;
 
 class PathOpsAngleTester {
 public:
-    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
-        return lh.convexHullOverlaps(&rh);
+    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
+        return lh.convexHullOverlaps(rh);
     }
 
-    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
-        return lh.endsIntersect(&rh);
+    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
+        return lh.endsIntersect(rh);
     }
 };
 
@@ -406,29 +406,28 @@ static bool bruteForceCheck(skiatest::Reporter* reporter, const SkDQuad& quad1,
     return ccw == upperRange.ccw;
 }
 
-static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3],
-        SkChunkAlloc* allocator) {
+class PathOpsSegmentTester {
+public:
+    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
+        segment->debugConstructQuad(shortQuad);
+    }
+};
+
+static void makeSegment(const SkDQuad& quad, SkPoint shortQuad[3], SkOpSegment* result) {
     shortQuad[0] = quad[0].asSkPoint();
     shortQuad[1] = quad[1].asSkPoint();
     shortQuad[2] = quad[2].asSkPoint();
-    contour->addQuad(shortQuad, allocator);
+    PathOpsSegmentTester::ConstructQuad(result, shortQuad);
 }
 
 static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
-        int testNo, SkChunkAlloc* allocator) {
+        int testNo) {
     SkPoint shortQuads[2][3];
-
-    SkOpContour contour;
-    SkOpGlobalState state(NULL  PATH_OPS_DEBUG_PARAMS(&contour));
-    contour.init(&state, false, false);
-    makeSegment(&contour, quad1, shortQuads[0], allocator);
-    makeSegment(&contour, quad1, shortQuads[1], allocator);
-    SkOpSegment* seg1 = contour.first();
-    seg1->debugAddAngle(0, 1, allocator);
-    SkOpSegment* seg2 = seg1->next();
-    seg2->debugAddAngle(0, 1, allocator);
-    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
-            *seg2->debugLastAngle());
+    SkOpSegment seg[2];
+    makeSegment(quad1, shortQuads[0], &seg[0]);
+    makeSegment(quad2, shortQuads[1], &seg[1]);
+    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg[0].debugLastAngle(),
+            *seg[1].debugLastAngle());
     const SkDPoint& origin = quad1[0];
     REPORTER_ASSERT(reporter, origin == quad2[0]);
     double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX);
@@ -546,27 +545,25 @@ static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, c
     }
     if (overlap < 0) {
         SkDEBUGCODE(int realEnds =)
-                PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(),
-                *seg2->debugLastAngle());
+                PathOpsAngleTester::EndsIntersect(*seg[0].debugLastAngle(),
+                *seg[1].debugLastAngle());
         SkASSERT(realEnds == (firstInside ? 1 : 0));
     }
     bruteForce(reporter, quad1, quad2, firstInside);
 }
 
 DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
-    SkChunkAlloc allocator(4096);
 //    gPathOpsAngleIdeasVerbose = true;
     const SkDQuad quads[] = {
 {{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
 {{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
     };
     for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) {
-        testQuadAngles(reporter, quads[index], quads[index + 1], 0, &allocator);
+        testQuadAngles(reporter, quads[index], quads[index + 1], 0);
     }
 }
 
 DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
-    SkChunkAlloc allocator(4096);
     if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
         return;
     }
@@ -590,7 +587,7 @@ DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
         if (i.used() > 1) {
             continue;
         }
-        testQuadAngles(reporter, quad1, quad2, index, &allocator);
+        testQuadAngles(reporter, quad1, quad2, index);
     }
 }
 
index db3e8644f14a0190277c6c2e82dcfaabb9257822..faf61584e6f483217bedd612bf557d709cf861b4 100644 (file)
@@ -6,9 +6,10 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
-#include "SkOpContour.h"
 #include "SkOpSegment.h"
+#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
+#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
@@ -190,20 +191,20 @@ DEF_TEST(PathOpsAngleFindSlop, reporter) {
 
 class PathOpsAngleTester {
 public:
-    static int After(SkOpAngle& lh, SkOpAngle& rh) {
+    static int After(const SkOpAngle& lh, const SkOpAngle& rh) {
         return lh.after(&rh);
     }
 
-    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
-        return lh.convexHullOverlaps(&rh);
+    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
+        return lh.convexHullOverlaps(rh);
     }
 
-    static int Orderable(SkOpAngle& lh, SkOpAngle& rh) {
-        return lh.orderable(&rh);
+    static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) {
+        return lh.orderable(rh);
     }
 
-    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
-        return lh.endsIntersect(&rh);
+    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
+        return lh.endsIntersect(rh);
     }
 
     static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
@@ -213,6 +214,18 @@ public:
 
 class PathOpsSegmentTester {
 public:
+    static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) {
+        segment->debugConstructCubic(shortCubic);
+    }
+
+    static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) {
+        segment->debugConstructLine(shortLine);
+    }
+
+    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
+        segment->debugConstructQuad(shortQuad);
+    }
+
     static void DebugReset(SkOpSegment* segment) {
         segment->debugReset();
     }
@@ -233,10 +246,7 @@ static CircleData circleDataSet[] = {
 static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
 
 DEF_TEST(PathOpsAngleCircle, reporter) {
-    SkOpContour contour;
-    SkOpGlobalState state(NULL  PATH_OPS_DEBUG_PARAMS(&contour));
-    contour.init(&state, false, false);
-    SkChunkAlloc allocator(4096);
+    SkOpSegment segment[2];
     for (int index = 0; index < circleDataSetSize; ++index) {
         CircleData& data = circleDataSet[index];
         for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
@@ -244,21 +254,17 @@ DEF_TEST(PathOpsAngleCircle, reporter) {
         }
         switch (data.fPtCount) {
             case 2:
-                contour.addLine(data.fShortPts, &allocator);
+                PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts);
                 break;
             case 3:
-                contour.addQuad(data.fShortPts, &allocator);
+                PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts);
                 break;
             case 4:
-                contour.addCubic(data.fShortPts, &allocator);
+                PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts);
                 break;
         }
     }
-    SkOpSegment* first = contour.first();
-    first->debugAddAngle(0, 1, &allocator);
-    SkOpSegment* next = first->next();
-    next->debugAddAngle(0, 1, &allocator);
-    PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
+    PathOpsAngleTester::Orderable(*segment[0].debugLastAngle(), *segment[1].debugLastAngle());
 }
 
 struct IntersectData {
@@ -373,39 +379,11 @@ static IntersectData intersectDataSet16[] = { // pathops_visualizer.htm:7419
     { {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
 }; //
 
-// from skpi_gino_com_16
-static IntersectData intersectDataSet17[] = {
-    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
-        , 3, 0.74590454, 0.547660352, {} },
-    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
-        , 4, 0.12052623, 0, {} },
-    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
-        , 3, 0.74590454, 1, {} },
-};
-
-static IntersectData intersectDataSet18[] = {
-    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
-        , 3, 0.74590454, 1, {} },
-    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
-        , 4, 0.12052623, 0.217351928, {} },
-    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
-        , 3, 0.74590454, 0.547660352, {} },
-};
-
-static IntersectData intersectDataSet19[] = {
-    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
-        , 4, 0.135148995, 0.134791946, {} },
-    { /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}}
-        , 4, 0.956740456, 0.894913214, {} },
-    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
-        , 4, 0.135148995, 0.551812363, {} },
-};
-
 #define I(x) intersectDataSet##x
 
 static IntersectData* intersectDataSets[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
+    I(11), I(12), I(13), I(14), I(15), I(16),
 };
 
 #undef I
@@ -413,55 +391,56 @@ static IntersectData* intersectDataSets[] = {
 
 static const int intersectDataSetSizes[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
+    I(11), I(12), I(13), I(14), I(15), I(16),
 };
 
 #undef I
 
 static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
 
-struct FourPoints {
-    SkPoint pts[4];
-};
-
 DEF_TEST(PathOpsAngleAfter, reporter) {
-    SkChunkAlloc allocator(4096);
-    SkOpContour contour;
-    SkOpGlobalState state(NULL  PATH_OPS_DEBUG_PARAMS(&contour));
-    contour.init(&state, false, false);
     for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
         IntersectData* dataArray = intersectDataSets[index];
         const int dataSize = intersectDataSetSizes[index];
+        SkOpSegment segment[3];
         for (int index2 = 0; index2 < dataSize - 2; ++index2) {
-            allocator.reset();
-            contour.reset();
-            for (int index3 = 0; index3 < 3; ++index3) {
+            for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) {
+                PathOpsSegmentTester::DebugReset(&segment[temp]);
+            }
+            for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) {
                 IntersectData& data = dataArray[index2 + index3];
-                SkPoint* temp = (SkPoint*) SkOpTAllocator<FourPoints>::Allocate(&allocator);
+                SkPoint temp[4];
                 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
                     temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
                 }
                 switch (data.fPtCount) {
                     case 2: {
-                        contour.addLine(temp, &allocator);
+                        SkDLine seg = SkDLine::SubDivide(temp, data.fTStart,
+                                data.fTStart < data.fTEnd ? 1 : 0);
+                        data.fShortPts[0] = seg[0].asSkPoint();
+                        data.fShortPts[1] = seg[1].asSkPoint();
+                        PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts);
                         } break;
                     case 3: {
-                        contour.addQuad(temp, &allocator);
+                        SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd);
+                        data.fShortPts[0] = seg[0].asSkPoint();
+                        data.fShortPts[1] = seg[1].asSkPoint();
+                        data.fShortPts[2] = seg[2].asSkPoint();
+                        PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts);
                         } break;
                     case 4: {
-                        contour.addCubic(temp, &allocator);
+                        SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd);
+                        data.fShortPts[0] = seg[0].asSkPoint();
+                        data.fShortPts[1] = seg[1].asSkPoint();
+                        data.fShortPts[2] = seg[2].asSkPoint();
+                        data.fShortPts[3] = seg[3].asSkPoint();
+                        PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts);
                         } break;
                 }
             }
-            SkOpSegment* seg1 = contour.first();
-            seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd, &allocator);
-            SkOpSegment* seg2 = seg1->next();
-            seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd, &allocator);
-            SkOpSegment* seg3 = seg2->next();
-            seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd, &allocator);
-            SkOpAngle& angle1 = *seg1->debugLastAngle();
-            SkOpAngle& angle2 = *seg2->debugLastAngle();
-            SkOpAngle& angle3 = *seg3->debugLastAngle();
+            SkOpAngle& angle1 = *const_cast<SkOpAngle*>(segment[0].debugLastAngle());
+            SkOpAngle& angle2 = *const_cast<SkOpAngle*>(segment[1].debugLastAngle());
+            SkOpAngle& angle3 = *const_cast<SkOpAngle*>(segment[2].debugLastAngle());
             PathOpsAngleTester::SetNext(angle1, angle3);
        // These data sets are seeded when the set itself fails, so likely the dataset does not
        // match the expected result. The tests above return 1 when first added, but
@@ -472,26 +451,35 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
     }
 }
 
-void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* allocator) {
-    SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
-            : this->addT(startT, kNoAlias, allocator);
-    SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
-            : this->addT(endT, kNoAlias, allocator);
-    SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
-    SkOpSpanBase* startSpan = &fHead;
-    while (startSpan->ptT() != startPtT) {
-        startSpan = startSpan->upCast()->next();
-    }
-    SkOpSpanBase* endSpan = &fHead;
-    while (endSpan->ptT() != endPtT) {
-        endSpan = endSpan->upCast()->next();
-    }
-    angle->set(startSpan, endSpan);
-    if (startT < endT) {
-        startSpan->upCast()->setToAngle(angle);
-        endSpan->setFromAngle(angle);
-    } else {
-        endSpan->upCast()->setToAngle(angle);
-        startSpan->setFromAngle(angle);
-    }
+void SkOpSegment::debugConstruct() {
+    addStartSpan(1);
+    addEndSpan(1);
+    debugAddAngle(0, 1);
+}
+
+void SkOpSegment::debugAddAngle(int start, int end) {
+    SkASSERT(start != end);
+    SkOpAngle& angle = fAngles.push_back();
+    angle.set(this, start, end);
+}
+
+void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) {
+    addCubic(shortQuad, false, false);
+    addT(NULL, shortQuad[0], 0);
+    addT(NULL, shortQuad[3], 1);
+    debugConstruct();
+}
+
+void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) {
+    addLine(shortQuad, false, false);
+    addT(NULL, shortQuad[0], 0);
+    addT(NULL, shortQuad[1], 1);
+    debugConstruct();
+}
+
+void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) {
+    addQuad(shortQuad, false, false);
+    addT(NULL, shortQuad[0], 0);
+    addT(NULL, shortQuad[2], 1);
+    debugConstruct();
 }
index fbe12c01b5a82620581a11f77127a8b716016920..455f2e967be6719b9ec3ded3659ceb0946e9d229 100644 (file)
@@ -59,6 +59,7 @@ path2.lineTo(SkBits2Float(0x422c58d6), SkBits2Float(0x422705c1));
 path2.cubicTo(SkBits2Float(0x42383446), SkBits2Float(0x421ac98f), SkBits2Float(0x4242b98a), SkBits2Float(0x420d5308), SkBits2Float(0x424bbb17), SkBits2Float(0x41fdb8ee));
 path2.lineTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6));
 path2.close();
+// SkOpSegment.cpp:3488: failed assertion "other->fTs[min].fWindSum == oppWinding"
     testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
 }
 
@@ -1295,7 +1296,7 @@ path.lineTo(SkBits2Float(0x4285e672), SkBits2Float(0xc2443b5f));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -1511,7 +1512,7 @@ path.lineTo(SkBits2Float(0x42a3a81d), SkBits2Float(0xc15e595e));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -1753,7 +1754,7 @@ path.lineTo(SkBits2Float(0x4039d102), SkBits2Float(0xc2a5e5fe));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -1883,7 +1884,11 @@ path.lineTo(SkBits2Float(0x3ee8b040), SkBits2Float(0xc2a5ff5d));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    if (FLAGS_runFail) {
+        testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    } else {
+        testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    }
 }
 // op end success 1
 
@@ -3977,7 +3982,7 @@ path.lineTo(SkBits2Float(0x42a38b52), SkBits2Float(0xc1639578));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -4087,7 +4092,7 @@ path.lineTo(SkBits2Float(0x42a5fe22), SkBits2Float(0x3f4744a1));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -4235,7 +4240,7 @@ path.lineTo(SkBits2Float(0x429c4e4c), SkBits2Float(0x41df969b));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -4409,7 +4414,7 @@ path.lineTo(SkBits2Float(0x428cfdb5), SkBits2Float(0x422f3e36));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -4831,7 +4836,7 @@ path.lineTo(SkBits2Float(0x425b4ae0), SkBits2Float(0x427944c0));
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -4951,7 +4956,7 @@ path.lineTo(SkBits2Float(0x424f88ba), SkBits2Float(0x428191f0));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -5362,7 +5367,7 @@ path.lineTo(SkBits2Float(0x3fc9081a), SkBits2Float(0xc2a5f864));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -5438,7 +5443,7 @@ path.lineTo(SkBits2Float(0x40848cae), SkBits2Float(0xc2a5cb0c));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -6292,7 +6297,7 @@ path.lineTo(SkBits2Float(0x429ff91f), SkBits2Float(0xc1b14b8a));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -7049,7 +7054,7 @@ path.lineTo(SkBits2Float(0x4273ad4f), SkBits2Float(0x42617d52));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -7438,7 +7443,7 @@ path.lineTo(SkBits2Float(0x427e3109), SkBits2Float(0x42559108));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -7555,7 +7560,7 @@ path.lineTo(SkBits2Float(0x4279eebd), SkBits2Float(0x425a890e));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -7858,7 +7863,7 @@ path.lineTo(SkBits2Float(0x42759f2b), SkBits2Float(0x425f5e9b));
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 // op end success 1
 
@@ -10681,7 +10686,7 @@ path.close();
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp1394;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp68;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
@@ -11123,5 +11128,5 @@ DEF_TEST(PathOpsBattle, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, NULL, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
 }
index 5fdeb3e2b9e2a06766c05eab00dd340703f56abd..1eadebc550a920f0dbdc35ca758da28723243876 100644 (file)
@@ -4,10 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-
-#include "PathOpsExtendedTest.h"
 #include "PathOpsTestCommon.h"
-#include "SkBitmap.h"
 #include "Test.h"
 
 DEF_TEST(PathOpsBuilder, reporter) {
@@ -25,7 +22,6 @@ DEF_TEST(PathOpsBuilder, reporter) {
     REPORTER_ASSERT(reporter, result.isEmpty());
 
     SkPath rectPath;
-    rectPath.setFillType(SkPath::kEvenOdd_FillType);
     rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
     builder.add(rectPath, kUnion_PathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
@@ -37,14 +33,13 @@ DEF_TEST(PathOpsBuilder, reporter) {
     REPORTER_ASSERT(reporter, rectPath == result);
 
     rectPath.reset();
-    rectPath.setFillType(SkPath::kEvenOdd_FillType);
     rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
     builder.add(rectPath, kUnion_PathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
     REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
-     REPORTER_ASSERT(reporter, rectPath == result);
+    REPORTER_ASSERT(reporter, rectPath == result);
 
     builder.add(rectPath, kDifference_PathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
@@ -74,7 +69,5 @@ DEF_TEST(PathOpsBuilder, reporter) {
     builder.add(circle2, kUnion_PathOp);
     builder.add(circle3, kDifference_PathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
-    SkBitmap bitmap;
-    int pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result, bitmap);
-    REPORTER_ASSERT(reporter, pixelDiff == 0);
+    REPORTER_ASSERT(reporter, opCompare == result);
 }
index a424c50c5567b19273a6e0b10bf244862cb4ab78..b6a9e5910af766af170a69c6e190ff87400518e6 100644 (file)
@@ -6,7 +6,6 @@
  */
 #include "PathOpsCubicIntersectionTestData.h"
 #include "PathOpsTestCommon.h"
-#include "SkGeometry.h"
 #include "SkIntersections.h"
 #include "SkPathOpsRect.h"
 #include "SkReduceOrder.h"
@@ -163,60 +162,6 @@ static const SkDCubic testSet[] = {
 const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
 
 static const SkDCubic newTestSet[] = {
-{{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27576}, {980.025879,1481.27527}}},
-{{{980.025879,1481.27527}, {980.025452,1481.27222}, {980.023743,1481.26038}, {980.02179,1481.24072}}},
-
-{{{1.80943513,3.07782435}, {1.66686702,2.16806936}, {1.68301272,0}, {3,0}}},
-{{{0,1}, {0,3}, {3,2}, {5,2}}},
-
-{{{3.4386673,2.66977954}, {4.06668949,2.17046738}, {4.78887367,1.59629118}, {6,2}}},
-{{{1.71985495,3.49467373}, {2.11620402,2.7201426}, {2.91897964,1.15138781}, {6,3}}},
-
-{{{0,1}, {0.392703831,1.78540766}, {0.219947904,2.05676103}, {0.218561709,2.05630541}}},
-{{{0.218561709,2.05630541}, {0.216418028,2.05560064}, {0.624105453,1.40486407}, {4.16666651,1.00000012}}},
-
-{{{0, 1}, {3, 5}, {2, 1}, {3, 1}}},
-{{{1.01366711f, 2.21379328f}, {1.09074128f, 2.23241305f}, {1.60246587f, 0.451849401f}, {5, 3}}},
-
-{{{0, 1}, {0.541499972f, 3.16599989f}, {1.08299994f, 2.69299984f}, {2.10083938f, 1.80391729f}}},
-{{{0.806384504f, 2.85426903f}, {1.52740121f, 1.99355423f}, {2.81689167f, 0.454222918f}, {5, 1}}},
-
-{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
-{{{2, 3}, {2.36602545f, 3.36602545f}, {2.330127f, 3.06217766f}, {2.28460979f, 2.67691422f}}},
-
-{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
-{{{2.28460979f, 2.67691422f}, {2.20577145f, 2.00961876f}, {2.09807634f, 1.09807622f}, {4, 3}}},
-
-{{{0, 1}, {0.8211091160774231, 2.0948121547698975}, {0.91805583238601685, 2.515404224395752}, {0.91621249914169312, 2.5146586894989014}}},
-{{{0.91621249914169312, 2.5146586894989014}, {0.91132104396820068, 2.5126807689666748}, {0.21079301834106445, -0.45617169141769409}, {10.5, -1.6666665077209473}}},
-
-{{{42.6237564,68.9841232}, {32.449646,81.963089}, {14.7713947,103.565269}, {12.6310005,105.247002}}},
-{{{37.2640038,95.3540039}, {37.2640038,95.3540039}, {11.3710003,83.7339935}, {-25.0779991,124.912003}}},
-
-{{{0,1}, {4,5}, {6,0}, {1,0}}},
-{{{0,6}, {0,1}, {1,0}, {5,4}}},
-
-{{{0,1}, {4,6}, {5,1}, {6,2}}},
-{{{1,5}, {2,6}, {1,0}, {6,4}}},
-
-{{{322, 896.04803466796875}, {314.09201049804687, 833.4376220703125}, {260.24713134765625, 785}, {195, 785}}},
-{{{195, 785}, {265.14016723632812, 785}, {322, 842.30755615234375}, {322, 913}}},
-
-{{{1, 4}, {4, 5}, {3, 2}, {6, 3}}},
-{{{2, 3}, {3, 6}, {4, 1}, {5, 4}}},
-
-{{{67, 913}, {67, 917.388916015625}, {67.224380493164063, 921.72576904296875}, {67.662384033203125, 926}}},
-{{{194, 1041}, {123.85984039306641, 1041}, {67, 983.69244384765625}, {67, 913}}},
-
-{{{1,4}, {1,5}, {6,0}, {5,1}}},
-{{{0,6}, {1,5}, {4,1}, {5,1}}},
-
-{{{0,1}, {4,5}, {6,0}, {1,0}}},
-{{{0,6}, {0,1}, {1,0}, {5,4}}},
-
-{{{0,1}, {4,6}, {2,0}, {2,0}}},
-{{{0,2}, {0,2}, {1,0}, {6,4}}},
-
 {{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}},
 {{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}},
 
@@ -361,6 +306,7 @@ static const SkDCubic newTestSet[] = {
 };
 
 const int newTestSetCount = (int) SK_ARRAY_COUNT(newTestSet);
+
 static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
         bool coin) {
     SkASSERT(ValidCubic(cubic1));
@@ -374,22 +320,28 @@ static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const S
         cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
         cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
 #endif
-#if DEBUG_T_SECT_DUMP > 1
-    gDumpTSectNum = 0;
-#endif
-    SkIntersections intersections;
-    intersections.intersect(cubic1, cubic2);
-#if DEBUG_T_SECT_DUMP == 3
-    SkDebugf("</div>\n\n");
-    SkDebugf("<script type=\"text/javascript\">\n\n");
-    SkDebugf("var testDivs = [\n");
-    for (int index = 1; index <= gDumpTSectNum; ++index) {
-        SkDebugf("sect%d,\n", index);
+    SkTArray<SkDQuad, true> quads1;
+    CubicToQuads(cubic1, cubic1.calcPrecision(), quads1);
+#if ONE_OFF_DEBUG
+    SkDebugf("computed quadratics set 1\n");
+    for (int index = 0; index < quads1.count(); ++index) {
+        const SkDQuad& q = quads1[index];
+        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
+                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
     }
 #endif
-    if (coin && intersections.used() != 2) {
-        SkDebugf("");
+    SkTArray<SkDQuad, true> quads2;
+    CubicToQuads(cubic2, cubic2.calcPrecision(), quads2);
+#if ONE_OFF_DEBUG
+    SkDebugf("computed quadratics set 2\n");
+    for (int index = 0; index < quads2.count(); ++index) {
+        const SkDQuad& q = quads2[index];
+        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
+                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
     }
+#endif
+    SkIntersections intersections;
+    intersections.intersect(cubic1, cubic2);
     REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
     double tt1, tt2;
     SkDPoint xy1, xy2;
@@ -606,30 +558,37 @@ int selfSetCount = (int) SK_ARRAY_COUNT(selfSet);
 
 static void selfOneOff(skiatest::Reporter* reporter, int index) {
     const SkDCubic& cubic = selfSet[index];
-    SkPoint c[4];
-    for (int i = 0; i < 4; ++i) {
-        c[i] = cubic[i].asSkPoint();
+#if ONE_OFF_DEBUG
+    int idx2;
+    double max[3];
+    int ts = cubic.findMaxCurvature(max);
+    for (idx2 = 0; idx2 < ts; ++idx2) {
+        SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
+                max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY);
     }
-    SkScalar loopT;
-    SkScalar d[3];
-    SkCubicType cubicType = SkClassifyCubic(c, d);
-    if (SkDCubic::ComplexBreak(c, &loopT) && cubicType == SkCubicType::kLoop_SkCubicType) {
-        SkIntersections i;
-        SkPoint twoCubics[7];
-        SkChopCubicAt(c, twoCubics, loopT);
-        SkDCubic chopped[2];
-        chopped[0].set(&twoCubics[0]);
-        chopped[1].set(&twoCubics[3]);
-        int result = i.intersect(chopped[0], chopped[1]);
-        REPORTER_ASSERT(reporter, result == 2);
-        REPORTER_ASSERT(reporter, i.used() == 2);
-        for (int index = 0; index < result; ++index) {
-            SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
-            SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
-            REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
-            reporter->bumpTestCount();
-        }
+    SkTArray<double, true> ts1;
+    SkTArray<SkDQuad, true> quads1;
+    cubic.toQuadraticTs(cubic.calcPrecision(), &ts1);
+    for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
+        SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
+    }
+    CubicToQuads(cubic, cubic.calcPrecision(), quads1);
+    for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
+        const SkDQuad& q = quads1[idx2];
+        SkDebugf("  {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n",
+                q[0].fX, q[0].fY,  q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
     }
+    SkDebugf("\n");
+#endif
+    SkIntersections i;
+    int result = i.intersect(cubic);
+    REPORTER_ASSERT(reporter, result == 1);
+    REPORTER_ASSERT(reporter, i.used() == 1);
+    REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0]));
+    SkDPoint pt1 = cubic.ptAtT(i[0][0]);
+    SkDPoint pt2 = cubic.ptAtT(i[1][0]);
+    REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
+    reporter->bumpTestCount();
 }
 
 static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
@@ -640,12 +599,12 @@ static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
 }
 
 static const SkDCubic coinSet[] = {
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
-
     {{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}},
     {{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125},
             {324.71566772460937, 714.62060546875}, {325, 714.9990234375}}},
+
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
 };
 
 static int coinSetCount = (int) SK_ARRAY_COUNT(coinSet);
@@ -683,72 +642,3 @@ DEF_TEST(PathOpsCubicIntersection, reporter) {
     if (false) CubicIntersection_IntersectionFinder();
     if (false) CubicIntersection_RandTest(reporter);
 }
-
-static void binaryTest(const SkDCubic& cubic1, const SkDCubic& cubic2,
-        skiatest::Reporter* reporter) {
-    SkASSERT(ValidCubic(cubic1));
-    SkASSERT(ValidCubic(cubic2));
-    SkIntersections intersections;
-    SkReduceOrder reduce1, reduce2;
-    int order1 = reduce1.reduce(cubic1, SkReduceOrder::kNo_Quadratics);
-    int order2 = reduce2.reduce(cubic2, SkReduceOrder::kNo_Quadratics);
-    if (order1 == 4 && order2 == 4) {
-        intersections.intersect(cubic1, cubic2);
-    } else {
-        intersections.reset();
-    }
-    SkIntersections intersections2;
-    (void) intersections2.intersect(cubic1, cubic2);
-    REPORTER_ASSERT(reporter, intersections.used() <= intersections2.used()
-            || intersections[0][0] + 0.01 > intersections[0][1]);
-    for (int index = 0; index < intersections2.used(); ++index) {
-//            SkASSERT(intersections.pt(index).approximatelyEqual(intersections2.pt(index)));
-        double tt1 = intersections2[0][index];
-        SkDPoint xy1 = cubic1.ptAtT(tt1);
-        double tt2 = intersections2[1][index];
-        SkDPoint xy2 = cubic2.ptAtT(tt2);
-        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
-    }
-}
-
-DEF_TEST(PathOpsCubicBinaryTest, reporter) {
-    int outer = 0;
-    int inner = outer + 1;
-    do {
-        const SkDCubic& cubic1 = testSet[outer];
-        const SkDCubic& cubic2 = testSet[inner];
-        binaryTest(cubic1, cubic2, reporter);
-        inner += 2;
-        outer += 2;
-    } while (outer < (int) testSetCount);
-}
-
-DEF_TEST(PathOpsCubicBinaryNew, reporter) {
-    int outer = 62;
-    int inner = outer + 1;
-    do {
-        const SkDCubic& cubic1 = newTestSet[outer];
-        const SkDCubic& cubic2 = newTestSet[inner];
-        binaryTest(cubic1, cubic2, reporter);
-        inner += 2;
-        outer += 2;
-    } while (outer < (int) newTestSetCount);
-}
-
-DEF_TEST(PathOpsCubicBinaryStd, reporter) {
-    const int firstTest = 0;
-    for (size_t index = firstTest; index < tests_count; ++index) {
-        const SkDCubic& cubic1 = tests[index][0];
-        const SkDCubic& cubic2 = tests[index][1];
-        binaryTest(cubic1, cubic2, reporter);
-    }
-}
-
-DEF_TEST(PathOpsCubicBinaryCoin, reporter) {
-    int firstFail = 0;
-    for (int index = firstFail; index < coinSetCount; index += 2) {
-        const SkDCubic& cubic1 = coinSet[index];
-        const SkDCubic& cubic2 = coinSet[index + 1];
-        binaryTest(cubic1, cubic2, reporter);
-    }
-}
index 7f725ef9fd8e08865a2e9b5e69ecfafed654bb85..31056d255294d57809d23a61dc3851e621a440e6 100644 (file)
@@ -46,8 +46,8 @@ const SkDCubic pointDegenerates[] = {
 const size_t pointDegenerates_count = SK_ARRAY_COUNT(pointDegenerates);
 
 const SkDCubic notPointDegenerates[] = {
-    {{{1 + FLT_EPSILON * 8, 1}, {1, FLT_EPSILON * 8}, {1, 1}, {1, 1}}},
-    {{{1 + FLT_EPSILON * 8, 1}, {1 - FLT_EPSILON * 8, 1}, {1, 1}, {1, 1}}}
+    {{{1 + FLT_EPSILON * 2, 1}, {1, FLT_EPSILON * 2}, {1, 1}, {1, 1}}},
+    {{{1 + FLT_EPSILON * 2, 1}, {1 - FLT_EPSILON * 2, 1}, {1, 1}, {1, 1}}}
 };
 
 const size_t notPointDegenerates_count =
@@ -156,8 +156,8 @@ const SkDCubic notLines[] = {
 
 const size_t notLines_count = SK_ARRAY_COUNT(notLines);
 
-static const double E = FLT_EPSILON * 8;
-static const double F = FLT_EPSILON * 8;
+static const double E = FLT_EPSILON * 2;
+static const double F = FLT_EPSILON * 3;
 
 const SkDCubic modEpsilonLines[] = {
     {{{0, E}, {0, 0}, {0, 0}, {1, 0}}},  // horizontal
@@ -187,8 +187,8 @@ const SkDCubic modEpsilonLines[] = {
     {{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}}},
     {{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}},  // first-middle middle-last coincident
     {{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}},  // no coincident
-    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F+F}}},  // INVESTIGATE: why the epsilon is bigger
-    {{{1, 1+F+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
     {{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}}},
     {{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}}},
     {{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}}},
index 6fdce3cb18ab635b2d589966c472ec70350315a8..234a53805f6aad63ce90f75a97bba961feea1235 100644 (file)
@@ -49,9 +49,6 @@ static void testFail(skiatest::Reporter* reporter, int iIndex) {
 }
 
 static lineCubic lineCubicTests[] = {
-    {{{{0.468027353,4}, {1.06734705,1.33333337}, {1.36700678,0}, {3,0}}},
-    {{{2,1}, {0,1}}}},
-
     {{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875},
             {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}},
             {{{-287.9506133720805678, -557.1376476615772617},
index d1ce05b638122c4eff9eb9e32c6623b5bb3f5593..967dfc7f4e44132007dcbc8128777dc202c8ac76 100644 (file)
 static struct quadCubic {
     SkDCubic cubic;
     SkDQuad quad;
+    int answerCount;
+    SkDPoint answers[2];
 } quadCubicTests[] = {
+#if 0  // FIXME : this should not fail (root problem behind skpcarrot_is24 )
     {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
-     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}},
+     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1,
+      {{1019.421, 662.449}}},
+#endif
 
     {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
-     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
+     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
+     {{778, 14089}, {776.82855609581270,14091.828250841330}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}},
+     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2,
+      {{1110, 817}, {1110.70715f, 817.292908f}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}},
+     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2,
+      {{1110.70715f, 817.292908f}, {1111, 818}}},
 
     {{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}},
      {{{55, 207}, {52.929431915283203, 206.99949645996094},
-       {51.464466094970703, 205.53553771972656}}}},
+       {51.464466094970703, 205.53553771972656}}}, 2,
+      {{55, 207}, {51.464466094970703, 205.53553771972656}}},
 
     {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}},
      {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688},
-       {34.355339050292969, 82.355339050292969}}}},
+       {34.355339050292969, 82.355339050292969}}}, 2,
+      {{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}},
+     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
+      {{18,226}, {0,0}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}},
+     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
+      {{10,234}, {0,0}}},
 };
 
 static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests);
@@ -63,9 +75,9 @@ static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
         SkDebugf("[%d] quad order=%d\n", iIndex, order2);
         REPORTER_ASSERT(reporter, 0);
     }
-    SkDCubic quadToCubic = quad.toCubic();
     SkIntersections i;
-    int roots = i.intersect(cubic, quadToCubic);
+    int roots = i.intersect(cubic, quad);
+    SkASSERT(roots == quadCubicTests[index].answerCount);
     for (int pt = 0; pt < roots; ++pt) {
         double tt1 = i[0][pt];
         SkDPoint xy1 = cubic.ptAtT(tt1);
@@ -76,6 +88,15 @@ static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
                 __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
         }
         REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+        bool found = false;
+        for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
+            found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
+        }
+        if (!found) {
+            SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
+                __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
+        }
+        REPORTER_ASSERT(reporter, found);
     }
     reporter->bumpTestCount();
 }
@@ -90,3 +111,195 @@ DEF_TEST(PathOpsCubicQuadIntersection, reporter) {
 DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) {
     cubicQuadIntersection(reporter, 0);
 }
+
+static bool gPathOpCubicQuadSlopVerbose = false;
+static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
+
+// determine that slop required after quad/quad finds a candidate intersection
+// use the cross of the tangents plus the distance from 1 or 0 as knobs
+DEF_TEST(PathOpsCubicQuadSlop, reporter) {
+    // create a random non-selfintersecting cubic
+    // break it into quadratics
+    // offset the quadratic, measuring the slop required to find the intersection
+    if (!gPathOpCubicQuadSlopVerbose) {  // takes a while to run -- so exclude it by default
+        return;
+    }
+    int results[101];
+    sk_bzero(results, sizeof(results));
+    double minCross[101];
+    sk_bzero(minCross, sizeof(minCross));
+    double maxCross[101];
+    sk_bzero(maxCross, sizeof(maxCross));
+    double sumCross[101];
+    sk_bzero(sumCross, sizeof(sumCross));
+    int foundOne = 0;
+    int slopCount = 1;
+    SkRandom ran;
+    for (int index = 0; index < 10000000; ++index) {
+        if (index % 1000 == 999) SkDebugf(".");
+        SkDCubic cubic = {{
+                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
+        }};
+        SkIntersections i;
+        if (i.intersect(cubic)) {
+            continue;
+        }
+        SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
+        cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
+        double tStart = 0;
+        int tsCount = ts.count();
+        for (int i1 = 0; i1 <= tsCount; ++i1) {
+            const double tEnd = i1 < tsCount ? ts[i1] : 1;
+            SkDCubic part = cubic.subDivide(tStart, tEnd);
+            SkDQuad quad = part.toQuad();
+            SkReduceOrder reducer;
+            int order = reducer.reduce(quad);
+            if (order != 3) {
+                continue;
+            }
+            for (int i2 = 0; i2 < 100; ++i2) {
+                SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
+                SkDQuad nearby = {{
+                        {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
+                        {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
+                        {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
+                }};
+                order = reducer.reduce(nearby);
+                if (order != 3) {
+                    continue;
+                }
+                SkIntersections locals;
+                locals.allowNear(false);
+                locals.intersect(quad, nearby);
+                if (locals.used() != 1) {
+                    continue;
+                }
+                // brute force find actual intersection
+                SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
+                SkIntersections liner;
+                int i3;
+                int found = -1;
+                int foundErr = true;
+                for (i3 = 1; i3 <= 1000; ++i3) {
+                    cubicLine[0] = cubicLine[1];
+                    cubicLine[1] = cubic.ptAtT(i3 / 1000.);
+                    liner.reset();
+                    liner.allowNear(false);
+                    liner.intersect(nearby, cubicLine);
+                    if (liner.used() == 0) {
+                        continue;
+                    }
+                    if (liner.used() > 1) {
+                        foundErr = true;
+                        break;
+                    }
+                    if (found > 0) {
+                        foundErr = true;
+                        break;
+                    }
+                    foundErr = false;
+                    found = i3;
+                }
+                if (foundErr) {
+                    continue;
+                }
+                SkDVector dist = liner.pt(0) - locals.pt(0);
+                SkDVector qV = nearby.dxdyAtT(locals[0][0]);
+                double cubicT = (found - 1 + liner[1][0]) / 1000.;
+                SkDVector cV = cubic.dxdyAtT(cubicT);
+                double qxc = qV.crossCheck(cV);
+                double qvLen = qV.length();
+                double cvLen = cV.length();
+                double maxLen = SkTMax(qvLen, cvLen);
+                qxc /= maxLen;
+                double quadT = tStart + (tEnd - tStart) * locals[0][0];
+                double diffT = fabs(cubicT - quadT);
+                int diffIdx = (int) (diffT * 100);
+                results[diffIdx]++;
+                double absQxc = fabs(qxc);
+                if (sumCross[diffIdx] == 0) {
+                    minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
+                } else {
+                    minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
+                    maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
+                    sumCross[diffIdx] +=  absQxc;
+                }
+                if (diffIdx >= 20) {
+#if 01
+                    SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+                        " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+                        " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+                        " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
+                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
+                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
+                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
+                        nearby[2].fX, nearby[2].fY,
+                        liner.pt(0).fX, liner.pt(0).fY,
+                        locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
+#else
+                    SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
+                        quadT, cubicT, dist.length(), qxc);
+                    SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
+                    SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
+                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
+                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
+                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
+                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
+                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
+                        nearby[2].fX, nearby[2].fY,
+                        liner.pt(0).fX, liner.pt(0).fY,
+                        locals.pt(0).fX, locals.pt(0).fY);
+                    SkDebugf("</div>\n\n");
+#endif
+                }
+                ++foundOne;
+            }
+            tStart = tEnd;
+        }
+        if (++foundOne >= 100000) {
+            break;
+        }
+    }
+#if 01
+    SkDebugf("slopCount=%d\n", slopCount);
+    int max = 100;
+    while (results[max] == 0) {
+        --max;
+    }
+    for (int i = 0; i <= max; ++i) {
+        if (i > 0 && i % 10 == 0) {
+            SkDebugf("\n");
+        }
+        SkDebugf("%d ", results[i]);
+    }
+    SkDebugf("min\n");
+    for (int i = 0; i <= max; ++i) {
+        if (i > 0 && i % 10 == 0) {
+            SkDebugf("\n");
+        }
+        SkDebugf("%1.9g ", minCross[i]);
+    }
+    SkDebugf("max\n");
+    for (int i = 0; i <= max; ++i) {
+        if (i > 0 && i % 10 == 0) {
+            SkDebugf("\n");
+        }
+        SkDebugf("%1.9g ", maxCross[i]);
+    }
+    SkDebugf("avg\n");
+    for (int i = 0; i <= max; ++i) {
+        if (i > 0 && i % 10 == 0) {
+            SkDebugf("\n");
+        }
+        SkDebugf("%1.9g ", sumCross[i] / results[i]);
+    }
+#else
+    for (int i = 1; i < slopCount; ++i) {
+        SkDebugf("        slop%d,\n", i);
+    }
+#endif
+    SkDebugf("\n");
+}
diff --git a/tests/PathOpsCubicToQuadsTest.cpp b/tests/PathOpsCubicToQuadsTest.cpp
new file mode 100644 (file)
index 0000000..ab22a83
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsCubicIntersectionTestData.h"
+#include "PathOpsQuadIntersectionTestData.h"
+#include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
+#include "SkIntersections.h"
+#include "SkPathOpsRect.h"
+#include "SkReduceOrder.h"
+#include "Test.h"
+
+static void test(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
+                 int firstTest, size_t testCount) {
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const SkDCubic& cubic = cubics[index];
+        SkASSERT(ValidCubic(cubic));
+        double precision = cubic.calcPrecision();
+        SkTArray<SkDQuad, true> quads;
+        CubicToQuads(cubic, precision, quads);
+        if (quads.count() != 1 && quads.count() != 2) {
+            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
+                    quads.count());
+        }
+        REPORTER_ASSERT(reporter, quads.count() == 1);
+    }
+}
+
+static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name,
+                 int firstTest, size_t testCount) {
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const SkDQuad& quad = quadTests[index];
+        SkASSERT(ValidQuad(quad));
+        SkDCubic cubic = quad.toCubic();
+        double precision = cubic.calcPrecision();
+        SkTArray<SkDQuad, true> quads;
+        CubicToQuads(cubic, precision, quads);
+        if (quads.count() != 1 && quads.count() != 2) {
+            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
+                    quads.count());
+        }
+        REPORTER_ASSERT(reporter, quads.count() <= 2);
+    }
+}
+
+static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
+                  int firstTest, size_t testCount) {
+    // test if computed line end points are valid
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const SkDCubic& cubic = cubics[index];
+        SkASSERT(ValidCubic(cubic));
+        double precision = cubic.calcPrecision();
+        SkTArray<SkDQuad, true> quads;
+        CubicToQuads(cubic, precision, quads);
+        if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
+                || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
+            SkDebugf("[%d] unmatched start\n", static_cast<int>(index));
+            REPORTER_ASSERT(reporter, 0);
+        }
+        int last = quads.count() - 1;
+        if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
+                || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
+            SkDebugf("[%d] unmatched end\n", static_cast<int>(index));
+            REPORTER_ASSERT(reporter, 0);
+        }
+    }
+}
+
+static void testC(skiatest::Reporter* reporter, const SkDCubic(* cubics)[2], const char* name,
+                  int firstTest, size_t testCount) {
+    for (size_t index = firstTest; index < testCount; ++index) {
+        for (int idx2 = 0; idx2 < 2; ++idx2) {
+            const SkDCubic& cubic = cubics[index][idx2];
+            SkASSERT(ValidCubic(cubic));
+            double precision = cubic.calcPrecision();
+            SkTArray<SkDQuad, true> quads;
+            CubicToQuads(cubic, precision, quads);
+            if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
+                    || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
+                SkDebugf("[%d][%d] unmatched start\n", static_cast<int>(index), idx2);
+                REPORTER_ASSERT(reporter, 0);
+            }
+            int last = quads.count() - 1;
+            if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
+                    || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
+                SkDebugf("[%d][%d] unmatched end\n", static_cast<int>(index), idx2);
+                REPORTER_ASSERT(reporter, 0);
+            }
+        }
+    }
+}
+
+DEF_TEST(CubicToQuads, reporter) {
+    enum {
+        RunAll,
+        RunPointDegenerates,
+        RunNotPointDegenerates,
+        RunLines,
+        RunNotLines,
+        RunModEpsilonLines,
+        RunLessEpsilonLines,
+        RunNegEpsilonLines,
+        RunQuadraticLines,
+        RunQuadraticModLines,
+        RunComputedLines,
+        RunComputedTests,
+        RunNone
+    } run = RunAll;
+    int firstTestIndex = 0;
+#if 0
+    run = RunComputedLines;
+    firstTestIndex = 18;
+#endif
+    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates
+            ? firstTestIndex : SK_MaxS32;
+    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates
+            ? firstTestIndex : SK_MaxS32;
+    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
+    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
+    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines
+            ? firstTestIndex : SK_MaxS32;
+    int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests
+            ? firstTestIndex : SK_MaxS32;
+
+    test(reporter, pointDegenerates, "pointDegenerates", firstPointDegeneratesTest,
+            pointDegenerates_count);
+    testC(reporter, notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest,
+            notPointDegenerates_count);
+    test(reporter, lines, "lines", firstLinesTest, lines_count);
+    testC(reporter, notLines, "notLines", firstNotLinesTest, notLines_count);
+    testC(reporter, modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
+    test(reporter, lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest,
+            lessEpsilonLines_count);
+    test(reporter, negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
+    test(reporter, quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
+    test(reporter, quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
+            quadraticModEpsilonLines_count);
+    testC(reporter, lines, "computed lines", firstComputedLinesTest, lines_count);
+    testC(reporter, tests, "computed tests", firstComputedCubicsTest, tests_count);
+}
+
+static SkDCubic locals[] = {
+    {{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002},
+            {0.0017190297609673323, 0.99828097023903239},
+            {0.0053709083094631276, 0.99505672974365911}}},
+    {{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139},
+            {8.03767257, 89.1628526}}},
+    {{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441},
+            {80.392774, 61.3533852}}},
+    {{{60.776536520932126, 71.249307306133829}, {87.107894191103014, 22.377669868235323},
+            {1.4974754310666936, 68.069569937917208}, {45.261946574441133, 17.536076632112298}}},
+};
+
+static size_t localsCount = SK_ARRAY_COUNT(locals);
+
+#define DEBUG_CRASH 0
+#define TEST_AVERAGE_END_POINTS 0  // must take const off to test
+extern const bool AVERAGE_END_POINTS;
+
+static void oneOff(skiatest::Reporter* reporter, size_t x) {
+    const SkDCubic& cubic = locals[x];
+    SkASSERT(ValidCubic(cubic));
+    const SkPoint skcubic[4] = {
+            {static_cast<float>(cubic[0].fX), static_cast<float>(cubic[0].fY)},
+            {static_cast<float>(cubic[1].fX), static_cast<float>(cubic[1].fY)},
+            {static_cast<float>(cubic[2].fX), static_cast<float>(cubic[2].fY)},
+            {static_cast<float>(cubic[3].fX), static_cast<float>(cubic[3].fY)}};
+    SkScalar skinflect[2];
+    int skin = SkFindCubicInflections(skcubic, skinflect);
+    if (false) SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
+    SkTArray<SkDQuad, true> quads;
+    double precision = cubic.calcPrecision();
+    CubicToQuads(cubic, precision, quads);
+    if (false) SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
+}
+
+DEF_TEST(CubicsToQuadratics_OneOff_Loop, reporter) {
+    for (size_t x = 0; x < localsCount; ++x) {
+        oneOff(reporter, x);
+    }
+}
+
+DEF_TEST(CubicsToQuadratics_OneOff_Single, reporter) {
+    oneOff(reporter, 0);
+}
index bfad134f77c593248d93920e8fcd550c222e61c9..dd86dd39e3a1e28099fa3c573c561123e3e3b8d4 100644 (file)
@@ -43,6 +43,10 @@ DEF_TEST(PathOpsLineUtilities, reporter) {
             SkDebugf("%s [%d] expected left\n", __FUNCTION__, index);
             REPORTER_ASSERT(reporter, 0);
         }
+        line2 = line.subDivide(1, 0);
+        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
+        line2 = SkDLine::SubDivide(pts, 1, 0);
+        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
         SkDPoint mid = line.ptAtT(.5);
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fX + line[1].fX) / 2, mid.fX));
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fY + line[1].fY) / 2, mid.fY));
index e197d5d618eecaa185fc2523a5bf526a3e521602..186d691d1096b34e603e76def8c690a3cb154a84 100644 (file)
@@ -38,6 +38,7 @@ DEF_TEST(PathOpsDPoint, reporter) {
         REPORTER_ASSERT(reporter, p == pt);
         REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt));
         REPORTER_ASSERT(reporter, p.roughlyEqual(pt));
+        REPORTER_ASSERT(reporter, p.moreRoughlyEqual(pt));
         p.fX = p.fY = 0;
         REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0);
         REPORTER_ASSERT(reporter, p.approximatelyZero());
diff --git a/tests/PathOpsDQuadTest.cpp b/tests/PathOpsDQuadTest.cpp
new file mode 100644 (file)
index 0000000..bd29ff1
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkPath.h"
+#include "SkPathOpsQuad.h"
+#include "SkRRect.h"
+#include "Test.h"
+
+static const SkDQuad tests[] = {
+    {{{1, 1}, {2, 1}, {0, 2}}},
+    {{{0, 0}, {1, 1}, {3, 1}}},
+    {{{2, 0}, {1, 1}, {2, 2}}},
+    {{{4, 0}, {0, 1}, {4, 2}}},
+    {{{0, 0}, {0, 1}, {1, 1}}},
+};
+
+static const SkDPoint inPoint[]= {
+    {1,   1.2},
+    {1,   0.8},
+    {1.8, 1},
+    {1.5, 1},
+    {0.4999, 0.5},  // was 0.5, 0.5; points on the hull are considered outside
+};
+
+static const SkDPoint outPoint[]= {
+    {1,   1.6},
+    {1,   1.5},
+    {2.2, 1},
+    {1.5, 1.5},
+    {1.1, 0.5},
+};
+
+static const size_t tests_count = SK_ARRAY_COUNT(tests);
+
+DEF_TEST(PathOpsDQuad, reporter) {
+    for (size_t index = 0; index < tests_count; ++index) {
+        const SkDQuad& quad = tests[index];
+        SkASSERT(ValidQuad(quad));
+        bool result = quad.pointInHull(inPoint[index]);
+        if (!result) {
+            SkDebugf("%s [%d] expected in hull\n", __FUNCTION__, index);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        result = quad.pointInHull(outPoint[index]);
+        if (result) {
+            SkDebugf("%s [%d] expected outside hull\n", __FUNCTION__, index);
+            REPORTER_ASSERT(reporter, 0);
+        }
+    }
+}
+
+DEF_TEST(PathOpsRRect, reporter) {
+    SkPath path;
+    SkRRect rRect;
+    SkRect rect = {135, 143, 250, 177};
+    SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}};
+    rRect.setRectRadii(rect, radii);
+    path.addRRect(rRect);
+}
index 70d39d15c03ebc0e4ed57a7694d4b4c1ad0d19c4..874e82b69a22957947c3a0fcc9c8b358dac81983 100644 (file)
 #include "SkPathOpsRect.h"
 #include "Test.h"
 
+static const SkDLine lineTests[] = {
+    {{{2, 1}, {2, 1}}},
+    {{{2, 1}, {1, 1}}},
+    {{{2, 1}, {2, 2}}},
+    {{{1, 1}, {2, 2}}},
+    {{{3, 0}, {2, 1}}},
+    {{{3, 2}, {1, 1}}},
+};
+
 static const SkDQuad quadTests[] = {
     {{{1, 1}, {2, 1}, {0, 2}}},
     {{{0, 0}, {1, 1}, {3, 1}}},
@@ -25,31 +34,44 @@ static const SkDCubic cubicTests[] = {
     {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
 };
 
+static const size_t lineTests_count = SK_ARRAY_COUNT(lineTests);
 static const size_t quadTests_count = SK_ARRAY_COUNT(quadTests);
 static const size_t cubicTests_count = SK_ARRAY_COUNT(cubicTests);
 
-static void setRawBounds(const SkDQuad& quad, SkDRect* rect) {
-    rect->set(quad[0]);
-    rect->add(quad[1]);
-    rect->add(quad[2]);
-}
-
-static void setRawBounds(const SkDCubic& cubic, SkDRect* rect) {
-    rect->set(cubic[0]);
-    rect->add(cubic[1]);
-    rect->add(cubic[2]);
-    rect->add(cubic[3]);
-}
-
 DEF_TEST(PathOpsDRect, reporter) {
     size_t index;
     SkDRect rect, rect2;
+    for (index = 0; index < lineTests_count; ++index) {
+        const SkDLine& line = lineTests[index];
+        SkASSERT(ValidLine(line));
+        rect.setBounds(line);
+        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX));
+        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY));
+        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX));
+        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY));
+        rect2.set(line[0]);
+        rect2.add(line[1]);
+        REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX));
+        REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY));
+        REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX));
+        REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY));
+        REPORTER_ASSERT(reporter, rect.contains(line[0]));
+        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
+    }
     for (index = 0; index < quadTests_count; ++index) {
         const SkDQuad& quad = quadTests[index];
         SkASSERT(ValidQuad(quad));
-        setRawBounds(quad, &rect);
+        rect.setRawBounds(quad);
+        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX,
+                SkTMin(quad[1].fX, quad[2].fX)));
+        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY,
+                SkTMin(quad[1].fY, quad[2].fY)));
+        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX,
+                SkTMax(quad[1].fX, quad[2].fX)));
+        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY,
+                SkTMax(quad[1].fY, quad[2].fY)));
         rect2.setBounds(quad);
-        REPORTER_ASSERT(reporter, rect.intersects(rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
@@ -59,9 +81,17 @@ DEF_TEST(PathOpsDRect, reporter) {
     for (index = 0; index < cubicTests_count; ++index) {
         const SkDCubic& cubic = cubicTests[index];
         SkASSERT(ValidCubic(cubic));
-        setRawBounds(cubic, &rect);
+        rect.setRawBounds(cubic);
+        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX,
+                SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX))));
+        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY,
+                SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY))));
+        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX,
+                SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX))));
+        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY,
+                SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY))));
         rect2.setBounds(cubic);
-        REPORTER_ASSERT(reporter, rect.intersects(rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
diff --git a/tests/PathOpsDTriangleTest.cpp b/tests/PathOpsDTriangleTest.cpp
new file mode 100644 (file)
index 0000000..b5e2d41
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkPathOpsTriangle.h"
+#include "Test.h"
+
+static const SkDTriangle tests[] = {
+    {{{2, 0}, {3, 1}, {2, 2}}},
+    {{{3, 1}, {2, 2}, {1, 1}}},
+    {{{3, 0}, {2, 1}, {3, 2}}},
+};
+
+static const SkDPoint inPoint[] = {
+    {2.5, 1},
+    {2, 1.5},
+    {2.5, 1},
+};
+
+static const SkDPoint outPoint[] = {
+    {3, 0},
+    {2.5, 2},
+    {2.5, 2},
+};
+
+static const size_t tests_count = SK_ARRAY_COUNT(tests);
+
+DEF_TEST(PathOpsTriangleUtilities, reporter) {
+    for (size_t index = 0; index < tests_count; ++index) {
+        const SkDTriangle& triangle = tests[index];
+        SkASSERT(ValidTriangle(triangle));
+        bool result = triangle.contains(inPoint[index]);
+        if (!result) {
+            SkDebugf("%s [%d] expected point in triangle\n", __FUNCTION__, index);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        result = triangle.contains(outPoint[index]);
+        if (result) {
+            SkDebugf("%s [%d] expected point outside triangle\n", __FUNCTION__, index);
+            REPORTER_ASSERT(reporter, 0);
+        }
+    }
+}
+
+static const SkDTriangle oneOff[] = {
+    {{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753},
+      {279.25839233398438, 7.7416000366210938}}},
+
+    {{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877},
+      {279.25839233398438, 7.7416000366210938}}}
+};
+
+static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff);
+
+DEF_TEST(PathOpsTriangleOneOff, reporter) {
+    for (size_t index = 0; index < oneOff_count; ++index) {
+        const SkDTriangle& triangle = oneOff[index];
+        SkASSERT(ValidTriangle(triangle));
+        for (int inner = 0; inner < 3; ++inner) {
+            bool result = triangle.contains(triangle.fPts[inner]);
+            if (result) {
+                SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner);
+                REPORTER_ASSERT(reporter, 0);
+            }
+        }
+    }
+}
index 9930453d01ea9fb0a7d37af298ebc1fa59a4b7ad..c4fbbfa69517610c7168e6fb9e6f361b2c455e78 100755 (executable)
@@ -1,12 +1,3 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "PathOpsTSectDebug.h"
-#include "SkOpCoincidence.h"
 #include "SkOpContour.h"
 #include "SkIntersectionHelper.h"
 #include "SkOpSegment.h"
@@ -59,6 +50,7 @@ static void output_points(const SkPoint* pts, int count) {
             SkDebugf(", ");
         }
     }
+    SkDebugf(");\n");
 }
 
 static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
@@ -69,27 +61,18 @@ static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
             case SkPath::kMove_Verb:
                 SkDebugf("    %s.moveTo(", pathName);
                 output_points(&pts[0], 1);
-                SkDebugf(");\n");
                 continue;
             case SkPath::kLine_Verb:
                 SkDebugf("    %s.lineTo(", pathName);
                 output_points(&pts[1], 1);
-                SkDebugf(");\n");
                 break;
             case SkPath::kQuad_Verb:
                 SkDebugf("    %s.quadTo(", pathName);
                 output_points(&pts[1], 2);
-                SkDebugf(");\n");
-                break;
-            case SkPath::kConic_Verb:
-                SkDebugf("    %s.conicTo(", pathName);
-                output_points(&pts[1], 2);
-                SkDebugf(", %1.9gf);\n", iter.conicWeight());
                 break;
             case SkPath::kCubic_Verb:
                 SkDebugf("    %s.cubicTo(", pathName);
                 output_points(&pts[1], 3);
-                SkDebugf(");\n");
                 break;
             case SkPath::kClose_Verb:
                 SkDebugf("    %s.close();\n", pathName);
@@ -185,35 +168,228 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
 }
 #endif
 
-void SkDCubic::dump() const {
-    dumpInner();
-    SkDebugf("}},\n");
+void SkOpAngle::dump() const {
+    dumpOne(true);
+    SkDebugf("\n");
+}
+
+void SkOpAngle::dumpOne(bool functionHeader) const {
+//    fSegment->debugValidate();
+    const SkOpSpan& mSpan = fSegment->span(SkMin32(fStart, fEnd));
+    if (functionHeader) {
+        SkDebugf("%s ", __FUNCTION__);
+    }
+    SkDebugf("[%d", fSegment->debugID());
+    SkDebugf("/%d", debugID());
+    SkDebugf("] next=");
+    if (fNext) {
+        SkDebugf("%d", fNext->fSegment->debugID());
+        SkDebugf("/%d", fNext->debugID());
+    } else {
+        SkDebugf("?");
+    }
+    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
+    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fSegment->span(fStart).fT, fStart,
+            fSegment->span(fEnd).fT, fEnd);
+    SkDebugf(" sgn=%d windVal=%d", sign(), mSpan.fWindValue);
+
+    SkDebugf(" windSum=");
+    SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
+    if (mSpan.fOppValue != 0 || mSpan.fOppSum != SK_MinS32) {
+        SkDebugf(" oppVal=%d", mSpan.fOppValue);
+        SkDebugf(" oppSum=");
+        SkPathOpsDebug::WindingPrintf(mSpan.fOppSum);
+    }
+    if (mSpan.fDone) {
+        SkDebugf(" done");
+    }
+    if (unorderable()) {
+        SkDebugf(" unorderable");
+    }
+    if (small()) {
+        SkDebugf(" small");
+    }
+    if (mSpan.fTiny) {
+        SkDebugf(" tiny");
+    }
+    if (fSegment->operand()) {
+        SkDebugf(" operand");
+    }
+    if (fStop) {
+        SkDebugf(" stop");
+    }
+}
+
+void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    const char* indent = "";
+    do {
+        SkDebugf("%s", indent);
+        next->dumpOne(false);
+        if (segment == next->fSegment) {
+            if (this == fNext) {
+                SkDebugf(" << from");
+            }
+            if (to == fNext) {
+                SkDebugf(" << to");
+            }
+        }
+        SkDebugf("\n");
+        indent = "           ";
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpLoop() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->dumpOne(false);
+        SkDebugf("\n");
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpPartials() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->fCurvePart.dumpNumber();
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngleSet::dump() const {
+    // FIXME: unimplemented
+/* This requires access to the internal SkChunkAlloc data
+   Defer implementing this until it is needed for debugging
+*/
+    SkASSERT(0);
+}
+
+void SkOpContour::dump() const {
+    int segmentCount = fSegments.count();
+    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
+    for (int test = 0; test < segmentCount; ++test) {
+        SkDebugf("  [%d] ((SkOpSegment*) 0x%p) [%d]\n", test, &fSegments[test],
+                fSegments[test].debugID());
+    }
+}
+
+void SkOpContour::dumpAngles() const {
+    int segmentCount = fSegments.count();
+    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
+    for (int test = 0; test < segmentCount; ++test) {
+        SkDebugf("  [%d] ", test);
+        fSegments[test].dumpAngles();
+    }
+}
+
+void SkOpContour::dumpCoincidence(const SkCoincidence& coin) const {
+    int thisIndex = coin.fSegments[0];
+    const SkOpSegment& s1 = fSegments[thisIndex];
+    int otherIndex = coin.fSegments[1];
+    const SkOpSegment& s2 = coin.fOther->fSegments[otherIndex];
+    SkDebugf("((SkOpSegment*) 0x%p) [%d]  ((SkOpSegment*) 0x%p) [%d]\n", &s1, s1.debugID(),
+            &s2, s2.debugID());
+    for (int index = 0; index < 2; ++index) {
+        SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[0][index].fX, coin.fPts[0][index].fY);
+        if (coin.fNearly[index]) {
+            SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[1][index].fX, coin.fPts[1][index].fY);
+        }
+        SkDebugf("  seg1t=%1.9g seg2t=%1.9g\n", coin.fTs[0][index], coin.fTs[1][index]);
+    }
+}
+
+void SkOpContour::dumpCoincidences() const {
+    int count = fCoincidences.count();
+    if (count > 0) {
+        SkDebugf("fCoincidences count=%d\n", count);
+        for (int test = 0; test < count; ++test) {
+            dumpCoincidence(fCoincidences[test]);
+        }
+    }
+    count = fPartialCoincidences.count();
+    if (count == 0) {
+        return;
+    }
+    SkDebugf("fPartialCoincidences count=%d\n", count);
+    for (int test = 0; test < count; ++test) {
+        dumpCoincidence(fPartialCoincidences[test]);
+    }
+}
+
+void SkOpContour::dumpPt(int index) const {
+    int segmentCount = fSegments.count();
+    for (int test = 0; test < segmentCount; ++test) {
+        const SkOpSegment& segment = fSegments[test];
+        if (segment.debugID() == index) {
+            fSegments[test].dumpPts();
+        }
+    }
 }
 
-void SkDCubic::dumpID(int id) const {
-    dumpInner();
-    SkDebugf("}} id=%d\n", id);
+void SkOpContour::dumpPts() const {
+    int segmentCount = fSegments.count();
+    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
+    for (int test = 0; test < segmentCount; ++test) {
+        SkDebugf("  [%d] ", test);
+        fSegments[test].dumpPts();
+    }
+}
+
+void SkOpContour::dumpSpan(int index) const {
+    int segmentCount = fSegments.count();
+    for (int test = 0; test < segmentCount; ++test) {
+        const SkOpSegment& segment = fSegments[test];
+        if (segment.debugID() == index) {
+            fSegments[test].dumpSpans();
+        }
+    }
 }
 
-static inline bool double_is_NaN(double x) { return x != x; }
+void SkOpContour::dumpSpans() const {
+    int segmentCount = fSegments.count();
+    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
+    for (int test = 0; test < segmentCount; ++test) {
+        SkDebugf("  [%d] ", test);
+        fSegments[test].dumpSpans();
+    }
+}
 
-void SkDCubic::dumpInner() const {
+void SkDCubic::dump() const {
     SkDebugf("{{");
     int index = 0;
     do {
-        if (index != 0) {
-            if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
-                return;
-            }
+        fPts[index].dump();
+        SkDebugf(", ");
+    } while (++index < 3);
+    fPts[index].dump();
+    SkDebugf("}}\n");
+}
+
+void SkDCubic::dumpNumber() const {
+    SkDebugf("{{");
+    int index = 0;
+    bool dumpedOne = false;
+    do {
+        if (!(fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY)) {
+            continue;
+        }
+        if (dumpedOne) {
             SkDebugf(", ");
         }
         fPts[index].dump();
+        dumpedOne = true;
     } while (++index < 3);
-    if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
-        return;
+    if (fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY) {
+        if (dumpedOne) {
+            SkDebugf(", ");
+        }
+        fPts[index].dump();
     }
-    SkDebugf(", ");
-    fPts[index].dump();
+    SkDebugf("}}\n");
 }
 
 void SkDLine::dump() const {
@@ -221,7 +397,7 @@ void SkDLine::dump() const {
     fPts[0].dump();
     SkDebugf(", ");
     fPts[1].dump();
-    SkDebugf("}},\n");
+    SkDebugf("}}\n");
 }
 
 void SkDPoint::dump() const {
@@ -249,16 +425,10 @@ void SkDPoint::DumpHex(const SkPoint& pt) {
 }
 
 void SkDQuad::dump() const {
-    dumpInner();
-    SkDebugf("}},\n");
+    dumpComma("");
 }
 
-void SkDQuad::dumpID(int id) const {
-    dumpInner();
-    SkDebugf("}} id=%d\n", id);
-}
-
-void SkDQuad::dumpInner() const {
+void SkDQuad::dumpComma(const char* comma) const {
     SkDebugf("{{");
     int index = 0;
     do {
@@ -266,262 +436,436 @@ void SkDQuad::dumpInner() const {
         SkDebugf(", ");
     } while (++index < 2);
     fPts[index].dump();
+    SkDebugf("}}%s\n", comma ? comma : "");
 }
 
-void SkIntersections::dump() const {
-    SkDebugf("used=%d of %d", fUsed, fMax);
-    for (int index = 0; index < fUsed; ++index) {
-        SkDebugf(" t=(%s%1.9g,%s%1.9g) pt=(%1.9g,%1.9g)",
-                fIsCoincident[0] & (1 << index) ? "*" : "", fT[0][index],
-                fIsCoincident[1] & (1 << index) ? "*" : "", fT[1][index],
-                fPt[index].fX, fPt[index].fY);
-        if (index < 2 && fNearlySame[index]) {
-            SkDebugf(" pt2=(%1.9g,%1.9g)",fPt2[index].fX, fPt2[index].fY);
-        }
+void SkIntersectionHelper::dump() const {
+    SkDPoint::Dump(pts()[0]);
+    SkDPoint::Dump(pts()[1]);
+    if (verb() >= SkPath::kQuad_Verb) {
+        SkDPoint::Dump(pts()[2]);
     }
-    SkDebugf("\n");
+    if (verb() >= SkPath::kCubic_Verb) {
+        SkDPoint::Dump(pts()[3]);
+    }
+}
+
+const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const {
+    return fTs;
 }
 
-const SkOpAngle* SkPathOpsDebug::DebugAngleAngle(const SkOpAngle* angle, int id) {
-    return angle->debugAngle(id);
+void SkOpSegment::dumpAngles() const {
+    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
+    const SkOpAngle* fromAngle = NULL;
+    const SkOpAngle* toAngle = NULL;
+    for (int index = 0; index < count(); ++index) {
+        const SkOpAngle* fAngle = fTs[index].fFromAngle;
+        const SkOpAngle* tAngle = fTs[index].fToAngle;
+        if (fromAngle == fAngle && toAngle == tAngle) {
+            continue;
+        }
+        if (fAngle) {
+            SkDebugf("  [%d] from=%d ", index, fAngle->debugID());
+            fAngle->dumpTo(this, tAngle);
+        }
+        if (tAngle) {
+            SkDebugf("  [%d] to=%d   ", index, tAngle->debugID());
+            tAngle->dumpTo(this, fAngle);
+        }
+        fromAngle = fAngle;
+        toAngle = tAngle;
+    }
 }
 
-SkOpContour* SkPathOpsDebug::DebugAngleContour(SkOpAngle* angle, int id) {
-    return angle->debugContour(id);
+void SkOpSegment::dumpContour(int firstID, int lastID) const {
+    if (debugID() < 0) {
+        return;
+    }
+    const SkOpSegment* test = this - (debugID() - 1);
+    test += (firstID - 1);
+    const SkOpSegment* last = test + (lastID - firstID);
+    while (test <= last) {
+        test->dumpSpans();
+        ++test;
+    }
 }
 
-const SkOpPtT* SkPathOpsDebug::DebugAnglePtT(const SkOpAngle* angle, int id) {
-    return angle->debugPtT(id);
+void SkOpSegment::dumpPts() const {
+    int last = SkPathOpsVerbToPoints(fVerb);
+    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
+    int index = 0;
+    do {
+        SkDPoint::Dump(fPts[index]);
+        SkDebugf(", ");
+    } while (++index < last);
+    SkDPoint::Dump(fPts[index]);
+    SkDebugf("}}\n");
 }
 
-const SkOpSegment* SkPathOpsDebug::DebugAngleSegment(const SkOpAngle* angle, int id) {
-    return angle->debugSegment(id);
+void SkOpSegment::dumpHexPts() const {
+    int last = SkPathOpsVerbToPoints(fVerb);
+    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
+    int index = 0;
+    do {
+        SkDPoint::DumpHex(fPts[index]);
+        SkDebugf(", ");
+    } while (++index < last);
+    SkDPoint::DumpHex(fPts[index]);
+    SkDebugf("}}\n");
 }
 
-const SkOpSpanBase* SkPathOpsDebug::DebugAngleSpan(const SkOpAngle* angle, int id) {
-    return angle->debugSpan(id);
+void SkOpSegment::dumpDPts() const {
+    int count = SkPathOpsVerbToPoints(fVerb);
+    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
+    int index = 0;
+    do {
+        SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
+        dPt.dump();
+        if (index != count) {
+            SkDebugf(", ");
+        }
+    } while (++index <= count);
+    SkDebugf("}}\n");
 }
 
-const SkOpAngle* SkPathOpsDebug::DebugContourAngle(SkOpContour* contour, int id) {
-    return contour->debugAngle(id);
+void SkOpSegment::dumpSpans() const {
+    int count = this->count();
+    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = this->span(index);
+        SkDebugf("  [%d] ", index);
+        span.dumpOne();
+    }
 }
 
-SkOpContour* SkPathOpsDebug::DebugContourContour(SkOpContour* contour, int id) {
-    return contour->debugContour(id);
+void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour, true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpCoincidences();
+    }
 }
 
-const SkOpPtT* SkPathOpsDebug::DebugContourPtT(SkOpContour* contour, int id) {
-    return contour->debugPtT(id);
+void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour* , true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpCoincidences();
+    }
 }
 
-const SkOpSegment* SkPathOpsDebug::DebugContourSegment(SkOpContour* contour, int id) {
-    return contour->debugSegment(id);
+void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour, true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dump();
+    }
 }
 
-const SkOpSpanBase* SkPathOpsDebug::DebugContourSpan(SkOpContour* contour, int id) {
-    return contour->debugSpan(id);
+void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour* , true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dump();
+    }
 }
 
-const SkOpAngle* SkPathOpsDebug::DebugPtTAngle(const SkOpPtT* ptT, int id) {
-    return ptT->debugAngle(id);
+void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour, true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpAngles();
+    }
 }
 
-SkOpContour* SkPathOpsDebug::DebugPtTContour(SkOpPtT* ptT, int id) {
-    return ptT->debugContour(id);
+void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour* , true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpAngles();
+    }
 }
 
-const SkOpPtT* SkPathOpsDebug::DebugPtTPtT(const SkOpPtT* ptT, int id) {
-    return ptT->debugPtT(id);
+void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour, true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpPts();
+    }
 }
 
-const SkOpSegment* SkPathOpsDebug::DebugPtTSegment(const SkOpPtT* ptT, int id) {
-    return ptT->debugSegment(id);
+void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour* , true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpPts();
+    }
 }
 
-const SkOpSpanBase* SkPathOpsDebug::DebugPtTSpan(const SkOpPtT* ptT, int id) {
-    return ptT->debugSpan(id);
+void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour, true>& contours, int segmentID) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpPt(segmentID);
+    }
 }
 
-const SkOpAngle* SkPathOpsDebug::DebugSegmentAngle(const SkOpSegment* span, int id) {
-    return span->debugAngle(id);
+void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpPt(segmentID);
+    }
 }
 
-SkOpContour* SkPathOpsDebug::DebugSegmentContour(SkOpSegment* span, int id) {
-    return span->debugContour(id);
+void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour, true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpSpans();
+    }
 }
 
-const SkOpPtT* SkPathOpsDebug::DebugSegmentPtT(const SkOpSegment* span, int id) {
-    return span->debugPtT(id);
+void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour* , true>& contours) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpSpans();
+    }
 }
 
-const SkOpSegment* SkPathOpsDebug::DebugSegmentSegment(const SkOpSegment* span, int id) {
-    return span->debugSegment(id);
+void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour, true>& contours, int segmentID) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index].dumpSpan(segmentID);
+    }
 }
 
-const SkOpSpanBase* SkPathOpsDebug::DebugSegmentSpan(const SkOpSegment* span, int id) {
-    return span->debugSpan(id);
+void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
+    int count = contours.count();
+    for (int index = 0; index < count; ++index) {
+        contours[index]->dumpSpan(segmentID);
+    }
 }
 
-const SkOpAngle* SkPathOpsDebug::DebugSpanAngle(const SkOpSpanBase* span, int id) {
-    return span->debugAngle(id);
+void SkPathOpsDebug::DumpSpans(const SkTDArray<SkOpSpan *>& spans) {
+    int count = spans.count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan* span = spans[index];
+        const SkOpSpan& oSpan = span->fOther->span(span->fOtherIndex);
+        const SkOpSegment* segment = oSpan.fOther;
+        SkDebugf("((SkOpSegment*) 0x%p) [%d] ", segment, segment->debugID());
+        SkDebugf("spanIndex:%d ", oSpan.fOtherIndex);
+        span->dumpOne();
+    }
+}
+
+// this does not require that other T index is initialized or correct
+const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const {
+    if (!fOther) {
+        return NULL;
+    }
+    int oppCount = fOther->count();
+    for (int index = 0; index < oppCount; ++index) {
+        const SkOpSpan& otherSpan = fOther->span(index);
+        double otherTestT = otherSpan.fT;
+        if (otherTestT < fOtherT) {
+            continue;
+        }
+        SkASSERT(otherTestT == fOtherT);
+        const SkOpSegment* candidate = otherSpan.fOther;
+        const SkOpSpan* first = candidate->debugSpans().begin();
+        const SkOpSpan* last = candidate->debugSpans().end() - 1;
+        if (first <= this && this <= last) {
+            if (spanIndex) {
+                *spanIndex = this - first;
+            }
+            return candidate;
+        }
+    }
+    SkASSERT(0);
+    return NULL;
 }
 
-SkOpContour* SkPathOpsDebug::DebugSpanContour(SkOpSpanBase* span, int id) {
-    return span->debugContour(id);
+void SkOpSpan::dumpOne() const {
+    SkDebugf("t=");
+    DebugDumpDouble(fT);
+    SkDebugf(" pt=");
+    SkDPoint::Dump(fPt);
+    if (fOther) {
+        SkDebugf(" other.fID=%d", fOther->debugID());
+        SkDebugf(" [%d] otherT=", fOtherIndex);
+        DebugDumpDouble(fOtherT);
+    } else {
+        SkDebugf(" other.fID=? [?] otherT=?");
+    }
+    if (fWindSum != SK_MinS32) {
+        SkDebugf(" windSum=%d", fWindSum);
+    }
+    if (fOppSum != SK_MinS32 && (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0)) {
+        SkDebugf(" oppSum=%d", fOppSum);
+    }
+    SkDebugf(" windValue=%d", fWindValue);
+    if (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0) {
+        SkDebugf(" oppValue=%d", fOppValue);
+    }
+    if (fFromAngle && fFromAngle->debugID()) {
+        SkDebugf(" from=%d", fFromAngle->debugID());
+    }
+    if (fToAngle && fToAngle->debugID()) {
+        SkDebugf(" to=%d", fToAngle->debugID());
+    }
+    if (fChased) {
+        SkDebugf(" chased");
+    }
+    if (fCoincident) {
+        SkDebugf(" coincident");
+    }
+    if (fDone) {
+        SkDebugf(" done");
+    }
+    if (fLoop) {
+        SkDebugf(" loop");
+    }
+    if (fMultiple) {
+        SkDebugf(" multiple");
+    }
+    if (fNear) {
+        SkDebugf(" near");
+    }
+    if (fSmall) {
+        SkDebugf(" small");
+    }
+    if (fTiny) {
+        SkDebugf(" tiny");
+    }
+    SkDebugf("\n");
 }
 
-const SkOpPtT* SkPathOpsDebug::DebugSpanPtT(const SkOpSpanBase* span, int id) {
-    return span->debugPtT(id);
+void SkOpSpan::dump() const {
+    ptrdiff_t spanIndex;
+    const SkOpSegment* segment = debugToSegment(&spanIndex);
+    if (segment) {
+        SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", segment, segment->debugID());
+        SkDebugf("  [%d] ", spanIndex);
+    } else {
+        SkDebugf("((SkOpSegment*) ?) [?]\n");
+        SkDebugf("  [?] ");
+    }
+    dumpOne();
 }
 
-const SkOpSegment* SkPathOpsDebug::DebugSpanSegment(const SkOpSpanBase* span, int id) {
-    return span->debugSegment(id);
+void Dump(const SkTArray<class SkOpContour, true>& contours) {
+    SkPathOpsDebug::DumpContours(contours);
 }
 
-const SkOpSpanBase* SkPathOpsDebug::DebugSpanSpan(const SkOpSpanBase* span, int id) {
-    return span->debugSpan(id);
+void Dump(const SkTArray<class SkOpContour* , true>& contours) {
+    SkPathOpsDebug::DumpContours(contours);
 }
 
-void SkPathOpsDebug::DumpContours(SkTDArray<SkOpContour* >* contours) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dump();
-    }
+void Dump(const SkTArray<class SkOpContour, true>* contours) {
+    SkPathOpsDebug::DumpContours(*contours);
 }
 
-void SkPathOpsDebug::DumpContoursAll(SkTDArray<SkOpContour* >* contours) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dumpAll();
-    }
+void Dump(const SkTArray<class SkOpContour* , true>* contours) {
+    SkPathOpsDebug::DumpContours(*contours);
 }
 
-void SkPathOpsDebug::DumpContoursAngles(const SkTDArray<SkOpContour* >* contours) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dumpAngles();
-    }
+void Dump(const SkTDArray<SkOpSpan *>& chase) {
+    SkPathOpsDebug::DumpSpans(chase);
 }
 
-void SkPathOpsDebug::DumpContoursPts(const SkTDArray<SkOpContour* >* contours) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dumpPts();
-    }
+void Dump(const SkTDArray<SkOpSpan *>* chase) {
+    SkPathOpsDebug::DumpSpans(*chase);
 }
 
-void SkPathOpsDebug::DumpContoursPt(const SkTDArray<SkOpContour* >* contours, int segmentID) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dumpPt(segmentID);
-    }
+void DumpAngles(const SkTArray<class SkOpContour, true>& contours) {
+    SkPathOpsDebug::DumpContourAngles(contours);
 }
 
-void SkPathOpsDebug::DumpContoursSegment(const SkTDArray<SkOpContour* >* contours,
-        int segmentID) {
-    if (contours->count()) {
-        (*contours)[0]->dumpSegment(segmentID);
-    }
+void DumpAngles(const SkTArray<class SkOpContour* , true>& contours) {
+    SkPathOpsDebug::DumpContourAngles(contours);
 }
 
-void SkPathOpsDebug::DumpContoursSpan(const SkTDArray<SkOpContour* >* contours,
-        int spanID) {
-    if (contours->count()) {
-        (*contours)[0]->dumpSpan(spanID);
-    }
+void DumpAngles(const SkTArray<class SkOpContour, true>* contours) {
+    SkPathOpsDebug::DumpContourAngles(*contours);
 }
 
-void SkPathOpsDebug::DumpContoursSpans(const SkTDArray<SkOpContour* >* contours) {
-    int count = contours->count();
-    for (int index = 0; index < count; ++index) {
-        (*contours)[index]->dumpSpans();
-    }
+void DumpAngles(const SkTArray<class SkOpContour* , true>* contours) {
+    SkPathOpsDebug::DumpContourAngles(*contours);
 }
 
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSect<SkDCubic>* sect, int id) {
-    return sect->debugSpan(id);
+void DumpCoin(const SkTArray<class SkOpContour, true>& contours) {
+    SkPathOpsDebug::DumpCoincidence(contours);
 }
 
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSect<SkDQuad>* sect, int id) {
-    return sect->debugSpan(id);
+void DumpCoin(const SkTArray<class SkOpContour* , true>& contours) {
+    SkPathOpsDebug::DumpCoincidence(contours);
 }
 
-const SkTSpan<SkDCubic>* DebugT(const SkTSect<SkDCubic>* sect, double t) {
-    return sect->debugT(t);
+void DumpCoin(const SkTArray<class SkOpContour, true>* contours) {
+    SkPathOpsDebug::DumpCoincidence(*contours);
 }
 
-const SkTSpan<SkDQuad>* DebugT(const SkTSect<SkDQuad>* sect, double t) {
-    return sect->debugT(t);
+void DumpCoin(const SkTArray<class SkOpContour* , true>* contours) {
+    SkPathOpsDebug::DumpCoincidence(*contours);
 }
 
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSpan<SkDCubic>* span, int id) {
-    return span->debugSpan(id);
+void DumpSpans(const SkTArray<class SkOpContour, true>& contours) {
+    SkPathOpsDebug::DumpContourSpans(contours);
 }
 
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSpan<SkDQuad>* span, int id) {
-    return span->debugSpan(id);
+void DumpSpans(const SkTArray<class SkOpContour* , true>& contours) {
+    SkPathOpsDebug::DumpContourSpans(contours);
 }
 
-const SkTSpan<SkDCubic>* DebugT(const SkTSpan<SkDCubic>* span, double t) {
-    return span->debugT(t);
+void DumpSpans(const SkTArray<class SkOpContour, true>* contours) {
+    SkPathOpsDebug::DumpContourSpans(*contours);
 }
 
-const SkTSpan<SkDQuad>* DebugT(const SkTSpan<SkDQuad>* span, double t) {
-    return span->debugT(t);
+void DumpSpans(const SkTArray<class SkOpContour* , true>* contours) {
+    SkPathOpsDebug::DumpContourSpans(*contours);
 }
 
-void Dump(const SkTSect<SkDCubic>* sect) {
-    sect->dump();
+void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
+    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
 }
 
-void Dump(const SkTSect<SkDQuad>* sect) {
-    sect->dump();
+void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
+    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
 }
 
-void Dump(const SkTSpan<SkDCubic>* span) {
-    span->dump();
+void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
+    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
 }
 
-void Dump(const SkTSpan<SkDQuad>* span) {
-    span->dump();
+void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
+    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
 }
 
-void DumpBoth(SkTSect<SkDCubic>* sect1, SkTSect<SkDCubic>* sect2) {
-    sect1->dumpBoth(sect2);
+void DumpPts(const SkTArray<class SkOpContour, true>& contours) {
+    SkPathOpsDebug::DumpContourPts(contours);
 }
 
-void DumpBoth(SkTSect<SkDQuad>* sect1, SkTSect<SkDQuad>* sect2) {
-    sect1->dumpBoth(sect2);
+void DumpPts(const SkTArray<class SkOpContour* , true>& contours) {
+    SkPathOpsDebug::DumpContourPts(contours);
 }
 
-void DumpCoin(SkTSect<SkDCubic>* sect1) {
-    sect1->dumpCoin();
+void DumpPts(const SkTArray<class SkOpContour, true>* contours) {
+    SkPathOpsDebug::DumpContourPts(*contours);
 }
 
-void DumpCoin(SkTSect<SkDQuad>* sect1) {
-    sect1->dumpCoin();
+void DumpPts(const SkTArray<class SkOpContour* , true>* contours) {
+    SkPathOpsDebug::DumpContourPts(*contours);
 }
 
-void DumpCoinCurves(SkTSect<SkDCubic>* sect1) {
-    sect1->dumpCoinCurves();
+void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
+    SkPathOpsDebug::DumpContourPt(contours, segmentID);
 }
 
-void DumpCoinCurves(SkTSect<SkDQuad>* sect1) {
-    sect1->dumpCoinCurves();
+void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
+    SkPathOpsDebug::DumpContourPt(contours, segmentID);
 }
 
-void DumpCurves(const SkTSect<SkDQuad>* sect) {
-    sect->dumpCurves();
+void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
+    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
 }
 
-void DumpCurves(const SkTSect<SkDCubic>* sect) {
-    sect->dumpCurves();
+void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
+    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
 }
 
 static void dumpTestCase(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) {
-    SkDebugf("\n<div id=\"quad%d\">\n", testNo);
-    quad1.dumpInner();
-    SkDebugf("}}, ");
+    SkDebugf("<div id=\"quad%d\">\n", testNo);
+    quad1.dumpComma(",");
     quad2.dump();
     SkDebugf("</div>\n\n");
 }
@@ -551,649 +895,3 @@ void DumpT(const SkDQuad& quad, double t) {
     SkDLine line = {{quad.ptAtT(t), quad[0]}};
     line.dump();
 }
-
-const SkOpAngle* SkOpAngle::debugAngle(int id) const {
-    return this->segment()->debugAngle(id);
-}
-
-SkOpContour* SkOpAngle::debugContour(int id) {
-    return this->segment()->debugContour(id);
-}
-
-const SkOpPtT* SkOpAngle::debugPtT(int id) const {
-    return this->segment()->debugPtT(id);
-}
-
-const SkOpSegment* SkOpAngle::debugSegment(int id) const {
-    return this->segment()->debugSegment(id);
-}
-
-const SkOpSpanBase* SkOpAngle::debugSpan(int id) const {
-    return this->segment()->debugSpan(id);
-}
-
-void SkOpAngle::dump() const {
-    dumpOne(true);
-    SkDebugf("\n");
-}
-
-void SkOpAngle::dumpOne(bool functionHeader) const {
-//    fSegment->debugValidate();
-    const SkOpSegment* segment = this->segment();
-    const SkOpSpan& mSpan = *fStart->starter(fEnd);
-    if (functionHeader) {
-        SkDebugf("%s ", __FUNCTION__);
-    }
-    SkDebugf("[%d", segment->debugID());
-    SkDebugf("/%d", debugID());
-    SkDebugf("] next=");
-    if (fNext) {
-        SkDebugf("%d", fNext->fStart->segment()->debugID());
-        SkDebugf("/%d", fNext->debugID());
-    } else {
-        SkDebugf("?");
-    }
-    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
-    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fStart->t(), fStart->debugID(),
-                fEnd->t(), fEnd->debugID());
-    SkDebugf(" sgn=%d windVal=%d", this->sign(), mSpan.windValue());
-
-    SkDebugf(" windSum=");
-    SkPathOpsDebug::WindingPrintf(mSpan.windSum());
-    if (mSpan.oppValue() != 0 || mSpan.oppSum() != SK_MinS32) {
-        SkDebugf(" oppVal=%d", mSpan.oppValue());
-        SkDebugf(" oppSum=");
-        SkPathOpsDebug::WindingPrintf(mSpan.oppSum());
-    }
-    if (mSpan.done()) {
-        SkDebugf(" done");
-    }
-    if (unorderable()) {
-        SkDebugf(" unorderable");
-    }
-    if (segment->operand()) {
-        SkDebugf(" operand");
-    }
-    if (fStop) {
-        SkDebugf(" stop");
-    }
-}
-
-void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    const char* indent = "";
-    do {
-        SkDebugf("%s", indent);
-        next->dumpOne(false);
-        if (segment == next->fStart->segment()) {
-            if (this == fNext) {
-                SkDebugf(" << from");
-            }
-            if (to == fNext) {
-                SkDebugf(" << to");
-            }
-        }
-        SkDebugf("\n");
-        indent = "           ";
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpCurves() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->fCurvePart.dumpID(next->segment()->debugID());
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpLoop() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->dumpOne(false);
-        SkDebugf("\n");
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpTest() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        SkDebugf("{ ");
-        SkOpSegment* segment = next->segment();
-        segment->dumpPts();
-        SkDebugf(", %d, %1.9g, %1.9g, {} },\n", SkPathOpsVerbToPoints(segment->verb()) + 1,
-                next->start()->t(), next->end()->t());
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-bool SkOpPtT::debugMatchID(int id) const {
-    int limit = this->debugLoopLimit(false);
-    int loop = 0;
-    const SkOpPtT* ptT = this;
-    do {
-        if (ptT->debugID() == id) {
-            return true;
-        }
-    } while ((!limit || ++loop <= limit) && (ptT = ptT->next()) && ptT != this);
-    return false;
-}
-
-const SkOpAngle* SkOpPtT::debugAngle(int id) const {
-    return this->span()->debugAngle(id);
-}
-
-SkOpContour* SkOpPtT::debugContour(int id) {
-    return this->span()->debugContour(id);
-}
-
-const SkOpPtT* SkOpPtT::debugPtT(int id) const {
-    return this->span()->debugPtT(id);
-}
-
-const SkOpSegment* SkOpPtT::debugSegment(int id) const {
-    return this->span()->debugSegment(id);
-}
-
-const SkOpSpanBase* SkOpPtT::debugSpan(int id) const {
-    return this->span()->debugSpan(id);
-}
-
-void SkOpPtT::dump() const {
-    SkDebugf("seg=%d span=%d ptT=%d",
-            this->segment()->debugID(), this->span()->debugID(), this->debugID());
-    this->dumpBase();
-    SkDebugf("\n");
-}
-
-void SkOpPtT::dumpAll() const {
-    contour()->indentDump();
-    const SkOpPtT* next = this;
-    int limit = debugLoopLimit(true);
-    int loop = 0;
-    do {
-        SkDebugf("%.*s", contour()->debugIndent(), "        ");
-        SkDebugf("seg=%d span=%d ptT=%d",
-                next->segment()->debugID(), next->span()->debugID(), next->debugID());
-        next->dumpBase();
-        SkDebugf("\n");
-        if (limit && ++loop >= limit) {
-            SkDebugf("*** abort loop ***\n");
-            break;
-        }
-    } while ((next = next->fNext) && next != this);
-    contour()->outdentDump();
-}
-
-void SkOpPtT::dumpBase() const {
-    SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s", this->fT, this->fPt.fX, this->fPt.fY,
-            this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : "");
-}
-
-const SkOpAngle* SkOpSpanBase::debugAngle(int id) const {
-    return this->segment()->debugAngle(id);
-}
-
-SkOpContour* SkOpSpanBase::debugContour(int id) {
-    return this->segment()->debugContour(id);
-}
-
-const SkOpPtT* SkOpSpanBase::debugPtT(int id) const {
-    return this->segment()->debugPtT(id);
-}
-
-const SkOpSegment* SkOpSpanBase::debugSegment(int id) const {
-    return this->segment()->debugSegment(id);
-}
-
-const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const {
-    return this->segment()->debugSpan(id);
-}
-
-void SkOpSpanBase::dump() const {
-    this->dumpAll();
-    SkDebugf("\n");
-}
-
-void SkOpSpanBase::dumpAll() const {
-    SkDebugf("%.*s", contour()->debugIndent(), "        ");
-    SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID());
-    this->dumpBase();
-    SkDebugf("\n");
-    this->fPtT.dumpAll();
-}
-
-void SkOpSpanBase::dumpBase() const {
-    if (this->fAligned) {
-        SkDebugf(" aligned");
-    }
-    if (this->fChased) {
-        SkDebugf(" chased");
-    }
-    if (!this->final()) {
-        this->upCast()->dumpSpan();
-    }
-    const SkOpSpanBase* coin = this->coinEnd();
-    if (this != coin) {
-        SkDebugf(" coinEnd seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
-    } else if (this->final() || !this->upCast()->isCoincident()) {
-        const SkOpPtT* oPt = this->ptT()->next();
-        SkDebugf(" seg/span=%d/%d", oPt->segment()->debugID(), oPt->span()->debugID());
-    }
-}
-
-void SkOpSpanBase::dumpCoin() const {
-    const SkOpSpan* span = this->upCastable();
-    if (!span) {
-        return;
-    }
-    if (!span->isCoincident()) {
-        return;
-    }
-    span->dumpCoin();
-}
-
-void SkOpSpan::dumpCoin() const {
-    const SkOpSpan* coincident = fCoincident;
-    bool ok = debugCoinLoopCheck();
-    this->dump();
-    int loop = 0;
-    do {
-        coincident->dump();
-        if (!ok && ++loop > 10) {
-            SkDebugf("*** abort loop ***\n");
-            break;
-        }
-    } while ((coincident = coincident->fCoincident) != this);
-}
-
-bool SkOpSpan::dumpSpan() const {
-    SkOpSpan* coin = fCoincident;
-    if (this != coin) {
-        SkDebugf(" coinStart seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
-    }
-    SkDebugf(" windVal=%d", this->windValue());
-    SkDebugf(" windSum=");
-    SkPathOpsDebug::WindingPrintf(this->windSum());
-    if (this->oppValue() != 0 || this->oppSum() != SK_MinS32) {
-        SkDebugf(" oppVal=%d", this->oppValue());
-        SkDebugf(" oppSum=");
-        SkPathOpsDebug::WindingPrintf(this->oppSum());
-    }
-    if (this->done()) {
-        SkDebugf(" done");
-    }
-    return this != coin;
-}
-
-const SkOpAngle* SkOpSegment::debugAngle(int id) const {
-    return this->contour()->debugAngle(id);
-}
-
-SkOpContour* SkOpSegment::debugContour(int id) {
-    return this->contour()->debugContour(id);
-}
-
-const SkOpPtT* SkOpSegment::debugPtT(int id) const {
-    return this->contour()->debugPtT(id);
-}
-
-const SkOpSegment* SkOpSegment::debugSegment(int id) const {
-    return this->contour()->debugSegment(id);
-}
-
-const SkOpSpanBase* SkOpSegment::debugSpan(int id) const {
-    return this->contour()->debugSpan(id);
-}
-
-void SkOpSegment::dump() const {
-    SkDebugf("%.*s", contour()->debugIndent(), "        ");
-    this->dumpPts();
-    const SkOpSpanBase* span = &fHead;
-    contour()->indentDump();
-    do {
-        SkDebugf("%.*s span=%d ", contour()->debugIndent(), "        ", span->debugID());
-        span->ptT()->dumpBase();
-        span->dumpBase();
-        SkDebugf("\n");
-    } while (!span->final() && (span = span->upCast()->next()));
-    contour()->outdentDump();
-}
-
-void SkOpSegment::dumpAll() const {
-    SkDebugf("%.*s", contour()->debugIndent(), "        ");
-    this->dumpPts();
-    const SkOpSpanBase* span = &fHead;
-    contour()->indentDump();
-    do {
-        span->dumpAll();
-    } while (!span->final() && (span = span->upCast()->next()));
-    contour()->outdentDump();
-}
-
-void SkOpSegment::dumpAngles() const {
-    SkDebugf("seg=%d\n", debugID());
-    const SkOpSpanBase* span = &fHead;
-    do {
-        const SkOpAngle* fAngle = span->fromAngle();
-        const SkOpAngle* tAngle = span->final() ? NULL : span->upCast()->toAngle();
-        if (fAngle) {
-            SkDebugf("  span=%d from=%d ", span->debugID(), fAngle->debugID());
-            fAngle->dumpTo(this, tAngle);
-        }
-        if (tAngle) {
-            SkDebugf("  span=%d to=%d   ", span->debugID(), tAngle->debugID());
-            tAngle->dumpTo(this, fAngle);
-        }
-    } while (!span->final() && (span = span->upCast()->next()));
-}
-
-void SkOpSegment::dumpCoin() const {
-    const SkOpSpan* span = &fHead;
-    do {
-        span->dumpCoin();
-    } while ((span = span->next()->upCastable()));
-}
-
-void SkOpSegment::dumpPts() const {
-    int last = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("seg=%d {{", this->debugID());
-    int index = 0;
-    do {
-        SkDPoint::Dump(fPts[index]);
-        SkDebugf(", ");
-    } while (++index < last);
-    SkDPoint::Dump(fPts[index]);
-    SkDebugf("}}\n");
-}
-
-void SkCoincidentSpans::dump() const {
-    SkDebugf("- seg=%d span=%d ptT=%d ", fCoinPtTStart->segment()->debugID(),
-        fCoinPtTStart->span()->debugID(), fCoinPtTStart->debugID());
-    fCoinPtTStart->dumpBase();
-    SkDebugf(" span=%d ptT=%d ", fCoinPtTEnd->span()->debugID(), fCoinPtTEnd->debugID());
-    fCoinPtTEnd->dumpBase();
-    if (fCoinPtTStart->segment()->operand()) {
-        SkDebugf(" operand");
-    }
-    if (fCoinPtTStart->segment()->isXor()) {
-        SkDebugf(" xor");
-    }
-    SkDebugf("\n");
-    SkDebugf("+ seg=%d span=%d ptT=%d ", fOppPtTStart->segment()->debugID(),
-        fOppPtTStart->span()->debugID(), fOppPtTStart->debugID());
-    fOppPtTStart->dumpBase();
-    SkDebugf(" span=%d ptT=%d ", fOppPtTEnd->span()->debugID(), fOppPtTEnd->debugID());
-    fOppPtTEnd->dumpBase();
-    if (fOppPtTStart->segment()->operand()) {
-        SkDebugf(" operand");
-    }
-    if (fOppPtTStart->segment()->isXor()) {
-        SkDebugf(" xor");
-    }
-    SkDebugf("\n");
-}
-
-void SkOpCoincidence::dump() const {
-    SkCoincidentSpans* span = fHead;
-    while (span) {
-        span->dump();
-        span = span->fNext;
-    }
-}
-
-void SkOpContour::dump() {
-    SkDebugf("contour=%d count=%d\n", this->debugID(), fCount);
-    if (!fCount) {
-        return;
-    }
-    const SkOpSegment* segment = &fHead;
-    PATH_OPS_DEBUG_CODE(fIndent = 0);
-    indentDump();
-    do {
-        segment->dump();
-    } while ((segment = segment->next()));
-    outdentDump();
-}
-
-void SkOpContour::dumpAll() {
-    SkDebugf("contour=%d count=%d\n", this->debugID(), fCount);
-    if (!fCount) {
-        return;
-    }
-    const SkOpSegment* segment = &fHead;
-    PATH_OPS_DEBUG_CODE(fIndent = 0);
-    indentDump();
-    do {
-        segment->dumpAll();
-    } while ((segment = segment->next()));
-    outdentDump();
-}
-
-
-void SkOpContour::dumpAngles() const {
-    SkDebugf("contour=%d\n", this->debugID());
-    const SkOpSegment* segment = &fHead;
-    do {
-        SkDebugf("  seg=%d ", segment->debugID());
-        segment->dumpAngles();
-    } while ((segment = segment->next()));
-}
-
-void SkOpContour::dumpPt(int index) const {
-    const SkOpSegment* segment = &fHead;
-    do {
-        if (segment->debugID() == index) {
-            segment->dumpPts();
-        }
-    } while ((segment = segment->next()));
-}
-
-void SkOpContour::dumpPts() const {
-    SkDebugf("contour=%d\n", this->debugID());
-    const SkOpSegment* segment = &fHead;
-    do {
-        SkDebugf("  seg=%d ", segment->debugID());
-        segment->dumpPts();
-    } while ((segment = segment->next()));
-}
-
-void SkOpContour::dumpPtsX() const {
-    if (!this->fCount) {
-        SkDebugf("<empty>\n");
-        return;
-    }
-    const SkOpSegment* segment = &fHead;
-    do {
-        segment->dumpPts();
-    } while ((segment = segment->next()));
-}
-
-void SkOpContour::dumpSegment(int index) const {
-    debugSegment(index)->dump();
-}
-
-void SkOpContour::dumpSegments(SkPathOp op) const {
-    bool firstOp = false;
-    const SkOpContour* c = this;
-    do {
-        if (!firstOp && c->operand()) {
-#if DEBUG_ACTIVE_OP
-            SkDebugf("op %s\n", SkPathOpsDebug::kPathOpStr[op]);
-#endif
-            firstOp = true;
-        }
-        c->dumpPtsX();
-    } while ((c = c->next()));
-}
-
-void SkOpContour::dumpSpan(int index) const {
-    debugSpan(index)->dump();
-}
-
-void SkOpContour::dumpSpans() const {
-    SkDebugf("contour=%d\n", this->debugID());
-    const SkOpSegment* segment = &fHead;
-    do {
-        SkDebugf("  seg=%d ", segment->debugID());
-        segment->dump();
-    } while ((segment = segment->next()));
-}
-
-#ifdef SK_DEBUG
-const SkOpAngle* SkOpGlobalState::debugAngle(int id) const {
-    const SkOpContour* contour = fHead;
-    do {
-        const SkOpSegment* segment = contour->first();
-        while (segment) {
-            const SkOpSpan* span = segment->head();
-            do {
-                SkOpAngle* angle = span->fromAngle();
-                if (angle && angle->debugID() == id) {
-                    return angle;
-                }
-                angle = span->toAngle();
-                if (angle && angle->debugID() == id) {
-                    return angle;
-                }
-            } while ((span = span->next()->upCastable()));
-            const SkOpSpanBase* tail = segment->tail();
-            SkOpAngle* angle = tail->fromAngle();
-            if (angle && angle->debugID() == id) {
-                return angle;
-            }
-            segment = segment->next();
-        }
-    } while ((contour = contour->next()));
-    return NULL;
-}
-
-SkOpContour* SkOpGlobalState::debugContour(int id) {
-    SkOpContour* contour = fHead;
-    do {
-        if (contour->debugID() == id) {
-            return contour;
-        }
-    } while ((contour = contour->next()));
-    return NULL;
-}
-
-const SkOpPtT* SkOpGlobalState::debugPtT(int id) const {
-    const SkOpContour* contour = fHead;
-    do {
-        const SkOpSegment* segment = contour->first();
-        while (segment) {
-            const SkOpSpan* span = segment->head();
-            do {
-                const SkOpPtT* ptT = span->ptT();
-                if (ptT->debugMatchID(id)) {
-                    return ptT;
-                }
-            } while ((span = span->next()->upCastable()));
-            const SkOpSpanBase* tail = segment->tail();
-            const SkOpPtT* ptT = tail->ptT();
-            if (ptT->debugMatchID(id)) {
-                return ptT;
-            }
-            segment = segment->next();
-        }
-    } while ((contour = contour->next()));
-    return NULL;
-}
-
-const SkOpSegment* SkOpGlobalState::debugSegment(int id) const {
-    const SkOpContour* contour = fHead;
-    do {
-        const SkOpSegment* segment = contour->first();
-        while (segment) {
-            if (segment->debugID() == id) {
-                return segment;
-            }
-            segment = segment->next();
-        }
-    } while ((contour = contour->next()));
-    return NULL;
-}
-
-const SkOpSpanBase* SkOpGlobalState::debugSpan(int id) const {
-    const SkOpContour* contour = fHead;
-    do {
-        const SkOpSegment* segment = contour->first();
-        while (segment) {
-            const SkOpSpan* span = segment->head();
-            do {
-                if (span->debugID() == id) {
-                    return span;
-                }
-            } while ((span = span->next()->upCastable()));
-            const SkOpSpanBase* tail = segment->tail();
-            if (tail->debugID() == id) {
-                return tail;
-            }
-            segment = segment->next();
-        }
-    } while ((contour = contour->next()));
-    return NULL;
-}
-#endif
-
-const SkOpAngle* DebugAngle(const SkTArray<SkOpContour*, true>* contours, int id) {
-    return (*contours)[0]->debugAngle(id);
-}
-
-SkOpContour* DumpContour(const SkTArray<SkOpContour*, true>* contours, int id) {
-    return (*contours)[0]->debugContour(id);
-}
-
-const SkOpPtT* DebugPtT(const SkTArray<SkOpContour*, true>* contours, int id) {
-    return (*contours)[0]->debugPtT(id);
-}
-
-const SkOpSegment* DebugSegment(const SkTArray<SkOpContour*, true>* contours, int id) {
-    return (*contours)[0]->debugSegment(id);
-}
-
-const SkOpSpanBase* DebugSpan(const SkTArray<SkOpContour*, true>* contours, int id) {
-    return (*contours)[0]->debugSpan(id);
-}
-
-void Dump(SkTDArray<SkOpContour* >* contours) {
-    SkPathOpsDebug::DumpContours(contours);
-}
-
-void DumpAll(SkTDArray<SkOpContour* >* contours) {
-    SkPathOpsDebug::DumpContoursAll(contours);
-}
-
-void DumpAngles(const SkTDArray<SkOpContour* >* contours) {
-    SkPathOpsDebug::DumpContoursAngles(contours);
-}
-
-void DumpSegment(const SkTDArray<SkOpContour* >* contours, int segmentID) {
-    SkPathOpsDebug::DumpContoursSegment(contours, segmentID);
-}
-
-void DumpSpan(const SkTDArray<SkOpContour* >* contours, int spanID) {
-    SkPathOpsDebug::DumpContoursSpan(contours, spanID);
-}
-
-void DumpSpans(const SkTDArray<SkOpContour* >* contours) {
-    SkPathOpsDebug::DumpContoursSpans(contours);
-}
-
-void DumpPt(const SkTDArray<SkOpContour* >* contours, int segmentID) {
-    SkPathOpsDebug::DumpContoursPt(contours, segmentID);
-}
-
-void DumpPts(const SkTDArray<SkOpContour* >* contours) {
-    SkPathOpsDebug::DumpContoursPts(contours);
-}
-
-#if DEBUG_T_SECT_DUMP > 1
-int gDumpTSectNum;
-#endif
index 4a806c2a51d472095a79b93d83f4367e953a9e33..e0d30ba0b3ae4a59577d32e1df6e2ed9e34a80c2 100644 (file)
@@ -24,7 +24,6 @@
 __SK_FORCE_IMAGE_DECODER_LINKING;
 
 DEFINE_bool2(runFail, f, false, "run tests known to fail.");
-DEFINE_bool2(runBinary, f, false, "run tests known to fail binary sect.");
 
 static const char marker[] =
     "</div>\n"
@@ -48,6 +47,10 @@ static const char* opSuffixes[] = {
     "o",
 };
 
+static bool gShowPath = false;
+static bool gComparePathsAssert = true;
+static bool gPathStrAssert = true;
+
 #if DEBUG_SHOW_TEST_NAME
 static void showPathData(const SkPath& path) {
     SkPath::RawIter iter(path);
@@ -79,13 +82,6 @@ static void showPathData(const SkPath& path) {
                 lastPt = pts[2];
                 lastPtSet = true;
                 break;
-            case SkPath::kConic_Verb:
-                SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},  //weight=%1.9g\n",
-                        pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
-                        iter.conicWeight());
-                lastPt = pts[2];
-                lastPtSet = true;
-                break;
             case SkPath::kCubic_Verb:
                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
@@ -277,7 +273,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
     return true;
 }
 
-int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
+static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
         const SkPath& two, SkBitmap& bitmap) {
     int errors2x2;
     SkPath scaledOne, scaledTwo;
@@ -286,6 +282,7 @@ int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPat
         return 0;
     }
     const int MAX_ERRORS = 9;
+    REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
 }
 
@@ -306,7 +303,7 @@ static void showPathOpPath(const char* testName, const SkPath& one, const SkPath
     *gTestOp.append() = shapeOp;
     ++gTestNo;
     SkDebugf("    SkPath path, pathB;\n");
-#if 0 && DEBUG_SHOW_TEST_NAME
+#if DEBUG_SHOW_TEST_NAME
     SkPathOpsDebug::ShowOnePath(a, "path", false);
     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
 #endif
@@ -337,14 +334,17 @@ static int comparePaths(skiatest::Reporter* reporter, const char* testName, cons
         return 0;
     }
     if (errors2x2 == 0) {
+        if (gShowPath) {
+            showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+        }
         return 0;
     }
-    if (errors2x2 > MAX_ERRORS) {
+    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
         SkAutoMutexAcquire autoM(compareDebugOut3);
         SkDebugf("\n*** this test fails ***\n");
         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
         REPORTER_ASSERT(reporter, 0);
-    } else if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
+    } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
         SkAutoMutexAcquire autoM(compareDebugOut4);
         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
     }
@@ -367,7 +367,7 @@ static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
 static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
         const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
 #if 0
-    outFile.writeText("\n<div id=\"");
+    outFile.writeText("<div id=\"");
     writeTestName(nameSuffix, outFile);
     outFile.writeText("\">\n");
     if (pathPrefix) {
@@ -412,12 +412,15 @@ static void outputToStream(const char* pathStr, const char* pathPrefix, const ch
 }
 
 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
-
 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                   const char* pathStr) {
     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
     path.setFillType(fillType);
-    state.fReporter->bumpTestCount();
+#if DEBUG_SHOW_TEST_NAME
+    if (gShowPath) {
+        SkPathOpsDebug::ShowOnePath(path, "path", false);
+    }
+#endif
     if (!Simplify(path, &out)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(state.fReporter, 0);
@@ -427,7 +430,7 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
         return true;
     }
     int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
-    if (result) {
+    if (result && gPathStrAssert) {
         SkAutoMutexAcquire autoM(simplifyDebugOut);
         char temp[8192];
         sk_bzero(temp, sizeof(temp));
@@ -447,39 +450,23 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
     return result == 0;
 }
 
-static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
-        bool checkFail) {
-#if 0 && DEBUG_SHOW_TEST_NAME
+bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+#if DEBUG_SHOW_TEST_NAME
     showPathData(path);
 #endif
     SkPath out;
     if (!Simplify(path, &out)) {
-        SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
+        SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
     SkBitmap bitmap;
-    int errors = comparePaths(reporter, filename, path, out, bitmap);
-    if (!checkFail) {
-        if (!errors) {
-            SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
-            REPORTER_ASSERT(reporter, 0);
-            return false;
-        }
-    } else if (errors) {
+    int result = comparePaths(reporter, filename, path, out, bitmap);
+    if (result && gPathStrAssert) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
-    return errors == 0;
-}
-
-bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
-    return inner_simplify(reporter, path, filename, true);
-}
-
-bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
-        bool checkFail) {
-    return inner_simplify(reporter, path, filename, checkFail);
+    return result == 0;
 }
 
 #if DEBUG_SHOW_TEST_NAME
@@ -493,7 +480,7 @@ static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
 
 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
         const SkPathOp shapeOp, const char* testName, bool threaded, bool expectSuccess) {
-#if 0 && DEBUG_SHOW_TEST_NAME
+#if DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
     SkPath out;
@@ -502,7 +489,7 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
-    if (!reporter->verbose()) {
+    if (threaded && !reporter->verbose()) {
         return true;
     }
     SkPath pathOut, scaledPathOut;
@@ -531,7 +518,7 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
     scaledOut.setFillType(out.getFillType());
     int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
             a, b, shapeOp, scale, expectSuccess);
-    if (result) {
+    if (result && gPathStrAssert) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
@@ -617,7 +604,6 @@ void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
-                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
     size_t index;
     if (firstTest) {
@@ -626,7 +612,8 @@ void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
             --index;
         }
 #if DEBUG_SHOW_TEST_NAME
-        SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
+        SkDebugf("<div id=\"%s\">\n", tests[index].str);
+        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
 #endif
         (*tests[index].fun)(reporter, tests[index].str);
         if (tests[index].fun == stopTest) {
@@ -635,14 +622,11 @@ void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
     }
     index = reverse ? count - 1 : 0;
     size_t last = reverse ? 0 : count - 1;
-    bool foundSkip = !skipTest;
     do {
-        if (tests[index].fun == skipTest) {
-            foundSkip = true;
-        }
-        if (foundSkip && tests[index].fun != firstTest) {
+        if (tests[index].fun != firstTest) {
     #if DEBUG_SHOW_TEST_NAME
-            SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
+            SkDebugf("<div id=\"%s\">\n", tests[index].str);
+            SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
     #endif
             (*tests[index].fun)(reporter, tests[index].str);
         }
index 0428457b50aec6e211ec524acaa4d63a4ef4e159..5f7e972f49799bd3117ac23b1619d81a52aa1b74 100644 (file)
@@ -17,7 +17,6 @@
 #include "Test.h"
 
 DECLARE_bool(runFail);
-DECLARE_bool(runBinary);
 
 struct PathOpsThreadState;
 
@@ -27,8 +26,7 @@ struct TestDesc {
 };
 
 //extern int comparePaths(const SkPath& one, const SkPath& two);
-extern int comparePaths(skiatest::Reporter* reporter, const char* filename,
-                        const SkPath& one, const SkPath& two, SkBitmap& bitmap);
+extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
 extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths);
 extern void showOp(const SkPathOp op);
 extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
@@ -42,8 +40,6 @@ extern bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, co
 extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                          const char* pathStr);
 extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
-extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
-                              const char* filename, bool checkFail);
 
 void initializeTests(skiatest::Reporter* reporter, const char* testName);
 void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
@@ -51,7 +47,6 @@ void outputProgress(char* ramStr, const char* pathStr, SkPathOp op);
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
-                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse);
 void ShowTestArray();
 void ShowTestName(PathOpsThreadState* data, int a, int b, int c, int d);
index 64eb81924c79b7c446ff83743e40824ceb60c60a..cd851a78ccb3bd03716c3ee8ccb9b4dc9305499f 100755 (executable)
@@ -121,6 +121,11 @@ path.close();
 }
 
 static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) {
+#ifdef SK_BUILD_FOR_ANDROID
+       if (!FLAGS_runFail) {
+               return;  // fails on nexus 9 in release, possibly related to fused multiply-add
+       }
+#endif
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
@@ -214,6 +219,11 @@ path.close();
 }
 
 static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) {
+#ifdef SK_BUILD_FOR_ANDROID
+       if (!FLAGS_runFail) {
+               return;  // fails on nexus 9 in release, possibly related to fused multiply-add
+       }
+#endif
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(-47.1494f, 4.35143f);
@@ -233,7 +243,7 @@ path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc
 path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
 path.close();
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
@@ -254,7 +264,7 @@ static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
     path.quadTo(-39.8065f, 18.9507f, -43.0072f, 19.8086f);
     path.close();
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) {
@@ -495,7 +505,7 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
@@ -585,7 +595,7 @@ path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) {
@@ -620,7 +630,7 @@ path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) {
@@ -709,7 +719,7 @@ path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) {
@@ -932,7 +942,7 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
@@ -1131,6 +1141,7 @@ path.close();
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
+//SkOpSegment.cpp:3475: failed assertion "firstAngle"
 static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
@@ -1436,7 +1447,7 @@ path.quadTo(SkBits2Float(0x421fbff7), SkBits2Float(0x41f8ceed), SkBits2Float(0x4
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) {
@@ -1531,10 +1542,10 @@ path.close();
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
+// SkOpSegment.cpp:4010: failed assertion "span->fOppSum == -0x7FFFFFFF || span->fOppSum == oppWinding
 static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
-#if 00
 path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
 path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e));
 path.quadTo(SkBits2Float(0x418ad549), SkBits2Float(0xc24fca02), SkBits2Float(0x41981613), SkBits2Float(0xc2444f40));
@@ -1545,8 +1556,6 @@ path.quadTo(SkBits2Float(0x40d9eeca), SkBits2Float(0xc218d592), SkBits2Float(0x4
 path.quadTo(SkBits2Float(0x405fd0f0), SkBits2Float(0xc22fcb17), SkBits2Float(0x408b5c58), SkBits2Float(0xc23c98a3));
 path.quadTo(SkBits2Float(0x40a6d038), SkBits2Float(0xc249662f), SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
 path.close();
-#endif
-#if 000
 path.moveTo(SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
 path.quadTo(SkBits2Float(0xc2113c71), SkBits2Float(0xc2240440), SkBits2Float(0xc203fb34), SkBits2Float(0xc22403dc));
 path.quadTo(SkBits2Float(0xc1ed73ee), SkBits2Float(0xc2240379), SkBits2Float(0xc1dab5b7), SkBits2Float(0xc21aa3d1));
@@ -1557,8 +1566,6 @@ path.quadTo(SkBits2Float(0xc2113e50), SkBits2Float(0xc1c8087f), SkBits2Float(0xc
 path.quadTo(SkBits2Float(0xc223fc87), SkBits2Float(0xc1ed871e), SkBits2Float(0xc223fc24), SkBits2Float(0xc20404cc));
 path.quadTo(SkBits2Float(0xc223fbc0), SkBits2Float(0xc2114609), SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
 path.close();
-#endif
-#if 00
 path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
 path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
@@ -1575,15 +1582,11 @@ path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc
 path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
 path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.close();
-#endif
-#if 01
 path.moveTo(SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
 path.lineTo(SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
 path.lineTo(SkBits2Float(0xc2533af7), SkBits2Float(0x41624f68));
 path.quadTo(SkBits2Float(0xc2533a8e), SkBits2Float(0x41625591), SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
 path.close();
-#endif
-#if 0
 path.moveTo(SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
 path.quadTo(SkBits2Float(0x41ed82ea), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
@@ -1599,8 +1602,6 @@ path.quadTo(SkBits2Float(0x41dab5bf), SkBits2Float(0x41dac7c8), SkBits2Float(0x4
 path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
 path.quadTo(SkBits2Float(0x41dac293), SkBits2Float(0x41dabaf3), SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
 path.close();
-#endif
-#if 00001
 path.moveTo(SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
 path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c));
 path.quadTo(SkBits2Float(0xc218dd36), SkBits2Float(0x40d9a0b8), SkBits2Float(0xc2156c96), SkBits2Float(0x411fdb9a));
@@ -1621,12 +1622,10 @@ path.lineTo(SkBits2Float(0xc2533b22), SkBits2Float(0x41624cea));
 path.quadTo(SkBits2Float(0xc256a842), SkBits2Float(0x412f19c8), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
 path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
 path.close();
-#endif
 
     SkPath path1(path);
     path.reset();
     path.setFillType((SkPath::FillType) 0);
-#if 01
 path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
 path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
 path.quadTo(SkBits2Float(0xc256a8ae), SkBits2Float(0x412f1cb2), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
@@ -1637,9 +1636,9 @@ path.quadTo(SkBits2Float(0xc211faaa), SkBits2Float(0x41534d02), SkBits2Float(0xc
 path.quadTo(SkBits2Float(0xc21f3c59), SkBits2Float(0x41979082), SkBits2Float(0xc22c0a07), SkBits2Float(0x419e6c7a));
 path.quadTo(SkBits2Float(0xc238d7b6), SkBits2Float(0x41a54872), SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
 path.close();
-#endif
+
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) {
@@ -2198,10 +2197,13 @@ path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x4
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) {
+    if (!FLAGS_runFail) { // FIXME: asserts in alignSpanState
+        return;
+    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
@@ -2394,7 +2396,6 @@ path.close();
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_2674194;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
@@ -2439,5 +2440,5 @@ DEF_TEST(PathOpsFuzz763, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
 }
index bc0259c9c9f8c37acd36edfa0fd26dee5c236428..105187be64c8f72b1685262f278b21416250b8ee 100644 (file)
@@ -11,9 +11,6 @@
 
 // FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
 static const SkDLine tests[][2] = {
-{{{{0.00010360032320022583, 1.0172703415155411}, {0.00014114845544099808, 1.0200891587883234}}},
- {{{0.00010259449481964111, 1.017270140349865}, {0.00018215179443359375, 1.022890567779541}}}},
-
 #if 0
     // these do intersect at a pair of points, but not close enough for check results liking
     {{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
@@ -85,13 +82,10 @@ static const SkDLine coincidentTests[][2] = {
 static const size_t coincidentTests_count = SK_ARRAY_COUNT(coincidentTests);
 
 static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
-                          const SkIntersections& ts, bool nearAllowed) {
+                          const SkIntersections& ts) {
     for (int i = 0; i < ts.used(); ++i) {
         SkDPoint result1 = line1.ptAtT(ts[0][i]);
         SkDPoint result2 = line2.ptAtT(ts[1][i]);
-        if (nearAllowed && result1.roughlyEqual(result2)) {
-            continue;
-        }
         if (!result1.approximatelyEqual(result2) && !ts.nearlySame(i)) {
             REPORTER_ASSERT(reporter, ts.used() != 1);
             result2 = line2.ptAtT(ts[1][i ^ 1]);
@@ -104,16 +98,14 @@ static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, co
     }
 }
 
-static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
-        bool nearAllowed) {
+static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) {
     SkASSERT(ValidLine(line1));
     SkASSERT(ValidLine(line2));
     SkIntersections i;
-    i.allowNear(nearAllowed);
     int pts = i.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts);
     REPORTER_ASSERT(reporter, pts == i.used());
-    check_results(reporter, line1, line2, i, nearAllowed);
+    check_results(reporter, line1, line2, i);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -122,28 +114,28 @@ static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const Sk
         double right = SkTMax(line1[0].fX, line1[1].fX);
         SkIntersections ts;
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
-        check_results(reporter, line2, line1, ts, nearAllowed);
+        check_results(reporter, line2, line1, ts);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
         double right = SkTMax(line2[0].fX, line2[1].fX);
         SkIntersections ts;
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
-        check_results(reporter, line1, line2, ts, nearAllowed);
+        check_results(reporter, line1, line2, ts);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
         double bottom = SkTMax(line1[0].fY, line1[1].fY);
         SkIntersections ts;
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
-        check_results(reporter, line2, line1, ts, nearAllowed);
+        check_results(reporter, line2, line1, ts);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
         double bottom = SkTMax(line2[0].fY, line2[1].fY);
         SkIntersections ts;
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
-        check_results(reporter, line1, line2, ts, nearAllowed);
+        check_results(reporter, line1, line2, ts);
     }
     reporter->bumpTestCount();
 }
@@ -156,7 +148,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
     int pts = ts.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts == 2);
     REPORTER_ASSERT(reporter, pts == ts.used());
-    check_results(reporter, line1, line2, ts, false);
+    check_results(reporter, line1, line2, ts);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -167,7 +159,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts, false);
+        check_results(reporter, line2, line1, ts);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
@@ -176,7 +168,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts, false);
+        check_results(reporter, line1, line2, ts);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
@@ -185,7 +177,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts, false);
+        check_results(reporter, line2, line1, ts);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
@@ -194,7 +186,7 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts, false);
+        check_results(reporter, line1, line2, ts);
     }
     reporter->bumpTestCount();
 }
@@ -209,7 +201,7 @@ DEF_TEST(PathOpsLineIntersection, reporter) {
     for (index = 0; index < tests_count; ++index) {
         const SkDLine& line1 = tests[index][0];
         const SkDLine& line2 = tests[index][1];
-        testOne(reporter, line1, line2, true);
+        testOne(reporter, line1, line2);
     }
     for (index = 0; index < noIntersect_count; ++index) {
         const SkDLine& line1 = noIntersect[index][0];
@@ -225,13 +217,8 @@ DEF_TEST(PathOpsLineIntersection, reporter) {
 DEF_TEST(PathOpsLineIntersectionOneOff, reporter) {
     int index = 0;
     SkASSERT(index < (int) tests_count);
-    testOne(reporter, tests[index][0], tests[index][1], true);
-}
-
-DEF_TEST(PathOpsLineIntersectionExactOneOff, reporter) {
-    int index = 0;
-    SkASSERT(index < (int) tests_count);
-    testOne(reporter, tests[index][0], tests[index][1], false);
+    testOne(reporter, tests[index][0], tests[index][1]);
+    testOne(reporter, tests[1][0], tests[1][1]);
 }
 
 DEF_TEST(PathOpsLineIntersectionOneCoincident, reporter) {
index 5815cf66f7cec3818f29b34b99faba5874193582..751ccc5f1baedfa01089b494b1fad988fa2bf2ee 100644 (file)
@@ -27,10 +27,6 @@ static void testOpCubicsMain(PathOpsThreadState* data) {
         SkPath pathA, pathB;
         if (progress) {
             char* str = pathStr;
-            const int loopNo = 129;
-            str += sprintf(str, "static void cubicOp%d(skiatest::Reporter* reporter,"
-                    " const char* filename) {\n", loopNo);
-            str += sprintf(str, "    SkPath path, pathB;\n");
             str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
                     e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
                     ? "EvenOdd" : "?UNDEFINED");
@@ -45,9 +41,6 @@ static void testOpCubicsMain(PathOpsThreadState* data) {
             str += sprintf(str, "    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
                     state.fB, state.fA, state.fD, state.fC);
             str += sprintf(str, "    pathB.close();\n");
-            str += sprintf(str, "    testPathOp(reporter, path, pathB, kDifference_PathOp,"
-                    " filename);\n");
-            str += sprintf(str, "}\n");
         }
         pathA.setFillType((SkPath::FillType) e);
         pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));
index 40bf2cbeef6be59b917df2089b31ac608d907891..c50e23bae92be26df3951f802c2c5e535abced6c 100755 (executable)
@@ -7,24 +7,6 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
 
-static int add_point(char* str, SkScalar x, SkScalar y) {
-    int result;
-    int asInt = SkScalarRoundToInt(x);
-    if (SkIntToScalar(asInt) == x) {
-        result = sprintf(str, "%d", asInt);
-    } else {
-        result = sprintf(str, "%1.9gf", x);
-    }
-    result += sprintf(str + result, ",");
-    asInt = SkScalarRoundToInt(y);
-    if (SkIntToScalar(asInt) == y) {
-        result += sprintf(str + result, "%d", asInt);
-    } else {
-        result += sprintf(str + result, "%1.9gf", y);
-    }
-    return result;
-}
-
 static void testOpLoopsMain(PathOpsThreadState* data) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
@@ -53,27 +35,14 @@ static void testOpLoopsMain(PathOpsThreadState* data) {
         SkPath pathA, pathB;
         if (progress) {
             char* str = pathStr;
-            const int loopNo = 7;
-            str += sprintf(str, "static void loop%d(skiatest::Reporter* reporter,"
-                    " const char* filename) {\n", loopNo);
-            str += sprintf(str, "    SkPath path, pathB;\n");
             str += sprintf(str, "    path.moveTo(%d,%d);\n", a, b);
-            str += sprintf(str, "    path.cubicTo(%d,%d, ", c, d);
-            str += add_point(str, endC.fX, endC.fY);
-            str += sprintf(str, ", ");
-            str += add_point(str, endD.fX, endD.fY);
-            str += sprintf(str, ");\n");
+            str += sprintf(str, "    path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
+                    c, d, endC.fX, endC.fY, endD.fX, endD.fY);
             str += sprintf(str, "    path.close();\n");
             str += sprintf(str, "    pathB.moveTo(%d,%d);\n", c, d);
-            str += sprintf(str, "    pathB.cubicTo(");
-            str += add_point(str, endC.fX, endC.fY);
-            str += sprintf(str, ", ");
-            str += add_point(str, endD.fX, endD.fY);
-            str += sprintf(str, ", %d,%d);\n", a, b);
+            str += sprintf(str, "    pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n",
+                    endC.fX, endC.fY, endD.fX, endD.fY, a, b);
             str += sprintf(str, "    pathB.close();\n");
-            str += sprintf(str, "    testPathOp(reporter, path, pathB, kIntersect_PathOp,"
-                    " filename);\n");
-            str += sprintf(str, "}\n");
         }
         pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b));
         pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY);
@@ -93,6 +62,9 @@ static void testOpLoopsMain(PathOpsThreadState* data) {
 }
 
 DEF_TEST(PathOpsOpLoopsThreaded, reporter) {
+    if (!FLAGS_runFail) {
+        return;
+    }
     initializeTests(reporter, "cubicOp");
     PathOpsThreadedTestRunner testRunner(reporter);
     for (int a = 0; a < 6; ++a) {  // outermost
@@ -112,6 +84,9 @@ finish:
 }
 
 DEF_TEST(PathOpsOpLoops, reporter) {
+    if (!FLAGS_runFail) {
+        return;
+    }
     initializeTests(reporter, "cubicOp");
     PathOpsThreadState state;
     state.fReporter = reporter;
index 6b50cc920c46e0377ba6ccfaec9b13a652b2fa02..fbfa0b56a7efe01b13c23eccdaff1efaef8ba1c8 100644 (file)
@@ -713,6 +713,11 @@ static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// this fails to detect a cubic/cubic intersection
+// the slight overlap is missed when the cubics are approximated by quadratics
+// and the subsequent line/cubic intersection also (correctly) misses the intersection
+// if the line/cubic was a matching line/approx.quadratic then the missing intersection
+// could have been detected
 static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -1790,6 +1795,9 @@ static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// this fails because the pair of nearly coincident cubics intersect at the ends
+// but the line connected to one of the cubics at the same point does not intersect
+// the other
 static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1926,7 +1934,7 @@ static void issue1417(skiatest::Reporter* reporter, const char* filename) {
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.close();
-    // FIXME : difficult data, circle back later
+
     testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
 }
 
@@ -2048,6 +2056,9 @@ static void rectOp3x(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
 }
 
+// this fails to generate two interior line segments 
+// an earlier pathops succeeded, but still failed to generate one interior line segment
+// (but was saved by assemble, which works around a single line missing segment)
 static void issue1435(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1;
     path1.moveTo(160, 60);
@@ -2245,7 +2256,7 @@ static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
 }
 
-static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {
+static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
    path.moveTo(-1.24344979e-014f, 348);
@@ -2266,7 +2277,7 @@ static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filenam
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {
+static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.99840144e-015f, 494);
@@ -2289,7 +2300,7 @@ static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {
+static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {  // partial coincidence
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(670.537415f, 285);
@@ -2315,7 +2326,7 @@ static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filena
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
+static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {  // bridge op
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.45716772e-016f, 924.336121f);
@@ -2340,7 +2351,7 @@ static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
+static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {  // zero span
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(320.097229f, 628.573669f);
@@ -2364,7 +2375,7 @@ static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
+static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {  // find chase op
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 926);
@@ -2383,7 +2394,7 @@ static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) {
+static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { // calc common
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(205.605804f, 142.334625f);
@@ -2407,7 +2418,7 @@ static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {
+static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {  // mark and chase winding
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(-4.4408921e-016f, 682.5f);
@@ -2428,7 +2439,7 @@ static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename)
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
-static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {
+static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {  // cubic/cubic intersect
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(324.071075f, 845.071045f);
@@ -2455,7 +2466,7 @@ static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filena
     pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711);
     pathB.lineTo(317, 711);
     pathB.close();
-    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
 static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) {
@@ -2713,6 +2724,7 @@ static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filen
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// this fails because cubic/quad misses an intersection (failure is isolated in c/q int test)
 static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2736,7 +2748,7 @@ static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
     pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f);
     pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
 }
 
 static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) {
@@ -3235,6 +3247,7 @@ static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// triggers addSimpleAngle with non-zero argument
 static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -3269,7 +3282,7 @@ static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
     pathB.moveTo(1, 3);
     pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
 }
 
 static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) {
@@ -3451,7 +3464,7 @@ static void issue2753(skiatest::Reporter* reporter, const char* filename) {
     path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f);
     path2.close();
 
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOpCheck(reporter, path1, path2, kUnion_PathOp, filename, FLAGS_runFail);
 }
 
 static void issue2808(skiatest::Reporter* reporter, const char* filename) {
@@ -3496,335 +3509,12 @@ static void cubicOp115(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
-static void testRect1(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, path2;
-    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
-    path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-//    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
-    testPathOp(reporter, path, path2, kUnion_PathOp, filename);
-}
-
-static void testRect2(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(4, 4, 5, 5, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp116(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(4,6, 2,0, 2,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,2);
-    pathB.cubicTo(0,2, 1,0, 6,4);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp117(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(4,5, 6,0, 1,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,6);
-    pathB.cubicTo(0,1, 1,0, 5,4);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp118(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(4,6, 5,1, 6,2);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(1,5);
-    pathB.cubicTo(2,6, 1,0, 6,4);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void loop1(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,1);
-    path.cubicTo(1,5, -5.66666651f,3.33333349f, 8.83333302f,2.33333349f);
-    path.close();
-    pathB.moveTo(1,5);
-    pathB.cubicTo(-5.66666651f,3.33333349f, 8.83333302f,2.33333349f, 0,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-#include "SkPathOpsCubic.h"
-
-static void loop1asQuad(skiatest::Reporter* reporter, const char* filename) {
-    SkDCubic c1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}};
-    SkDCubic c2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}};
-    double c1InflectionTs[2], c2InflectionTs[2];
-    SkDEBUGCODE(int c1InfTCount =) c1.findInflections(c1InflectionTs);
-    SkASSERT(c1InfTCount == 2);
-    SkDEBUGCODE(int c2InfTCount =) c2.findInflections(c2InflectionTs);
-    SkASSERT(c2InfTCount == 1);
-    SkASSERT(c1InflectionTs[0] > c1InflectionTs[1]);
-    SkDCubicPair c1pair = c1.chopAt(c1InflectionTs[0]);
-    SkDCubicPair c1apair = c1pair.first().chopAt(c1InflectionTs[1]);
-    SkDCubicPair c2pair = c2.chopAt(c2InflectionTs[0]);
-    SkDQuad q1[2] = { c1pair.first().toQuad(), c1pair.second().toQuad() };
-    SkDQuad q1a[2] = { c1apair.first().toQuad(), c1apair.second().toQuad() };
-    SkDQuad q2[2] = { c2pair.first().toQuad(), c2pair.second().toQuad() };
-    SkPath path, pathB;
-    path.moveTo(q1a[0].fPts[0].asSkPoint());
-    path.quadTo(q1a[0].fPts[1].asSkPoint(), q1a[0].fPts[2].asSkPoint());
-    path.quadTo(q1a[1].fPts[1].asSkPoint(), q1a[1].fPts[2].asSkPoint());
-    path.quadTo(q1[1].fPts[1].asSkPoint(), q1[1].fPts[2].asSkPoint());
-    path.close();
-    pathB.moveTo(q2[0].fPts[0].asSkPoint());
-    pathB.quadTo(q2[0].fPts[1].asSkPoint(), q2[0].fPts[2].asSkPoint());
-    pathB.quadTo(q2[1].fPts[1].asSkPoint(), q2[1].fPts[2].asSkPoint());
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void loop2(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,1);
-    path.cubicTo(3,4, 3.f,4.f, 4.5f,1.5f);
-    path.close();
-    pathB.moveTo(3,4);
-    pathB.cubicTo(3.f,4.f, 4.5f,1.5f, 0,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void loop3(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,1);
-    path.cubicTo(3,5, -3.66666651f,0, 10.5f,-1.66666651f);
-    path.close();
-    pathB.moveTo(3,5);
-    pathB.cubicTo(-3.66666651f,0, 10.5f,-1.66666651f, 0,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void loop4(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,5);
-    path.cubicTo(1,5, 1,4, 0.833333313f,3);
-    path.close();
-    pathB.moveTo(1,5);
-    pathB.cubicTo(1,4, 0.833333313f,3, 0,5);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-#include "SkParsePath.h"
-
-static void issue3517(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-
-    const char str[] = "M31.35 57.75L31.35 57.75C31.9 57.7486 32.45 57.7948 33 57.7413C33.55 57.6878 34.1 57.5014 34.65 57.4291C35.2 57.3569 35.75 57.3223 36.3 57.3079C36.85 57.2935 37.4 57.3143 37.95 57.3428C38.5 57.3712 39.05 57.4112 39.6 57.4786C40.15 57.546 40.7 57.7029 41.25 57.7472C41.8 57.7916 42.35 57.7962 42.9 57.7445C43.45 57.6928 44 57.5345 44.55 57.4373C45.1 57.34 45.65 57.2115 46.2 57.1611C46.75 57.1107 47.3 57.1371 47.85 57.1349C48.4 57.1327 48.95 57.144 49.5 57.1478C50.05 57.1516 50.6 57.1553 51.15 57.1579C51.7 57.1605 52.25 57.1601 52.8 57.1634C53.35 57.1667 53.9 57.1731 54.45 57.1776C55 57.182 55.55 57.1916 56.1 57.19C56.65 57.1884 57.2 57.178 57.75 57.168C58.3 57.158 58.85 57.1355 59.4 57.1299C59.95 57.1243 60.5 57.1338 61.05 57.1345C61.6 57.1352 62.15 57.124 62.7 57.134C63.25 57.1441 63.8 57.1731 64.35 57.195C64.9 57.2169 65.45 57.2532 66 57.2655C66.55 57.2778 67.1 57.2647 67.65 57.2687C68.2 57.2728 68.75 57.267 69.3 57.2896C69.85 57.3122 70.4 57.371 70.95 57.4044C71.5 57.4377 72.05 57.4668 72.6 57.4896C73.15 57.5123 73.7 57.545 74.25 57.5408C74.8 57.5365 75.35 57.5068 75.9 57.4641C76.45 57.4213 77 57.3244 77.55 57.2842C78.1 57.244 78.65 57.2163 79.2 57.2228C79.75 57.2293 80.3 57.29 80.85 57.3232C81.4 57.3563 81.95 57.396 82.5 57.4219C83.05 57.4478 83.6 57.4637 84.15 57.4787C84.7 57.4937 85.25 57.5011 85.8 57.5121C86.35 57.523 86.9 57.5411 87.45 57.5444C88 57.5477 88.55 57.5663 89.1 57.5318C89.65 57.4972 90.2 57.3126 90.75 57.337C91.3 57.3613 91.85 57.6088 92.4 57.6776C92.95 57.7465 93.5 57.7379 94.05 57.75C94.6 57.7621 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
-    SkParsePath::FromSVGString(str, &path);
-
-    const char strB[] = "M31.35 57.75L31.35 57.75C31.9 57.7514 32.45 57.7052 33 57.7587C33.55 57.8122 34.1 57.9986 34.65 58.0709C35.2 58.1431 35.75 58.1777 36.3 58.1921C36.85 58.2065 37.4 58.1857 37.95 58.1572C38.5 58.1288 39.05 58.0888 39.6 58.0214C40.15 57.954 40.7 57.7971 41.25 57.7528C41.8 57.7084 42.35 57.7038 42.9 57.7555C43.45 57.8072 44 57.9655 44.55 58.0627C45.1 58.16 45.65 58.2885 46.2 58.3389C46.75 58.3893 47.3 58.3629 47.85 58.3651C48.4 58.3673 48.95 58.356 49.5 58.3522C50.05 58.3484 50.6 58.3447 51.15 58.3421C51.7 58.3395 52.25 58.3399 52.8 58.3366C53.35 58.3333 53.9 58.3269 54.45 58.3224C55 58.318 55.55 58.3084 56.1 58.31C56.65 58.3116 57.2 58.322 57.75 58.332C58.3 58.342 58.85 58.3645 59.4 58.3701C59.95 58.3757 60.5 58.3662 61.05 58.3655C61.6 58.3648 62.15 58.376 62.7 58.366C63.25 58.3559 63.8 58.3269 64.35 58.305C64.9 58.2831 65.45 58.2468 66 58.2345C66.55 58.2222 67.1 58.2353 67.65 58.2313C68.2 58.2272 68.75 58.233 69.3 58.2104C69.85 58.1878 70.4 58.129 70.95 58.0956C71.5 58.0623 72.05 58.0332 72.6 58.0104C73.15 57.9877 73.7 57.955 74.25 57.9592C74.8 57.9635 75.35 57.9932 75.9 58.0359C76.45 58.0787 77 58.1756 77.55 58.2158C78.1 58.256 78.65 58.2837 79.2 58.2772C79.75 58.2707 80.3 58.21 80.85 58.1768C81.4 58.1437 81.95 58.104 82.5 58.0781C83.05 58.0522 83.6 58.0363 84.15 58.0213C84.7 58.0063 85.25 57.9989 85.8 57.9879C86.35 57.977 86.9 57.9589 87.45 57.9556C88 57.9523 88.55 57.9337 89.1 57.9682C89.65 58.0028 90.2 58.1874 90.75 58.163C91.3 58.1387 91.85 57.8912 92.4 57.8224C92.95 57.7535 93.5 57.7621 94.05 57.75C94.6 57.7379 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
-    SkParsePath::FromSVGString(strB, &pathB);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
-}
-
-static void cubicOp119(skiatest::Reporter* reporter, const char* filename) {
-    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();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void cubicOp120(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(2,4, 2,1, 4,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(1,2);
-    pathB.cubicTo(0,4, 1,0, 4,2);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp121(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(3,4, 3,2, 4,3);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(2,3);
-    pathB.cubicTo(3,4, 1,0, 4,3);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-// FIXME : haven't debugged this failure yet
-static void cubicOp122(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(3,5, 4,1, 4,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(1,4);
-    pathB.cubicTo(0,4, 1,0, 5,3);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp123(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(1,5, 2,0, 6,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,2);
-    pathB.cubicTo(0,6, 1,0, 5,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void loop5(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,2);
-    path.cubicTo(1,2, 1,1.66666663f, 0.833333313f,1.33333325f);
-    path.close();
-    pathB.moveTo(1,2);
-    pathB.cubicTo(1,1.66666663f, 0.833333313f,1.33333325f, 0,2);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void loop6(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.moveTo(0,1);
-    path.cubicTo(1,3, -1.66666675f,1.66666663f, 4.16666651f,1.00000012f);
-    path.close();
-    pathB.moveTo(1,3);
-    pathB.cubicTo(-1.66666675f,1.66666663f, 4.16666651f,1.00000012f, 0,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void cubicOp124(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(1,5, 6,0, 3,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,6);
-    pathB.cubicTo(0,3, 1,0, 5,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp125(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(3,6, 3,1, 6,2);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(1,3);
-    pathB.cubicTo(2,6, 1,0, 6,3);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp126(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(0,3, 6,0, 2,1);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,6);
-    pathB.cubicTo(1,2, 1,0, 3,0);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void cubicOp127(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(1,5, 6,0, 3,0);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(0,6);
-    pathB.cubicTo(0,3, 1,0, 5,1);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void cubicOp128(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.moveTo(0,1);
-    path.cubicTo(0,3, 3,2, 5,2);
-    path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.moveTo(2,3);
-    pathB.cubicTo(2,5, 1,0, 3,0);
-    pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
-}
-
-static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
-    TEST(cubicOp128),
-    TEST(cubicOp127),
-    TEST(cubicOp126),
-    TEST(cubicOp125),
-    TEST(cubicOp124),
-    TEST(loop6),
-    TEST(loop5),
-    TEST(cubicOp123),
-    TEST(cubicOp122),
-    TEST(cubicOp121),
-    TEST(cubicOp120),
-    TEST(cubicOp119),
-    TEST(loop4),
-    TEST(loop3),
-    TEST(loop2),
-    TEST(loop1asQuad),
-    TEST(loop1),
-    TEST(issue3517),
-    TEST(cubicOp118),
-    TEST(cubicOp117),
-    TEST(cubicOp116),
-    TEST(testRect2),
-    TEST(testRect1),
     TEST(cubicOp115),
-    TEST(issue2753),
+    TEST(issue2753),  // FIXME: pair of cubics miss intersection
     TEST(cubicOp114),  // FIXME: curve with inflection is ordered the wrong way
     TEST(issue2808),
     TEST(cubicOp114asQuad),
@@ -3837,6 +3527,8 @@ static struct TestDesc tests[] = {
     TEST(kari1),
     TEST(quadOp10i),
     TEST(cubicOp113),
+    // fails because a cubic/quadratic intersection is missed
+    // the internal quad/quad is far enough away from the real cubic/quad that it is rejected
     TEST(skpcarrot_is24),
     TEST(issue1417),
     TEST(cubicOp112),
@@ -3863,7 +3555,7 @@ static struct TestDesc tests[] = {
     TEST(issue1435),
     TEST(cubicOp98x),
     TEST(cubicOp97x),
-    TEST(skpcarpetplanet_ru22),
+    TEST(skpcarpetplanet_ru22),  // cubic/cubic intersect detects unwanted coincidence
     TEST(cubicOp96d),
     TEST(cubicOp95u),
     TEST(skpadbox_lt15),
@@ -4055,11 +3747,11 @@ DEF_TEST(PathOpsOp, reporter) {
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
     }
 }
 
@@ -4068,7 +3760,7 @@ static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
     path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
     SkPath pathB;
     pathB.addRect(0,0, 300,16);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename);
 }
 
 // m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
@@ -4088,7 +3780,7 @@ static void fuzz433(skiatest::Reporter* reporter, const char* filename) {
     path2.lineTo(-170 + 20,11000000000.0f + 20);
     path2.close();
 
-    testPathOpCheck(reporter, path1, path2, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename);
 }
 
 static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
@@ -4111,7 +3803,7 @@ static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
     path2.lineTo(190, 60);
     path2.close();
 
-    testPathOpCheck(reporter, path1, path2, kUnion_PathOp, filename, FLAGS_runFail);
+    testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
 }
 
 static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
@@ -4157,7 +3849,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
@@ -4203,7 +3895,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
@@ -4270,5 +3962,5 @@ DEF_TEST(PathOpsFailOp, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, failTests, failTestCount, NULL, NULL, NULL, false);
+    RunTestSet(reporter, failTests, failTestCount, 0, 0, false);
 }
index 0455802e3b8da69480af6e5511ec0ed3de5d737f..565098af81c0ce57da6d6138abfcd553b5ab32ec 100644 (file)
@@ -53,36 +53,6 @@ static void standardTestCases(skiatest::Reporter* reporter) {
 }
 
 static const SkDQuad testSet[] = {
-{{{4981.9990234375, 1590}, {4981.9990234375, 1617.7523193359375}, {4962.375, 1637.3760986328125}}},
-{{{4962.3759765625, 1637.3760986328125}, {4982, 1617.7523193359375}, {4982, 1590}}},
-
-{{{48.7416f, 7.74160004f}, {96.4831848f, -40}, {164, -40}}},
-{{{56.9671326f, 0}, {52.7835083f, 3.69968891f}, {48.7416f, 7.74160004f}}},
-
-{{{138, 80}, {147.15692138671875, 80}, {155.12803649902344, 82.86279296875}}},
-{{{155.12803649902344, 82.86279296875}, {153.14971923828125, 82.152290344238281}, {151.09841918945312, 81.618133544921875}}},
-
-{{{88, 130}, {88, 131.54483032226562}, {88.081489562988281, 133.0560302734375}}},
-{{{88.081489562988281, 133.0560302734375}, {88, 131.54483032226562}, {88, 130}}},
-
-{{{0.59987992,2.14448452}, {0.775417507,1.95606446}, {1.00564098,1.79310346}}},
-{{{1.00564098,1.79310346}, {1.25936198,1.615623}, {1.35901463,1.46834028}}},
-
-{{{3,0}, {0,1}, {3,2}}},
-{{{2,0}, {1,1}, {2,2}}},
-
-{{{38.656852722167969, 38.656852722167969}, {38.651023864746094, 38.662681579589844}, {38.644744873046875, 38.668937683105469}}},
-{{{38.656852722167969, 38.656852722167969}, {36.313709259033203, 41}, {33, 41}}},
-
-{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
-{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
-
-{{{4867.623046875, 1637.3760986328125}, {4847.9990234375, 1617.7523193359375}, {4847.9990234375, 1590}}},
-{{{4848, 1590}, {4848, 1617.7523193359375}, {4867.6240234375, 1637.3760986328125}}},
-
-{{{102.64466094970703, 165.3553466796875}, {110.79246520996094, 173.50314331054687}, {120.81797790527344, 177.11778259277344}}},
-{{{113.232177734375, 173.57899475097656}, {116.88026428222656, 175.69805908203125}, {120.81797790527344, 177.11778259277344}}},
-
 {{{-37.3484879,10.0192947}, {-36.4966316,13.2140198}, {-38.1506348,16.0788383}}},
 {{{-38.1462746,16.08918}, {-36.4904327,13.2193804}, {-37.3484879,10.0192947}}},
 
@@ -350,8 +320,6 @@ static void oneOffTests(skiatest::Reporter* reporter) {
 }
 
 static const SkDQuad coincidentTestSet[] = {
-    {{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
-    {{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
 #if 0
     {{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}},
     {{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}},
@@ -371,9 +339,9 @@ static void coincidentTestOne(skiatest::Reporter* reporter, int test1, int test2
     SkASSERT(ValidQuad(quad2));
     SkIntersections intersections2;
     intersections2.intersect(quad1, quad2);
-    REPORTER_ASSERT(reporter, intersections2.coincidentUsed() >= 2);
-    REPORTER_ASSERT(reporter, intersections2.used() >= 2);
-    for (int pt = 0; pt < intersections2.coincidentUsed(); pt += 2) {
+    REPORTER_ASSERT(reporter, intersections2.coincidentUsed() == 2);
+    REPORTER_ASSERT(reporter, intersections2.used() == 2);
+    for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
         double tt1 = intersections2[0][pt];
         double tt2 = intersections2[1][pt];
         SkDPoint pt1 = quad1.ptAtT(tt1);
@@ -422,8 +390,9 @@ static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) {
         left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]);
         SkDLine diag = {{q1[0], q1[2]}};
         left[2] = diag.isLeft(q2[index]);
-        SkDebugf("%s left=(%d, %d, %d)\n", __FUNCTION__, floatSign(left[0]),
-                floatSign(left[1]), floatSign(left[2]));
+        SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
+                floatSign(left[1]), floatSign(left[2]),
+                q1.pointInHull(q2[index]) ? "true" : "false");
     }
     SkDebugf("\n");
 }
@@ -550,14 +519,6 @@ static void QuadraticIntersection_IntersectionFinder() {
     intersectionFinder(0, 1);
 }
 
-DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
-    oneOffTest1(reporter, 10, 11);
-}
-
-DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
-    coincidentTestOne(reporter, 0, 1);
-}
-
 DEF_TEST(PathOpsQuadIntersection, reporter) {
     oneOffTests(reporter);
     coincidentTest(reporter);
@@ -566,31 +527,10 @@ DEF_TEST(PathOpsQuadIntersection, reporter) {
     if (false) QuadraticIntersection_PointFinder();
 }
 
-#include "SkCommonFlags.h"
+DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
+    coincidentTestOne(reporter, 0, 1);
+}
 
-DEF_TEST(PathOpsQuadBinaryProfile, reporter) {
-    if (!FLAGS_veryVerbose) {
-            return;
-    }
-    SkIntersections intersections;
-    for (int x = 0; x < 100; ++x) {
-        int outer = 0;
-        int inner = outer + 1;
-        do {
-            const SkDQuad& quad1 = testSet[outer];
-            const SkDQuad& quad2 = testSet[inner];
-            (void) intersections.intersect(quad1, quad2);
-            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
-            inner += 2;
-            outer += 2;
-        } while (outer < (int) testSetCount);
-    }
-    for (int x = 0; x < 100; ++x) {
-        for (size_t test = 0; test < quadraticTests_count; ++test) {
-            const SkDQuad& quad1 = quadraticTests[test][0];
-            const SkDQuad& quad2 = quadraticTests[test][1];
-            (void) intersections.intersect(quad1, quad2);
-            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
-        }
-    }
+DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
+    oneOffTest1(reporter, 0, 1);
 }
index f51f9518bd12b3b817776749a2b97a55d8ca11fe..0706efcf4571dc59e868a35e022327ce27f3240f 100644 (file)
@@ -44,10 +44,10 @@ const SkDQuad quadraticLines[] = {
 
 const size_t quadraticLines_count = SK_ARRAY_COUNT(quadraticLines);
 
-static const double F = FLT_EPSILON * 32;
-static const double H = FLT_EPSILON * 32;
-static const double J = FLT_EPSILON * 32;
-static const double K = FLT_EPSILON * 32;  // INVESTIGATE: why are larger multiples necessary?
+static const double F = FLT_EPSILON * 3;
+static const double H = FLT_EPSILON * 4;
+static const double J = FLT_EPSILON * 5;
+static const double K = FLT_EPSILON * 8;  // INVESTIGATE: why are larger multiples necessary?
 
 const SkDQuad quadraticModEpsilonLines[] = {
     {{{0, F}, {0, 0}, {1, 0}}},
@@ -64,7 +64,7 @@ const SkDQuad quadraticModEpsilonLines[] = {
     {{{1, 1+J}, {2, 2}, {3, 3}}},
     {{{1, 1}, {3, 3}, {3+F, 3}}},
     {{{1, 1}, {1+F, 1}, {2, 2}}},
-    {{{1, 1}, {2, 2}, {1, 1+K}}},
+    {{{1, 1}, {2, 2}, {1, 1+F}}},
     {{{1, 1}, {1, 1+F}, {3, 3}}},
     {{{1+H, 1}, {2, 2}, {4, 4}}},  // no coincident
     {{{1, 1+K}, {3, 3}, {4, 4}}},
diff --git a/tests/PathOpsQuadParameterizationTest.cpp b/tests/PathOpsQuadParameterizationTest.cpp
new file mode 100644 (file)
index 0000000..c7a2e87
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkDQuadImplicit.h"
+#include "SkPathOpsQuad.h"
+#include "Test.h"
+
+static bool point_on_parameterized_curve(const SkDQuad& quad, const SkDPoint& point) {
+    SkDQuadImplicit q(quad);
+    double  xx = q.x2() * point.fX * point.fX;
+    double  xy = q.xy() * point.fX * point.fY;
+    double  yy = q.y2() * point.fY * point.fY;
+    double   x = q.x() * point.fX;
+    double   y = q.y() * point.fY;
+    double   c = q.c();
+    double sum = xx + xy + yy + x + y + c;
+    return approximately_zero(sum);
+}
+
+static const SkDQuad quadratics[] = {
+    {{{0, 0}, {1, 0}, {1, 1}}},
+};
+
+static const int quadratics_count = (int) SK_ARRAY_COUNT(quadratics);
+
+DEF_TEST(PathOpsQuadImplicit, reporter) {
+    // split large quadratic
+    // compare original, parts, to see if the are coincident
+    for (int index = 0; index < quadratics_count; ++index) {
+        const SkDQuad& test = quadratics[index];
+        SkDQuadPair split = test.chopAt(0.5);
+        SkDQuad midThird = test.subDivide(1.0/3, 2.0/3);
+        const SkDQuad* quads[] = {
+            &test, &midThird, &split.first(), &split.second()
+        };
+        int quadsCount = (int) SK_ARRAY_COUNT(quads);
+        for (int one = 0; one < quadsCount; ++one) {
+            for (int two = 0; two < quadsCount; ++two) {
+                for (int inner = 0; inner < 3; inner += 2) {
+                     REPORTER_ASSERT(reporter, point_on_parameterized_curve(*quads[one],
+                            (*quads[two])[inner]));
+                }
+                REPORTER_ASSERT(reporter, SkDQuadImplicit::Match(*quads[one], *quads[two]));
+            }
+        }
+    }
+}
index 53e33bca8f2d2d439847e42afcda57fcc204522b..2a4b0a00252ff97ae176f3128fb0658fc353001b 100644 (file)
@@ -86,7 +86,10 @@ static void dontFailOne(skiatest::Reporter* reporter, int index) {
     SkPath result;
     result.setFillType(SkPath::kWinding_FillType);
     bool success = Simplify(path, &result);
-    REPORTER_ASSERT(reporter, success);
+    // linux 32 debug fails test 13 because the quad is not treated as linear
+    // there's no error in the math that I can find -- it looks like a processor
+    // or compiler bug -- so for now, allow either to work
+    REPORTER_ASSERT(reporter, success || index == 13);
     REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
     reporter->bumpTestCount();
 }
index 6a7b42510b5bc31f011173ae09f8c1c593894532..88547a0f04b741ab1142ab22c477b75d22ba549c 100644 (file)
@@ -3649,7 +3649,7 @@ static void testTriangles2(skiatest::Reporter* reporter, const char* filename) {
     testSimplify(reporter, path, filename);
 }
 
-// A test for this case:
+// A test this for this case:
 // contourA has two segments that are coincident
 // contourB has two segments that are coincident in the same place
 // each ends up with +2/0 pairs for winding count
@@ -4506,6 +4506,8 @@ static void testQuads47(skiatest::Reporter* reporter, const char* filename) {
     testSimplify(reporter, path, filename);
 }
 
+// this fails because there is a short unorderable segment and the unordered state isn't handled
+// correctly later on.
 static void testQuads46x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -4677,9 +4679,7 @@ static void testRect3(skiatest::Reporter* reporter, const char* filename) {
     testSimplify(reporter, path, filename);
 }
 
-static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = testCubic2;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static TestDesc tests[] = {
     TEST(testRect3),
@@ -4738,7 +4738,7 @@ static TestDesc tests[] = {
     TEST(testQuadralateral3),
     TEST(testDegenerate5),
     TEST(testQuad12),
-    TEST(testQuadratic51),
+    TEST(testQuadratic51),  // has unorderable angles
     TEST(testQuad8),
     TEST(testQuad11),
     TEST(testQuad10),
@@ -5111,13 +5111,14 @@ static void (*firstSubTest)(skiatest::Reporter* , const char* filename) = 0;
 static bool runSubTests = false;
 static bool runSubTestsFirst = false;
 static bool runReverse = false;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSimplify, reporter) {
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
     }
 }
index 58a69ad1ea12a2de4d10f134e37a2f986cc88e83..6af790f72a5f531220caa69e1820813054c516b0 100755 (executable)
@@ -896,6 +896,19 @@ static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filen
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* this cubic/quad pair
+ c = 430,280 430,278.895416 473.876068,278 528,278
+ q = 430,280 430.009796,277.101196 458.703552,275.050262
+ only intersect at the shared point (430,280)
+ they sort backwards because the tangent from pt[0] to control pt[1]
+ c' = (0.00000000000000000, -1.1045837402343750)
+ q' = (0.0097961425781250000, -2.8988037109375000)
+ suggests that the quad is counterclockwise of the cubic, when the reverse is true
+ the angle code is fooled because the control pt[1] of both the quad and cubic
+ is far away from cubic cntl [2] and quad pt [2].
+ Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent
+ Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl?
+ */
 static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -928,6 +941,11 @@ static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename)
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* didn't investigate thoroughly, but looks to be missorting quad and cubic
+    {{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}}
+    {{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}}
+    decision maker is case 14 leftLessThanRight
+ */
 static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1061,7 +1079,7 @@ static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) {
     pathB.lineTo(149, 675);
     pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671);
     pathB.close();
-    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
 static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) {
@@ -1087,12 +1105,10 @@ static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* fi
     pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202);
     pathB.lineTo(50, 183);
     pathB.close();
-    // FIXME: this generates quads and cubics that are (correctly) not coincident unlike the old code
-    // however, somewhere the angles are sorted incorrectly and the winding is computed to be -1/-2
-    // but I can't find the error
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* cubic ends just above opp line */
 static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1112,6 +1128,7 @@ static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filenam
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// pair of lines are not quite coincident, so sorting line/cubic fails (i think)
 static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1144,6 +1161,9 @@ static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char*
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// pair of curves have nearly the same initial tangent but are sorting by
+// that alone sorts them incorrectly. Need to detect that tangents are nearly
+// identical and not reliable by themselves
 static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1170,6 +1190,7 @@ static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// nearly identical to lavoixdunord -- to not-quite-coincident lines
 static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1202,6 +1223,7 @@ static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* file
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// visually looks like lavoixdunord and www_booking_com
 static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1561,6 +1583,11 @@ static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* Three edges are partially coincident. Only one of the three knows about the other two.
+   Subsequently, when the angle loop is created, it misses one of the edges.
+   After coincident edges are processed, probably need a check-and-correct that makes sure the
+   coincidences are all self-consistent.
+ */
 static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1579,6 +1606,13 @@ static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename)
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* this generates a pair of lines that are essentially coincident; but the next line at a right
+   angle is not treated as if it intersects at the same point.
+   There are several of options:
+     move the intersection of the right angle line to the coincident point (should 'near' do this?
+     construct another coincident pair from the right angle line to the coincident point
+     treat the intersection as simple and not coincident
+ */
 static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1597,6 +1631,7 @@ static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filen
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// fails on angle insert -- haven't investigated yet
 static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1622,6 +1657,7 @@ static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* fil
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+/* asserts in alignSpanState looks like a coincident related bug */
 static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1658,6 +1694,7 @@ static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* file
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// fails on angle insert
 static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1684,6 +1721,7 @@ static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// fails in intersections insert
 static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1728,6 +1766,7 @@ static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char*
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// /SkOpContour.cpp:278: failed assertion "!approximately_negative(oEndT - oStartT)
 static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1748,6 +1787,7 @@ static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1785,6 +1825,7 @@ static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char*
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1803,6 +1844,7 @@ static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// debugValidateLoop loop sum fails
 static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1821,6 +1863,7 @@ static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// debugValidateLoop loop sum fails
 static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1839,6 +1882,7 @@ static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkIntersections::lineVertical fUsed >= fMax
 static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1860,6 +1904,7 @@ static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* file
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpContour::calcPartialCoincidentWinding SkASSERT(!approximately_negative(endT - startT));
 static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1900,6 +1945,7 @@ static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const ch
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// can't find winding of remaining vertical edges
 static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1947,6 +1993,7 @@ static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter,
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// asserts in bridgeOp simple->isClosed()
 static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2087,6 +2134,7 @@ static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* f
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// !simple->isClosed()
 static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2110,6 +2158,7 @@ static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, con
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// line quad intersection SkIntersections::assert
 static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2131,6 +2180,7 @@ static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2253,6 +2303,7 @@ static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* file
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// joinCoincidence / findT / assert
 static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2281,6 +2332,7 @@ static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter,
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// joinCoincidence / findT / assert
 static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2309,6 +2361,7 @@ static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* f
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// cubic-cubic intersection reduce checkLinear assert
 static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2607,6 +2660,7 @@ static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* fil
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2630,6 +2684,7 @@ static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// joinCoincidence / findT / assert
 static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2666,6 +2721,7 @@ static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filen
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// !simple->isClosed()
 static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2730,6 +2786,7 @@ static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, con
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// !simple->isClosed()
 static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2813,6 +2870,7 @@ static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+// hangs
 static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2831,6 +2889,7 @@ static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// hangs
 static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3554,7 +3613,12 @@ static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* fi
     pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f);
     pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    if (FLAGS_runFail) {
+        testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    } else {
+        // INVESTIGATE : why this normal test takes fail case (test has never worked)
+        testPathFailOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    }
 }
 
 static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
@@ -3643,6 +3707,7 @@ static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const ch
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// addSimpleAngle: failed assertion "index == count() - 2"
 static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3662,6 +3727,7 @@ static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* fi
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// addTCoincident oPeek = &other->fTs[++oPeekIndex];
 static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3715,7 +3781,11 @@ static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* f
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+// SkOpSegment.cpp:4398: failed assertion "!span->fDone"
 static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) {
+    if (/* 0 && */ !FLAGS_runFail) {  // has never worked MUST BE FIXED BEFORE NEXT CHECKIN
+        return;
+    }
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(78.6429825f, 3150.97632f);
@@ -3744,36 +3814,12 @@ static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filena
     pathB.lineTo(77.6666718f, 3153.3335f);
     pathB.cubicTo(77.6666718f, 3151.49268f, 79.15905f, 3150, 81, 3150);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-}
-
-static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) {
-    SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.moveTo(941, 1494);
-    path.lineTo(941, 1464);
-    path.lineTo(985, 1464);
-    path.lineTo(985, 1494);
-    path.lineTo(941, 1494);
-    path.close();
-    SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
-
-pathB.moveTo(984.546021f, 1478.31494f);
-pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f);
-pathB.cubicTo(984.419006f, 1478.63477f, 983.044373f, 1481.90405f, 980.026001f, 1481.276f);
-pathB.cubicTo(980.026001f, 1481.276f, 980.02594f, 1481.27576f, 980.025879f, 1481.27527f);
-pathB.cubicTo(980.018494f, 1481.22131f, 979.602478f, 1478.38831f, 984.546021f, 1478.31494f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-
+    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
 }
 
-static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
-    TEST(skpwww_educationalcraft_com_4a),
     TEST(skpwww_lptemp_com_3),
     TEST(skpwww_shinydemos_com_5),
     TEST(skpwww_lptemp_com_5),
@@ -3893,10 +3939,11 @@ static struct TestDesc tests[] = {
 static const size_t testCount = SK_ARRAY_COUNT(tests);
 
 static bool runReverse = false;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSkp, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
 }
index b4715e63e07f07087f6988877d5956e22c2e2eff..5f8915f68123778a857c7abea9e99286ab10f33d 100644 (file)
 
 #include "SkPathOpsTSect.h"
 
-template<typename TCurve>
-const SkTSpan<TCurve>* SkTSect<TCurve>::debugSpan(int id) const {
-    const SkTSpan<TCurve>* test = fHead;
-    do {
-        if (test->debugID() == id) {
-            return test;
-        }
-    } while ((test = test->next()));
-#ifndef SK_RELEASE
-    test = fOppSect->fHead;
-    do {
-        if (test->debugID() == id) {
-            return test;
-        }
-    } while ((test = test->next()));
-#endif
-    return NULL;
-}
-
-template<typename TCurve>
-const SkTSpan<TCurve>* SkTSect<TCurve>::debugT(double t) const {
-    const SkTSpan<TCurve>* test = fHead;
-    const SkTSpan<TCurve>* closest = NULL;
-    double bestDist = DBL_MAX;
-    do {
-        if (between(test->fStartT, t, test->fEndT)) {
-            return test;
-        }
-        double testDist = SkTMin(fabs(test->fStartT - t), fabs(test->fEndT - t));
-        if (bestDist > testDist) {
-            bestDist = testDist;
-            closest = test;
-        }
-    } while ((test = test->next()));
-    SkASSERT(closest);
-    return closest;
-}
-
 template<typename TCurve>
 void SkTSect<TCurve>::dump() const {
-    dumpCommon(fHead);
-}
-
-extern int gDumpTSectNum;
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpBoth(SkTSect* opp) const {
-#if DEBUG_T_SECT_DUMP <= 2
-#if DEBUG_T_SECT_DUMP == 2
-    SkDebugf("%d ", ++gDumpTSectNum);
-#endif
-    this->dump();
-    SkDebugf(" ");
-    opp->dump();
-    SkDebugf("\n");
-#elif DEBUG_T_SECT_DUMP == 3
-    SkDebugf("<div id=\"sect%d\">\n", ++gDumpTSectNum);
-    if (this->fHead) {
-        this->dumpCurves();
-    }
-    if (opp->fHead) {
-        PATH_OPS_DEBUG_CODE(opp->dumpCurves());
-    }
-    SkDebugf("</div>\n\n");
-#endif
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpBounds(int id) const {
-    const SkTSpan<TCurve>* bounded = debugSpan(id);
-    if (!bounded) {
-        SkDebugf("no span matches %d\n", id);
-        return;
-    }
-    const SkTSpan<TCurve>* test = bounded->debugOpp()->fHead;
-    do {
-        if (test->findOppSpan(bounded)) {
-            test->dump();
-        }
-    } while ((test = test->next()));
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpCoin() const {
-    dumpCommon(fCoincident);
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpCoinCurves() const {
-    dumpCommonCurves(fCoincident);
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpCommon(const SkTSpan<TCurve>* test) const {
     SkDebugf("id=%d", debugID());
+    const SkTSpan<TCurve>* test = fHead;
     if (!test) {
         SkDebugf(" (empty)");
         return;
     }
     do {
         SkDebugf(" ");
-        test->dump();
+        test->dump(this);
     } while ((test = test->next()));
 }
 
 template<typename TCurve>
-void SkTSect<TCurve>::dumpCommonCurves(const SkTSpan<TCurve>* test) const {
-    do {
-        test->fPart.dumpID(test->debugID());
-    } while ((test = test->next()));
+void SkTSect<TCurve>::dumpBoth(const SkTSect& opp) const {
+    dump();
+    SkDebugf(" ");
+    opp.dump();
+    SkDebugf("\n");
+}
+
+template<typename TCurve>
+void SkTSect<TCurve>::dumpBoth(const SkTSect* opp) const {
+    dumpBoth(*opp);
 }
 
 template<typename TCurve>
 void SkTSect<TCurve>::dumpCurves() const {
-    dumpCommonCurves(fHead);
+    const SkTSpan<TCurve>* test = fHead;
+    do {
+        test->fPart.dump();
+    } while ((test = test->next()));
 }
 
+#if !DEBUG_T_SECT
 template<typename TCurve>
-const SkTSpan<TCurve>* SkTSpan<TCurve>::debugSpan(int id) const {
-    return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugSpan(id), NULL);
+int SkTSpan<TCurve>::debugID(const SkTSect<TCurve>* sect) const {
+    if (!sect) {
+        return -1;
+    }
+    int id = 1;
+    const SkTSpan* test = sect->fHead;
+    while (test && test != this) {
+        ++id;
+        test = test->fNext;
+    }
+    return id;
 }
+#endif
 
 template<typename TCurve>
-const SkTSpan<TCurve>* SkTSpan<TCurve>::debugT(double t) const {
-    return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugT(t), NULL);
+void SkTSpan<TCurve>::dumpID(const SkTSect<TCurve>* sect) const {
+    if (fCoinStart.isCoincident()) {
+        SkDebugf("%c", '*');
+    }
+    SkDebugf("%d", debugID(sect));
+    if (fCoinEnd.isCoincident()) {
+        SkDebugf("%c", '*');
+    }
 }
 
 template<typename TCurve>
-void SkTSpan<TCurve>::dump() const {
-    dumpID();
+void SkTSpan<TCurve>::dump(const SkTSect<TCurve>* sect) const {
+    dumpID(sect);
     SkDebugf("=(%g,%g) [", fStartT, fEndT);
     for (int index = 0; index < fBounded.count(); ++index) {
         SkTSpan* span = fBounded[index];
-        span->dumpID();
+        span->dumpID(sect);
         if (index < fBounded.count() - 1) {
             SkDebugf(",");
         }
     }
     SkDebugf("]");
 }
-
-template<typename TCurve>
-void SkTSpan<TCurve>::dumpBounds(int id) const {
-    PATH_OPS_DEBUG_CODE(fDebugSect->dumpBounds(id));
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::dumpID() const {
-    if (fCoinStart.isCoincident()) {
-        SkDebugf("%c", '*');
-    }
-    SkDebugf("%d", debugID());
-    if (fCoinEnd.isCoincident()) {
-        SkDebugf("%c", '*');
-    }
-}
index f1cba8ed8b2c8d07e79bbdfe5325b240d026f6ab..60a12ee56ec87afcb606e05a6916283502b5e23b 100644 (file)
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
-#include "SkReduceOrder.h"
-#include "SkTSort.h"
-
-static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
-    const double adjust = sqrt(3.) / 36;
-    SkDCubic sub;
-    const SkDCubic* cPtr;
-    if (start == 0) {
-        cPtr = &cubic;
-    } else {
-        // OPTIMIZE: special-case half-split ?
-        sub = cubic.subDivide(start, 1);
-        cPtr = &sub;
-    }
-    const SkDCubic& c = *cPtr;
-    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
-    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
-    double dist = sqrt(dx * dx + dy * dy);
-    double tDiv3 = precision / (adjust * dist);
-    double t = SkDCubeRoot(tDiv3);
-    if (start > 0) {
-        t = start + (1 - start) * t;
-    }
-    return t;
-}
-
-static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
-    double tDiv = calc_t_div(cubic, precision, 0);
-    if (tDiv >= 1) {
-        return true;
-    }
-    if (tDiv >= 0.5) {
-        ts->push_back(0.5);
-        return true;
-    }
-    return false;
-}
-
-static void addTs(const SkDCubic& cubic, double precision, double start, double end,
-        SkTArray<double, true>* ts) {
-    double tDiv = calc_t_div(cubic, precision, 0);
-    double parts = ceil(1.0 / tDiv);
-    for (double index = 0; index < parts; ++index) {
-        double newT = start + (index / parts) * (end - start);
-        if (newT > 0 && newT < 1) {
-            ts->push_back(newT);
-        }
-    }
-}
-
-static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
-    SkReduceOrder reducer;
-    int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
-    if (order < 3) {
-        return;
-    }
-    double inflectT[5];
-    int inflections = cubic->findInflections(inflectT);
-    SkASSERT(inflections <= 2);
-    if (!cubic->endsAreExtremaInXOrY()) {
-        inflections += cubic->findMaxCurvature(&inflectT[inflections]);
-        SkASSERT(inflections <= 5);
-    }
-    SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
-    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
-    // own subroutine?
-    while (inflections && approximately_less_than_zero(inflectT[0])) {
-        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
-    }
-    int start = 0;
-    int next = 1;
-    while (next < inflections) {
-        if (!approximately_equal(inflectT[start], inflectT[next])) {
-            ++start;
-        ++next;
-            continue;
-        }
-        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
-    }
-
-    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
-        --inflections;
-    }
-    SkDCubicPair pair;
-    if (inflections == 1) {
-        pair = cubic->chopAt(inflectT[0]);
-        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
-        if (orderP1 < 2) {
-            --inflections;
-        } else {
-            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
-            if (orderP2 < 2) {
-                --inflections;
-            }
-        }
-    }
-    if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
-        return;
-    }
-    if (inflections == 1) {
-        pair = cubic->chopAt(inflectT[0]);
-        addTs(pair.first(), precision, 0, inflectT[0], ts);
-        addTs(pair.second(), precision, inflectT[0], 1, ts);
-        return;
-    }
-    if (inflections > 1) {
-        SkDCubic part = cubic->subDivide(0, inflectT[0]);
-        addTs(part, precision, 0, inflectT[0], ts);
-        int last = inflections - 1;
-        for (int idx = 0; idx < last; ++idx) {
-            part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
-            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
-        }
-        part = cubic->subDivide(inflectT[last], 1);
-        addTs(part, precision, inflectT[last], 1, ts);
-        return;
-    }
-    addTs(*cubic, precision, 0, 1, ts);
-}
+#include "SkPathOpsTriangle.h"
 
 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
     SkTArray<double, true> ts;
-    toQuadraticTs(&cubic, precision, &ts);
+    cubic.toQuadraticTs(precision, &ts);
     if (ts.count() <= 0) {
         SkDQuad quad = cubic.toQuad();
         quads.push_back(quad);
@@ -298,6 +180,15 @@ bool ValidQuad(const SkDQuad& quad) {
     return true;
 }
 
+bool ValidTriangle(const SkDTriangle& triangle) {
+    for (int index = 0; index < 3; ++index) {
+        if (!ValidPoint(triangle.fPts[index])) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool ValidVector(const SkDVector& v) {
     if (SkDoubleIsNaN(v.fX)) {
         return false;
index 7a72ff29a5d448288bb054dffa446ca2fc8b293b..0c42bfbb057c67e7628fe0ecddcecb96486c13ed 100644 (file)
@@ -21,6 +21,7 @@ bool ValidLine(const SkDLine& line);
 bool ValidPoint(const SkDPoint& pt);
 bool ValidPoints(const SkPoint* pts, int count);
 bool ValidQuad(const SkDQuad& quad);
+bool ValidTriangle(const SkDTriangle& triangle);
 bool ValidVector(const SkDVector& v);
 
 #endif
index bf634f9f7e72f404291f841ff4fcd93fe4dcc251..15d6e5492742ecfad34a0b5ed90a559e9281e3b2 100644 (file)
@@ -49,16 +49,14 @@ static void testSetTest(skiatest::Reporter* reporter, int index) {
             const Curve& iTest = testSet.tests[inner];
             SkIntersections* i = combos.append();
             sk_bzero(i, sizeof(SkIntersections));
-            SkDLine oLine = {{ oTest.curve[0], oTest.curve[1] }};
-            SkDLine iLine = {{ iTest.curve[0], iTest.curve[1] }};
             if (oTest.ptCount == 1 && iTest.ptCount == 1) {
-                i->intersect(oLine, iLine);
+                i->intersect(*(const SkDLine*) &oTest.curve, *(const SkDLine*) &iTest.curve);
             } else if (oTest.ptCount == 1 && iTest.ptCount == 4) {
-                i->intersect(iTest.curve, oLine);
+                i->intersect(iTest.curve, *(const SkDLine*) &oTest.curve);
             } else if (oTest.ptCount == 4 && iTest.ptCount == 1) {
-                i->intersect(oTest.curve, iLine);
+                i->intersect(oTest.curve, *(const SkDLine*) &oTest.curve);
             } else if (oTest.ptCount == 4 && iTest.ptCount == 4) {
-                i->intersect(oTest.curve, iTest.curve);
+                i->intersectB(oTest.curve, iTest.curve);
             } else {
                 SkASSERT(0);
             }
index d50c26a5477c5a303ed28f3c5caa2914cc03773e..cea37520b1cfe06c15635b8378d7134f3578cc14 100644 (file)
@@ -8,6 +8,7 @@
 #include "PathOpsThreadedCommon.h"
 #include "SkCanvas.h"
 #include "SkRandom.h"
+#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
index 1ed3a2cfcee811c8a0c6221b26a1187079d785c1..a77abd6165da44cdf2a6a3dc9ee0e56a2e1ecb8d 100644 (file)
     <meta charset="utf-8" />
     <title></title>
 <div style="height:0">
-<div id="sect1">
-{{{1.80943513, 3.0778243500000002}, {1.66686702, 2.1680693600000001}, {1.68301272, 0}, {3, 0}}} id=1
-{{{0, 1}, {0, 2}, {0.75, 2.25}, {1.75, 2.25}}} id=2
-{{{1.75, 2.25}, {2.75, 2.25}, {4, 2}, {5, 2}}} id=4
+
+<div id="quad1">
+{{3.13,2.74}, {1.08,4.62}, {3.71,0.94}} 
+{{3.13,2.74}, {7.99,2.75}, {8.27,1.96}} 
 </div>
 
-<div id="sect2">
-{{{1.80943513, 3.0778243500000002}, {1.66686702, 2.1680693600000001}, {1.68301272, 0}, {3, 0}}} id=1
-{{{0, 1}, {0, 2}, {0.75, 2.25}, {1.75, 2.25}}} id=2
-{{{1.75, 2.25}, {2.2500000000000018, 2.25}, {2.8124999999999991, 2.1875}, {3.375, 2.125}}} id=4
+<div id="quad2">
+{{4.838888984361574,4.399276078363981}, {5.947577332875065,2.02910379790342}, {3.8092258119951885,2.108659563498883}}
+{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {3.3638348513490293,1.4969462106891218}}
 </div>
 
-<div id="sect3">
-{{{1.80943513, 3.0778243500000002}, {1.738151075, 2.6229468550000004}, {1.7065454725, 1.8534907675000001}, {1.85738429375, 1.19775405375}}} id=1
-{{{0, 1}, {0, 2}, {0.75, 2.25}, {1.75, 2.25}}} id=2
-{{{1.75, 2.25}, {2.2500000000000018, 2.25}, {2.8124999999999991, 2.1875}, {3.375, 2.125}}} id=4
+<div id="quad3">
+{{4.838888984361574,4.399276078363981}, {5.962263714769107,1.654601059605365}, {3.8789861259918847,2.8650082310420126}}
+{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {3.3638348513490293,1.4969462106891218}}
 </div>
 
-<div id="sect4">
-{{{1.80943513, 3.0778243500000002}, {1.7737931025, 2.8503856025000003}, {1.7480706881249999, 2.5443022068750003}, {1.7501136332812499, 2.2131114089062502}}} id=1
-{{{0, 1}, {0, 2}, {0.75, 2.25}, {1.75, 2.25}}} id=2
-{{{1.75, 2.25}, {2.2500000000000018, 2.25}, {2.8124999999999991, 2.1875}, {3.375, 2.125}}} id=4
+<div id="quad4">
+{{4.838888984361574,4.399276078363981}, {5.77868394109359,1.852867215174923}, {3.915702080726988,2.1820914729690903}}
+{{4.838888984361574,4.399276078363981}, {6.681232491841801,2.5287975370876032}, {3.3638348513490293,1.4969462106891218}}
 </div>
 
-<div id="sect5">
-{{{1.80943513, 3.0778243500000002}, {1.7737931025, 2.8503856025000003}, {1.7480706881249999, 2.5443022068750003}, {1.7501136332812499, 2.2131114089062502}}} id=1
-{{{0.5, 2}, {0.81249999999999956, 2.1874999999999987}, {1.2500000000000009, 2.2500000000000013}, {1.75, 2.25}}} id=8
-{{{1.75, 2.25}, {2.2500000000000018, 2.25}, {2.8124999999999991, 2.1875}, {3.375, 2.125}}} id=4
+<div id="quad5">
+{{4.838888984361574,4.399276078363981}, {6.082937568878361,1.9951156645288415}, {3.915702080726988,2.1820914729690903}}
+{{4.838888984361574,4.399276078363981}, {6.681232491841801,2.5287975370876032}, {3.3638348513490293,1.4969462106891218}}
 </div>
 
-<div id="sect6">
-{{{1.80943513, 3.0778243500000002}, {1.7737931025, 2.8503856025000003}, {1.7480706881249999, 2.5443022068750003}, {1.7501136332812499, 2.2131114089062502}}} id=1
-{{{0.5, 2}, {0.81249999999999956, 2.1874999999999987}, {1.2500000000000009, 2.2500000000000013}, {1.75, 2.25}}} id=8
-{{{1.75, 2.25}, {1.9999999999999976, 2.25}, {2.2656250000000022, 2.234375}, {2.5390625, 2.2109375}}} id=4
+<div id="quad6">
+{{4.898159171592373,4.367665311840888}, {6.695396170263287,1.769888953051804}, {3.6312051820191513,2.727377195492444}}
+{{4.898159171592373,4.367665311840888}, {6.961778044734251,2.4813813873029633}, {3.3638348513490293,1.4969462106891218}}
 </div>
 
-<div id="sect7">
-{{{1.80943513, 3.0778243500000002}, {1.7737931025, 2.8503856025000003}, {1.7480706881249999, 2.5443022068750003}, {1.7501136332812499, 2.2131114089062502}}} id=1
-{{{1.0546875, 2.1953125}, {1.2656250000000009, 2.2343750000000009}, {1.4999999999999996, 2.2499999999999996}, {1.75, 2.25}}} id=12
-{{{1.75, 2.25}, {1.9999999999999976, 2.25}, {2.2656250000000022, 2.234375}, {2.5390625, 2.2109375}}} id=4
+<div id="quad7">
+{{4.838888984361574,4.399276078363981}, {3.012741870322956,2.449520433298304}, {5.140619283496844,2.110967248292131}}
+{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
 </div>
 
-<div id="sect8">
-{{{1.7656425168945311, 2.6843748983789064}, {1.7550120280078125, 2.5380253562890633}, {1.7490921607031253, 2.3787068078906248}, {1.7501136332812499, 2.2131114089062502}}} id=7
-{{{1.0546875, 2.1953125}, {1.2656250000000009, 2.2343750000000009}, {1.4999999999999996, 2.2499999999999996}, {1.75, 2.25}}} id=12
-{{{1.75, 2.25}, {1.9999999999999976, 2.25}, {2.2656250000000022, 2.234375}, {2.5390625, 2.2109375}}} id=4
+<div id="quad8">
+{{4.838888984361574,4.399276078363981}, {3.1707957029384213,2.607574265913769}, {4.626944327496585,2.2848264641691425}}
+{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
 </div>
 
-<div id="sect9">
-{{{1.7656425168945311, 2.6843748983789064}, {1.7550120280078125, 2.5380253562890633}, {1.7490921607031253, 2.3787068078906248}, {1.7501136332812499, 2.2131114089062502}}} id=7
-{{{1.0546875, 2.1953125}, {1.2656250000000009, 2.2343750000000009}, {1.4999999999999996, 2.2499999999999996}, {1.75, 2.25}}} id=12
-{{{1.75, 2.25}, {1.8750000000000016, 2.2499999999999991}, {2.0039062499999982, 2.2460937500000004}, {2.1357421875, 2.2392578125}}} id=4
+<div id="quad9">
+{{4.838888984361574,4.399276078363981}, {3.463749932092156,2.935940544745428}, {5.161344349908893,2.4940794849932386}}
+{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
 </div>
 
-<div id="sect10">
-{{{1.7656425168945311, 2.6843748983789064}, {1.7550120280078125, 2.5380253562890633}, {1.7490921607031253, 2.3787068078906248}, {1.7501136332812499, 2.2131114089062502}}} id=7
-{{{1.3876953125, 2.2373046875}, {1.50390625, 2.24609375}, {1.625, 2.25}, {1.75, 2.25}}} id=16
-{{{1.75, 2.25}, {1.8750000000000016, 2.2499999999999991}, {2.0039062499999982, 2.2460937500000004}, {2.1357421875, 2.2392578125}}} id=4
+<div id="quad10">
+{{4.838888984361574,4.399276078363981}, {5.82508561259808,2.495362604119041}, {3.4377993053488463,2.7132154732530362}}
+{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {2.435268584733173,1.817005221735438}}
 </div>
 
-<div id="sect11">
-{{{1.7535085895385742, 2.4559603499780276}, {1.7508274956738286, 2.3771375952441409}, {1.7496028969921869, 2.2959091083984369}, {1.7501136332812499, 2.2131114089062502}}} id=9
-{{{1.3876953125, 2.2373046875}, {1.50390625, 2.24609375}, {1.625, 2.25}, {1.75, 2.25}}} id=16
-{{{1.75, 2.25}, {1.8750000000000016, 2.2499999999999991}, {2.0039062499999982, 2.2460937500000004}, {2.1357421875, 2.2392578125}}} id=4
+<div id="cubic1">
+{{0,0}, {1,0}, {0,1}, {1,1}}
+{{0,0}, {2,0}, {0,2}, {2,2}}
 </div>
 
-<div id="sect12">
-{{{1.7535085895385742, 2.4559603499780276}, {1.7508274956738286, 2.3771375952441409}, {1.7496028969921869, 2.2959091083984369}, {1.7501136332812499, 2.2131114089062502}}} id=9
-{{{1.3876953125, 2.2373046875}, {1.50390625, 2.24609375}, {1.625, 2.25}, {1.75, 2.25}}} id=16
-{{{1.75, 2.25}, {1.8124999999999989, 2.2499999999999996}, {1.8759765625000011, 2.2490234374999996}, {1.9403076171875, 2.2471923828125}}} id=4
+<div id="cubic2" >
+{{0.4655213647959181,1.5608657525510201}, {0.6599868463010203,0.4290098852040817}, {2.473652742346939,1.2464524872448977}, {1.8511738679846936,0.5344786352040818}}
+{{0.4655213647959181,1.5608657525510201}, {0.3250358737244896,0.819226323341837}, {1.4399214764030612,0.3318817761479596}, {1.2703414571528546,0.9081465322144181}}
+</div> 
+
+<div id="quad11">
+{{-378.22698974609375, -987.8935546875}, {-47.53326416015625, 482.7139892578125}, {-626.4708251953125, -338.62969970703125}}
+{{-378.22698974609375, -987.8935546875}, {-847.94854736328125, -861.42230224609375}, {-390.9146728515625, 402.08740234375}}
 </div>
 
-<div id="sect13">
-{{{1.7535085895385742, 2.4559603499780276}, {1.7508274956738286, 2.3771375952441409}, {1.7496028969921869, 2.2959091083984369}, {1.7501136332812499, 2.2131114089062502}}} id=9
-{{{1.5655517578125, 2.2469482421875}, {1.6259765625, 2.2490234375}, {1.6875, 2.25}, {1.75, 2.25}}} id=20
-{{{1.75, 2.25}, {1.8124999999999989, 2.2499999999999996}, {1.8759765625000011, 2.2490234374999996}, {1.9403076171875, 2.2471923828125}}} id=4
+<div id="quad12">
+{{-173.3448486328125, -962.89422607421875}, {-778.321533203125, -161.47637939453125}, {-196.77374267578125, -736.40155029296875}}
+{{-173.3448486328125, -962.89422607421875}, {652.3017578125, -400.67816162109375}, {-386.7855224609375, 361.1614990234375}}
 </div>
 
-<div id="sect14">
-{{{1.7506141751022339, 2.3360264837265015}, {1.7500367307348632, 2.2955168052368169}, {1.7498582651367187, 2.2545102586523438}, {1.7501136332812499, 2.2131114089062502}}} id=11
-{{{1.5655517578125, 2.2469482421875}, {1.6259765625, 2.2490234375}, {1.6875, 2.25}, {1.75, 2.25}}} id=20
-{{{1.75, 2.25}, {1.8124999999999989, 2.2499999999999996}, {1.8759765625000011, 2.2490234374999996}, {1.9403076171875, 2.2471923828125}}} id=4
+<div id="quad13">
+{{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
+{{{-968.181396484375, 544.0128173828125}, {593.677001953125, 865.5810546875}, {-66.57171630859375, -847.849853515625}}}
 </div>
 
-<div id="sect15">
-{{{1.7506141751022339, 2.3360264837265015}, {1.7500367307348632, 2.2955168052368169}, {1.7498582651367187, 2.2545102586523438}, {1.7501136332812499, 2.2131114089062502}}} id=11
-{{{1.5655517578125, 2.2469482421875}, {1.6259765625, 2.2490234375}, {1.6875, 2.25}, {1.75, 2.25}}} id=20
-{{{1.75, 2.25}, {1.7812500000000011, 2.2500000000000004}, {1.8127441406249989, 2.2497558593749991}, {1.8444671630859375, 2.2492828369140625}}} id=4
+<div id="quad14">
+{{{769.693115234375, -626.35089111328125}, {6.60491943359375, -210.43756103515625}, {-898.26654052734375, -17.76312255859375}}}
+{{{769.693115234375, -626.35089111328125}, {192.8486328125, 609.8062744140625}, {888.317626953125, -551.27215576171875}}}
 </div>
 
-<div id="sect16">
-{{{1.7506141751022339, 2.3360264837265015}, {1.7500367307348632, 2.2955168052368169}, {1.7498582651367187, 2.2545102586523438}, {1.7501136332812499, 2.2131114089062502}}} id=11
-{{{1.6569976806640625, 2.2492523193359375}, {1.687744140625, 2.249755859375}, {1.71875, 2.25}, {1.75, 2.25}}} id=24
-{{{1.75, 2.25}, {1.7812500000000011, 2.2500000000000004}, {1.8127441406249989, 2.2497558593749991}, {1.8444671630859375, 2.2492828369140625}}} id=4
+<div id="quad15">
+{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
+{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
 </div>
 
-<div id="sect17">
-{{{1.7500515994997787, 2.274902385537529}, {1.7499667235723877, 2.2544121828619379}, {1.7499859492089846, 2.2338108337792986}, {1.7501136332812499, 2.2131114089062502}}} id=13
-{{{1.6569976806640625, 2.2492523193359375}, {1.687744140625, 2.249755859375}, {1.71875, 2.25}, {1.75, 2.25}}} id=24
-{{{1.75, 2.25}, {1.7812500000000011, 2.2500000000000004}, {1.8127441406249989, 2.2497558593749991}, {1.8444671630859375, 2.2492828369140625}}} id=4
+<div id="quad16">
+{{{-353.9388427734375, 76.8973388671875}, {-36.00189208984375, 282.289306640625}, {-531.37969970703125, 683.95751953125}}}
+{{{-353.9388427734375, 76.8973388671875}, {-779.3529052734375, 509.6165771484375}, {-662.34088134765625, 124.4027099609375}}}
 </div>
 
-<div id="sect18">
-{{{1.7500515994997787, 2.274902385537529}, {1.7499667235723877, 2.2544121828619379}, {1.7499859492089846, 2.2338108337792986}, {1.7501136332812499, 2.2131114089062502}}} id=13
-{{{1.6569976806640625, 2.2492523193359375}, {1.687744140625, 2.249755859375}, {1.71875, 2.25}, {1.75, 2.25}}} id=24
-{{{1.75, 2.25}, {1.7656249999999989, 2.25}, {1.7813110351562511, 2.24993896484375}, {1.7970561981201172, 2.2498188018798828}}} id=4
+<div id="quad17">
+{{{-657.0289306640625, 681.611083984375}, {-991.8365478515625, 964.4644775390625}, {-843.3585205078125, 904.47998046875}}}
+{{{-657.0289306640625, 681.611083984375}, {-763.1571044921875, 39.1097412109375}, {618.2041015625, 840.6429443359375}}}
 </div>
 
-<div id="sect19">
-{{{1.7500515994997787, 2.274902385537529}, {1.7499667235723877, 2.2544121828619379}, {1.7499859492089846, 2.2338108337792986}, {1.7501136332812499, 2.2131114089062502}}} id=13
-{{{1.7033100128173828, 2.2498149871826172}, {1.7188110351562504, 2.2499389648437504}, {1.7343749999999991, 2.2499999999999991}, {1.75, 2.25}}} id=28
-{{{1.75, 2.25}, {1.7656249999999989, 2.25}, {1.7813110351562511, 2.24993896484375}, {1.7970561981201172, 2.2498188018798828}}} id=4
+<div id="quad18">
+{{{-609.406005859375, -684.37506103515625}, {766.4923095703125, 583.657958984375}, {-912.6832275390625, -949.553466796875}}}
+{{{-609.406005859375, -684.37506103515625}, {774.140380859375, 82.2415771484375}, {540.9007568359375, -136.982666015625}}}
 </div>
 
-<div id="sect20">
-{{{1.7500515994997787, 2.274902385537529}, {1.7500091615360831, 2.2646572841997337}, {1.7499927489633849, 2.2543843962601757}, {1.7500029063906433, 2.2440853555459359}}} id=13
-{{{1.7033100128173828, 2.2498149871826172}, {1.7188110351562504, 2.2499389648437504}, {1.7343749999999991, 2.2499999999999991}, {1.75, 2.25}}} id=28
-{{{1.75, 2.25}, {1.7656249999999989, 2.25}, {1.7813110351562511, 2.24993896484375}, {1.7970561981201172, 2.2498188018798828}}} id=4
+<div id="quad19">
+{{{-657.0289306640625, 681.611083984375}, {-991.8365478515625, 964.4644775390625}, {-843.3585205078125, 904.47998046875}}}
+{{{-657.0289306640625, 681.611083984375}, {-763.1571044921875, 39.1097412109375}, {618.2041015625, 840.6429443359375}}}
 </div>
 
-<div id="sect21">
-{{{1.7500515994997787, 2.274902385537529}, {1.7500091615360831, 2.2646572841997337}, {1.7499927489633849, 2.2543843962601757}, {1.7500029063906433, 2.2440853555459359}}} id=13
-{{{1.7033100128173828, 2.2498149871826172}, {1.7188110351562504, 2.2499389648437504}, {1.7343749999999991, 2.2499999999999991}, {1.75, 2.25}}} id=28
-{{{1.75, 2.25}, {1.7578125000000004, 2.25}, {1.7656402587890616, 2.2499847412109375}, {1.7734830379486084, 2.2499544620513916}}} id=4
+<div id="quad20">
+{{{123.2955322265625, -577.799560546875}, {-491.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
+{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
 </div>
 
-<div id="sect22">
-{{{1.7500515994997787, 2.274902385537529}, {1.7500091615360831, 2.2646572841997337}, {1.7499927489633849, 2.2543843962601757}, {1.7500029063906433, 2.2440853555459359}}} id=13
-{{{1.7266085147857666, 2.2499539852142334}, {1.7343902587890614, 2.2499847412109362}, {1.7421875000000011, 2.2500000000000013}, {1.75, 2.25}}} id=32
-{{{1.75, 2.25}, {1.7578125000000004, 2.25}, {1.7656402587890616, 2.2499847412109375}, {1.7734830379486084, 2.2499544620513916}}} id=4
+<div id="quad21">
+{{{123.2955322265625, -577.799560546875}, {-481.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
+{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
 </div>
 
-<div id="sect23">
-{{{1.7500075296736033, 2.2595140978078994}, {1.749999391463374, 2.2543778580665053}, {1.7499978276770138, 2.2492348759030554}, {1.7500029063906433, 2.2440853555459359}}} id=17
-{{{1.7266085147857666, 2.2499539852142334}, {1.7343902587890614, 2.2499847412109362}, {1.7421875000000011, 2.2500000000000013}, {1.75, 2.25}}} id=32
-{{{1.75, 2.25}, {1.7578125000000004, 2.25}, {1.7656402587890616, 2.2499847412109375}, {1.7734830379486084, 2.2499544620513916}}} id=4
+<div id="quad22">
+{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
+{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
 </div>
 
-<div id="sect24">
-{{{1.7500075296736033, 2.2595140978078994}, {1.749999391463374, 2.2543778580665053}, {1.7499978276770138, 2.2492348759030554}, {1.7500029063906433, 2.2440853555459359}}} id=17
-{{{1.7266085147857666, 2.2499539852142334}, {1.7343902587890614, 2.2499847412109362}, {1.7421875000000011, 2.2500000000000013}, {1.75, 2.25}}} id=32
-{{{1.75, 2.25}, {1.7539062499999991, 2.25}, {1.7578163146972661, 2.2499961853027344}, {1.7617301642894745, 2.2499885857105255}}} id=4
+<div id="quad23">
+{{{-341.26922607421875, 964.1964111328125}, {883.2567138671875, 812.7301025390625}, {286.0372314453125, 94.979248046875}}}
+{{{-341.26922607421875, 964.1964111328125}, {-158.90765380859375, 597.1875}, {-282.2255859375, 262.430908203125}}}
 </div>
 
-<div id="sect25">
-{{{1.7500075296736033, 2.2595140978078994}, {1.749999391463374, 2.2543778580665053}, {1.7499978276770138, 2.2492348759030554}, {1.7500029063906433, 2.2440853555459359}}} id=17
-{{{1.7382927238941193, 2.2499885261058807}, {1.7421913146972665, 2.2499961853027353}, {1.7460937499999996, 2.2499999999999996}, {1.75, 2.25}}} id=36
-{{{1.75, 2.25}, {1.7539062499999991, 2.25}, {1.7578163146972661, 2.2499961853027344}, {1.7617301642894745, 2.2499885857105255}}} id=4
+<div id="quad24">
+{{{123.2955322265625, -577.799560546875}, {-481.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
+{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
+{{{417.3499131065152, -577.799560546875}, {417.3499131065152, -699.60087482901156}, {331.22337542585541, -785.72740374616797}}}
 </div>
 
-<div id="sect26">
-{{{1.7500002616856762, 2.2518047069078144}, {1.7499994883020116, 2.2492332413546396}, {1.7500003670338282, 2.2466601157244943}, {1.7500029063906433, 2.2440853555459359}}} id=19
-{{{1.75, 2.25}, {1.7539062499999991, 2.25}, {1.7578163146972661, 2.2499961853027344}, {1.7617301642894745, 2.2499885857105255}}} id=4
+<div id="quad25">
+{{{922.6107177734375, 291.412109375}, {-939.361572265625, 589.8492431640625}, {-515.70941162109375, 120.2764892578125}}}
+{{{922.6107177734375, 291.412109375}, {148.5115966796875, -751.42095947265625}, {-347.47503662109375, 331.1798095703125}}}
+{{{922.6107177734375, -143.9114969433939}, {742.29377357777753, -143.9114969433939}, {614.79044900323777, -16.408159395199732}}}
+{{{487.2871114550436, 291.412109375}, {487.2871114550436, 471.72905357065997}, {614.79044900323777, 599.23237814519973}}}
 </div>
 
-<div id="sect27">
-{{{1.7500002616856762, 2.2518047069078144}, {1.7499994883020116, 2.2492332413546396}, {1.7500003670338282, 2.2466601157244943}, {1.7500029063906433, 2.2440853555459359}}} id=19
-{{{1.75, 2.25}, {1.7519531250000011, 2.2499999999999991}, {1.7539072036743153, 2.249999046325684}, {1.7558622322976589, 2.2499971427023411}}} id=4
+<div id="quad26">
+{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
+{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
+{{{33.221887415632978, -343.557373046875}, {33.221887415632978, -279.69039894717827}, {78.38265915086852, -234.52963180711851}}}
 </div>
 
-<div id="sect28">
-{{{1.7500002616856762, 2.2518047069078144}, {1.749999874993843, 2.2505189741312273}, {1.7499999013308822, 2.2492328263353962}, {1.7500003417604797, 2.2479462667113941}}} id=19
-{{{1.75, 2.25}, {1.7519531250000011, 2.2499999999999991}, {1.7539072036743153, 2.249999046325684}, {1.7558622322976589, 2.2499971427023411}}} id=4
+<div id="quad27">
+{{{-173.3448486328125, -962.89422607421875}, {-778.321533203125, -161.47637939453125}, {-196.77374267578125, -736.40155029296875}}}
+{{{-173.3448486328125, -962.89422607421875}, {652.3017578125, -400.67816162109375}, {-386.7855224609375, 361.1614990234375}}}
+{{{-270.84959533883426, -865.38947936819704}, {-230.46180860703427, -825.00168852687921}, {-173.3448486328125, -825.00168852687921}}}
+{{{-75.840101926790737, -865.38947936819704}, {-35.4523110854729, -905.77726609999695}, {-35.4523110854729, -962.89422607421875}}}
 </div>
 
-<div id="sect29">
-{{{1.7500002616856762, 2.2518047069078144}, {1.749999874993843, 2.2505189741312273}, {1.7499999013308822, 2.2492328263353962}, {1.7500003417604797, 2.2479462667113941}}} id=19
-{{{1.75, 2.25}, {1.7509765624999989, 2.2499999999999996}, {1.7519533634185802, 2.2499997615814205}, {1.752930402290076, 2.249999285209924}}} id=4
+<div id="quad28">
+{{{344.2755126953125, -689.900390625}, {743.6728515625, 512.8448486328125}, {928.598388671875, 111.946044921875}}}
+{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
+{{{344.2755126953125, -689.900390625}, {850.8173828125, 798.4874267578125}}}
+{{{344.2755126953125, -689.900390625}, {391.39917554828793, -551.43545842779145}}}
 </div>
 
-<div id="sect30">
-{{{1.7500002616856762, 2.2518047069078144}, {1.7500000683397601, 2.2511618405195208}, {1.7499999782510609, 2.2505188703764163}, {1.7499999915525417, 2.2498757968773848}}} id=19
-{{{1.75, 2.25}, {1.7509765624999989, 2.2499999999999996}, {1.7519533634185802, 2.2499997615814205}, {1.752930402290076, 2.249999285209924}}} id=4
+<div id="quad29">
+{{{351.8946533203125, 512.8131103515625}, {-294.22332763671875, 183.2200927734375}, {624.4842529296875, 862.0753173828125}}}
+{{{351.8946533203125, 512.8131103515625}, {489.1907958984375, -543.4212646484375}, {-432.7445068359375, 812.5205078125}}}
 </div>
 
-<div id="sect31">
-{{{1.7500002616856762, 2.2518047069078144}, {1.7500000683397601, 2.2511618405195208}, {1.7499999782510609, 2.2505188703764163}, {1.7499999915525417, 2.2498757968773848}}} id=19
-{{{1.75, 2.25}, {1.7504882812500011, 2.2500000000000004}, {1.7509766221046437, 2.2499999403953543}, {1.7514650225057267, 2.2499998212442733}}} id=4
+<div id="quad30">
+{{{627.6910400390625, 81.144287109375}, {168.9248046875, -211.72735595703125}, {-61.57086181640625, 915.171875}}}
+{{{627.6910400390625, 81.144287109375}, {918.159423828125, -325.468994140625}, {359.0523681640625, 817.4888916015625}}}
+{{{235.78221371860315, 81.144287109375}, {235.78221371860315, 243.47824037936314}, {350.56965608373537, 358.26567106470213}}},
 </div>
 
-<div id="sect32">
-{{{1.7500000491263352, 2.2508403295591259}, {1.7500000040986061, 2.2505188445374351}, {1.7499999849018013, 2.2501973336269003}, {1.7499999915525417, 2.2498757968773848}}} id=25
-{{{1.75, 2.25}, {1.7504882812500011, 2.2500000000000004}, {1.7509766221046437, 2.2499999403953543}, {1.7514650225057267, 2.2499998212442733}}} id=4
+<div id="quad31">
+{{{178.1549072265625, 62.724609375}, {541.3643798828125, 223.823486328125}, {-446.77471923828125, -15.990478515625}}}
+{{{178.1549072265625, 62.724609375}, {-347.14031982421875, -834.27191162109375}, {-495.13888549804687, 96.476806640625}}}
 </div>
 
-<div id="sect33">
-{{{1.7500000491263352, 2.2508403295591259}, {1.7500000040986061, 2.2505188445374351}, {1.7499999849018013, 2.2501973336269003}, {1.7499999915525417, 2.2498757968773848}}} id=25
-{{{1.75, 2.25}, {1.7502441406249989, 2.25}, {1.7504882961511623, 2.2499999850988388}, {1.7507324665712076, 2.2499999553037924}}} id=4
+<div id="quad32">
+{{{-809.41009521484375, 370.4566650390625}, {622.44677734375, -166.97119140625}, {-285.6748046875, 333.81005859375}}},
+{{{-809.41009521484375, 370.4566650390625}, {-110.36346435546875, -656.96044921875}, {906.4796142578125, 530.2061767578125}}}
 </div>
 
-<div id="sect34">
-{{{1.7500000009600125, 2.2503580826161893}, {1.7499999913636877, 2.2501973271671556}, {1.7499999882271713, 2.2500365652521426}, {1.7499999915525417, 2.2498757968773848}}} id=27
-{{{1.75, 2.25}, {1.7502441406249989, 2.25}, {1.7504882961511623, 2.2499999850988388}, {1.7507324665712076, 2.2499999553037924}}} id=4
+<div id="quad33">
+{{{-918.58624267578125, 653.6695556640625}, {-639.37548828125, 61.493896484375}, {-198.9605712890625, 243.704345703125}}},
+{{{-918.58624267578125, 653.6695556640625}, {-302.093505859375, -107.10955810546875}, {696.4962158203125, 600.738525390625}}}
 </div>
 
-<div id="sect35">
-{{{1.7500000009600125, 2.2503580826161893}, {1.7499999913636877, 2.2501973271671556}, {1.7499999882271713, 2.2500365652521426}, {1.7499999915525417, 2.2498757968773848}}} id=27
-{{{1.75, 2.25}, {1.7501220703125004, 2.25}, {1.7502441443502894, 2.2499999962747097}, {1.7503662221124614, 2.2499999888250386}}} id=4
+<div id="quad34">
+{{{-610.4193115234375, 861.173095703125}, {403.3203125, 215.3988037109375}, {-373.5546875, 179.88134765625}}},
+{{{-610.4193115234375, 861.173095703125}, {-757.244140625, -222.137451171875}, {705.892822265625, 87.4090576171875}}}
 </div>
 
-<div id="sect36">
+<div id="quad35">
+{{{282.5767822265625, -529.4022216796875}, {392.0968017578125, 768.1014404296875}, {712.11572265625, 189.19677734375}}},
+{{{282.5767822265625, -529.4022216796875}, {699.360595703125, 465.6171875}, {438.5755615234375, 125.5230712890625}}}
 </div>
 
+<div id="quad36">
+{{{-170.1510009765625, -184.905517578125}, {654.734130859375, 120.339599609375}, {-470.98443603515625, -69.4737548828125}}},
+{{{-170.1510009765625, -184.905517578125}, {-500.9822998046875, -148.40911865234375}, {-446.35821533203125, -840.5694580078125}}}
 </div>
 
-<script type="text/javascript">
+<div id="quad37">
+{{{-119.55023193359375, -39.2008056640625}, {-618.14306640625, -620.1419677734375}, {-779.53790283203125, -681.9923095703125}}},
+{{{-119.55023193359375, -39.2008056640625}, {365.968994140625, 55.4974365234375}, {98.1297607421875, -192.474609375}}}
+</div>
 
-var testDivs = [
-sect1,
-sect2,
-sect3,
-sect4,
-sect5,
-sect6,
-sect7,
-sect8,
-sect9,
-sect10,
-sect11,
-sect12,
-sect13,
-sect14,
-sect15,
-sect16,
-sect17,
-sect18,
-sect19,
-sect20,
-sect21,
-sect22,
-sect23,
-sect24,
-sect25,
-sect26,
-sect27,
-sect28,
-sect29,
-sect30,
-sect31,
-sect32,
-sect33,
-sect34,
-sect35,
-sect36,
+<div id="quad38">
+{{{607.9136962890625, 484.1448974609375}, {280.619140625, 982.736572265625}, {-577.5596923828125, 798.9134521484375}}},
+{{{607.9136962890625, 484.1448974609375}, {374.318115234375, -590.5146484375}, {-258.30438232421875, 592.958984375}}}
+</div>
 
+<div id="quad39">
+{{{-491.48846435546875, -470.9105224609375}, {109.7149658203125, -989.5384521484375}, {-275.900390625, 657.1920166015625}}},
+{{{-491.48846435546875, -470.9105224609375}, {-796.935791015625, 191.326171875}, {-852.120849609375, 62.06005859375}}}
+</div>
 
-    ];
+<div id="quad40">
+{{{-872.76458740234375, -163.30078125}, {723.6697998046875, 177.8204345703125}, {206.470703125, 147.9564208984375}}},
+{{{-872.76458740234375, -163.30078125}, {556.937744140625, 715.4345703125}, {627.348388671875, 77.0643310546875}}}
+</div>
 
-    var decimal_places = 3;
+<div id="quad108">
+{{{282.5767822265625, -529.4022216796875}, {392.0968017578125, 768.1014404296875}, {712.11572265625, 189.19677734375}}},
+{{{282.5767822265625, -529.4022216796875}, {699.360595703125, 465.6171875}, {438.5755615234375, 125.5230712890625}}}
+</div>
+
+<div id="quad159">
+{{{-868.3076171875, -212.74591064453125}, {-208.84014892578125, -57.353515625}, {393.79736328125, -986.03607177734375}}},
+{{{-868.3076171875, -212.74591064453125}, {371.0980224609375, -960.9017333984375}, {-236.2821044921875, -441.20074462890625}}}
+</div>
+
+<div id="quad212">
+{{{-610.4193115234375, 861.173095703125}, {403.3203125, 215.3988037109375}, {-373.5546875, 179.88134765625}}},
+{{{-610.4193115234375, 861.173095703125}, {-757.244140625, -222.137451171875}, {705.892822265625, 87.4090576171875}}}
+</div>
+
+<div id="quad232">
+{{{766.497802734375, 675.660400390625}, {639.0235595703125, 351.4776611328125}, {345.9315185546875, 624.685791015625}}},
+{{{766.497802734375, 675.660400390625}, {-901.72650146484375, 923.99169921875}, {755.665283203125, 416.728759765625}}}
+</div>
+
+<div id="quad379">
+{{{-872.76458740234375, -163.30078125}, {723.6697998046875, 177.8204345703125}, {206.470703125, 147.9564208984375}}},
+{{{-872.76458740234375, -163.30078125}, {556.937744140625, 715.4345703125}, {627.348388671875, 77.0643310546875}}}
+</div>
+
+<div id="quad413">
+{{{-127.60784912109375, 384.614990234375}, {-184.46685791015625, 717.5728759765625}, {-981.56524658203125, -827.18109130859375}}},
+{{{-127.60784912109375, 384.614990234375}, {-125.78131103515625, 751.187744140625}, {562.529541015625, -277.5535888671875}}}
+</div>
+
+<div id="quad179">
+{{{-595.956298828125, -113.24383544921875}, {-730.611572265625, 481.5323486328125}, {505.58447265625, -504.9130859375}}},
+{{{-595.956298828125, -113.24383544921875}, {-971.0836181640625, -849.73907470703125}, {-32.39227294921875, -906.3277587890625}}}
+</div>
+
+<div id="quad584">
+{{{-406.65435791015625, 599.96630859375}, {-566.71881103515625, -400.65362548828125}, {-486.0682373046875, 100.34326171875}}},
+{{{-406.65435791015625, 599.96630859375}, {799.783935546875, 992.77783203125}, {180.6688232421875, -490.0054931640625}}}
+</div>
+
+<div id="quad653">
+{{{-46.6143798828125, 164.224853515625}, {-161.7724609375, 327.61376953125}, {168.5106201171875, -948.4150390625}}},
+{{{-46.6143798828125, 164.224853515625}, {412.9364013671875, -199.26715087890625}, {-278.044677734375, 472.3961181640625}}}
+</div>
+
+<div id="quad809">
+{{{-176.8541259765625, -275.9761962890625}, {-723.969482421875, -7.4718017578125}, {931.6959228515625, 231.6737060546875}}},
+{{{-176.8541259765625, -275.9761962890625}, {-250.86737060546875, -748.8143310546875}, {-96.77099609375, -287.76336669921875}}}
+</div>
+
+<div id="quad14a">
+{{{-609.406005859375, -684.37506103515625}, {766.4923095703125, 583.657958984375}, {-912.6832275390625, -949.553466796875}}},
+{{{-609.406005859375, -684.37506103515625}, {774.140380859375, 82.2415771484375}, {540.9007568359375, -136.982666015625}}}
+</div>
+
+<div id="quad22a">
+{{{-728.5626220703125, 141.134521484375}, {749.9122314453125, -645.93359375}, {67.1751708984375, -285.85528564453125}}},
+{{{-728.5626220703125, 141.134521484375}, {-841.0341796875, -988.058349609375}, {34.87939453125, -489.359130859375}}}
+{{{276.48354206343231, -395.24293552482953}, {-728.5626220703125, 141.134521484375}}}
+{{{fX=97.702285839737073, -301.95147049201717}, {-728.5626220703125, 141.134521484375}}}
+{{{fX=-52.525628917174856, -536.31069276053427}, {-728.5626220703125, 141.134521484375}}}
+{{{fX=-5.2463328209585285, -511.63085965304060}, {-728.5626220703125, 141.134521484375}}}
+</div>
+
+<div id="quad77">
+{{{383.7933349609375, -397.5057373046875}, {480.7408447265625, 92.927490234375}, {690.7930908203125, -267.44964599609375}}},
+{{{383.7933349609375, -397.5057373046875}, {83.3685302734375, 619.781005859375}, {688.14111328125, 416.241455078125}}}
+</div>
+
+<div id="quad94">
+{{{627.6910400390625, 81.144287109375}, {168.9248046875, -211.72735595703125}, {-61.57086181640625, 915.171875}}},
+{{{627.6910400390625, 81.144287109375}, {918.159423828125, -325.468994140625}, {359.0523681640625, 817.4888916015625}}}
+{{{564.43435948662466, 47.034527772832369}, {627.6910400390625, 81.144287109375}}}
+{{{699.34014109378302, 79.147174806567705}, {627.6910400390625, 81.144287109375}}}
+</div>
+
+<div id="quad4a">
+{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}},
+{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
+</div>
+
+<div id="quad0">
+{{{-708.0077926931004413, -154.6166947224404566}, {-701.0429781735874712, -128.8517387364408933}, {505.58447265625, -504.9130859375}}},
+{{{-708.0077926931004413, -154.6166947224404566}, {-721.5125661899801344, -174.4028951148648048}, {-32.39227294921875, -906.3277587890625}}}
+{{{-707.8363172079705237, -154.25350453766481}, {-708.0077926931004413, -154.6166947224404566}}}
+{{{-708.1792267111628689, -154.9799046892118213}, {-708.0077926931004413, -154.6166947224404566}}}
+</div>
+
+<div id="quad999">
+{{{-708.00779269310044, -154.61669472244046}, {-707.92342686353186, -154.30459999551294}, {505.58447265625, -504.9130859375}}},
+{{{-708.00779269310044, -154.61669472244046}, {-708.1713780141481, -154.85636789757655}, {-32.39227294921875, -906.3277587890625}}}
+{{{-708.0077672218041, -154.61664072892336}, {-708.00779269310044, -154.61669472244046}}}
+{{{-708.00781827681976, -154.61674895426012}, {-708.00779269310044, -154.61669472244046}}}
+</div>
+
+<div id="quad113">
+{{{425.018310546875, -866.61865234375}, {-918.76531982421875, 209.05322265625}, {964.34716796875, 199.52587890625}}},
+{{{425.018310546875, -866.61865234375}, {703.10693359375, -955.0738525390625}, {-952.24664306640625, -717.94775390625}}}
+</div>
+
+<div id="quad136">
+{{{178.1549072265625, 62.724609375}, {541.3643798828125, 223.823486328125}, {-446.77471923828125, -15.990478515625}}},
+{{{178.1549072265625, 62.724609375}, {-347.14031982421875, -834.27191162109375}, {-495.138885498046875, 96.476806640625}}}
+</div>
+
+<div id="quad206">
+{{{-503.007415771484375, -318.59490966796875}, {-798.330810546875, -881.21630859375}, {-127.2027587890625, 769.6160888671875}}},
+{{{-503.007415771484375, -318.59490966796875}, {-153.6217041015625, -776.896728515625}, {-378.43701171875, -296.3197021484375}}}
+{{{-468.9176053311167607, -89.39573455985038208}, {-503.007415771484375, -318.59490966796875}}}
+{{{-356.1573846604815685, -497.6768266540607328}, {-503.007415771484375, -318.59490966796875}}}
+{{{-559.0376987487186398, -420.2054253473417589}, {-503.007415771484375, -318.59490966796875}}}
+{{{-431.6586315464865606, -409.8353728177644371}, {-503.007415771484375, -318.59490966796875}}}
+</div>
+
+<div id="quad640">
+{{{412.260498046875, 49.193603515625}, {838.97900390625, 86.9951171875}, {427.7896728515625, -605.6881103515625}}},
+{{{412.260498046875, 49.193603515625}, {-995.54583740234375, 990.032470703125}, {-881.18670654296875, 461.211669921875}}}
+</div>
+
+<div id="quad3160">
+{{{426.645751953125, 813.79150390625}, {-387.23828125, -588.89483642578125}, {792.4261474609375, -704.4637451171875}}},
+{{{426.645751953125, 813.79150390625}, {19.24896240234375, -416.09906005859375}, {233.8497314453125, 350.778564453125}}}
+</div>
+
+<div id="quad35237">
+{{{-770.8492431640625, 948.2369384765625}, {-853.37066650390625, 972.0301513671875}, {-200.62042236328125, -26.7174072265625}}},
+{{{-770.8492431640625, 948.2369384765625}, {513.602783203125, 578.8681640625}, {960.641357421875, -813.69757080078125}}}
+</div>
+
+<div id="quad37226">
+{{{563.8267822265625, -107.4566650390625}, {-44.67724609375, -136.57452392578125}, {492.3856201171875, -268.79644775390625}}},
+{{{563.8267822265625, -107.4566650390625}, {708.049072265625, -100.77789306640625}, {-48.88226318359375, 967.9022216796875}}}
+</div>
+
+<div id="quad67242">
+{{{598.857421875, 846.345458984375}, {-644.095703125, -316.12921142578125}, {-97.64599609375, 20.6158447265625}}},
+{{{598.857421875, 846.345458984375}, {715.7142333984375, 955.3599853515625}, {-919.9478759765625, 691.611328125}}}
+</div>
+
+<div id="quad208">
+{{{481.1463623046875, -687.09613037109375}, {643.64697265625, -951.9462890625}, {162.5869140625, 698.7342529296875}}},
+{{{481.1463623046875, -687.09613037109375}, {171.8175048828125, -919.07977294921875}, {153.3433837890625, -587.43072509765625}}}
+</div>
+
+<div id="quad8a">
+{{{344.2755126953125, -689.900390625}, {743.6728515625, 512.8448486328125}, {928.598388671875, 111.946044921875}}},
+{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
+</div>
+
+<div id="quad8b">
+{{{344.2755126953125, -689.900390625}, {928.598388671875, 111.946044921875}, {743.6728515625, 512.8448486328125}}},
+{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
+</div>
+
+<div id="quad8741">
+{{{944.9024658203125, 939.454345703125}, {-971.06219482421875, -914.24395751953125}, {-878.764404296875, -297.61602783203125}}},
+{{{944.9024658203125, 939.454345703125}, {-838.96612548828125, -785.837646484375}, {-126.80029296875, 921.1981201171875}}}
+{{{107.03238931174118, 218.460612766889}, {944.9024658203125, 939.454345703125}}}
+{{{-292.72752350740279, 99.917575976335598}, {944.9024658203125, 939.454345703125}}}
+</div>
+
+<div id="quad89987">
+{{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
+{{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
+</div>
+
+<div id="simplifyQuadratic36">
+{{{1.9474306106567383, 2.3777823448181152}, {1.9234547048814592, 2.2418855043499213}, {1.8885438442230225, 2.1114561557769775}}}
+{{{1.9474306106567383, 2.3777823448181152}, {2.0764266380046235, 2.2048800651418379}, {1.8888888359069824, 2.1111111640930176}}}
+</div>
+
+<div id="simplifyQuadratic58">
+{{326.236786,205.854996}, {329.104431,231.663818}, {351.512085,231.663818}}
+{{303.12088,141.299606}, {330.463562,217.659027}}
+</div>
+
+<div id="simplifyQuadratic58a">
+{{{326.23678588867188, 205.85499572753906}, {328.04376176056422, 222.11778818951981}, {337.6092529296875, 228.13298034667969}
+{{{303.12088012695312, 141.29960632324219}, {330.46356201171875, 217.65902709960937}
+</div>
+
+<div id="quadratic58again">
+{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}
+{{322.12738,233.397751}, {295.718353,159.505829}}
+</div>
+
+<div id="simplifyQuadratic56">
+{{{380.29449462890625, 140.44486999511719}, {387.29080200195312, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
+{{{380.29449462890625, 140.44486999511719}, {388.29925537109375, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
+{{{380.29449462890625, 140.44486999511719}, {387.692810, 137.858429}}}
+</div>
+
+<div id="simplifyQuadratic56a">
+{{{380.29449462890625, 140.44486999511719}, {387.29079954793264, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
+{{{380.29449462890625, 140.44486999511719}, {388.29925767018653, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
+{{fX=380.29449462890625 fY=140.44486999511719 }, {fX=398.16494750976562 fY=136.67460632324219 }} }
+{{fX=380.29449462890625 fY=140.44486999511719 }, {fX=396.03991699218750 fY=136.67460632324219 }}
+</div>
+
+<div id="simplifyQuadratic27">
+{{{1, 1}, {1, 0.666666687f}, {0.888888896f, 0.444444448f}}}
+{{{1, 1}, {1, 0.5f}, {0, 0}}}
+{{fX=1.0000000000000000 fY=1.0000000000000000 }, {fX=0.00000000000000000 fY=0.00000000000000000 }} }
+{{fX=1.0000000000000000 fY=1.0000000000000000 }, {fX=0.88888889551162720 fY=0.44444444775581360 }} }
+</div>
+
+<div id="cubicOp7d">
+{{{0.7114982008934021, 1.6617077589035034}, {0.51239079236984253, 1.4952657222747803}, {0.27760171890258789, 1.2776017189025879}, {0, 1}}}
+{{{0.7114982008934021, 1.6617077589035034}, {0.20600014925003052, 1.7854888439178467}, {9.8686491813063348e-017, 1.9077447652816772}, {0, 1}}}
+</div>
+
+<div id="cubicOp25i">
+{{{3.3856770992279053, 1.6298094987869263}, {3.777235186270762, 1.2744716237277114}, {3.7191683314895783, 1.4127666421509713}, {3.3995792865753174, 1.6371387243270874}}}
+{{{3.3856770992279053, 1.6298094987869263}, {3.3902986605112582, 1.6322361865810757}, {3.3949326825525121, 1.6346792563210237}, {3.3995792865753174, 1.6371387243270874}}}
+{{3.3856770992279053, 1.6298094987869263 }, {3.3995792865753174, 1.6371387243270874 }}
+</div>
+
+<div id="eldorado1">
+{{{1006.69513f, 291}, {1023.26367f, 291}, {1033.84021f, 304.431458f}, {1030.31836f, 321}}}
+{{{1030.318359375, 321}, {1036.695068359375, 291}}}
+{{fX=1030.3183593750000 fY=321.00000000000000 }, {fX=1006.6951293945312 fY=291.00000000000000 }} }
+</div>
+
+<div id="carpetplanet1">
+{{fX=67.000000000000000, 913.00000000000000 }, {194.00000000000000, 1041.0000000000000 }} }
+{{fX=67.000000000000000, 913.00000000000000 }, {67.662002563476562, 926.00000000000000 }} }
+{{{67, 913}, {67, 917.388977f}, {67.223999f, 921.726013f}, {67.6620026f, 926}}}
+{{{67, 913}, {67, 983.692017f}, {123.860001f, 1041}, {194, 1041}}}
+{{{67, 913}, {67.17070902440698, 919.69443917507760}}}
+</div>
+
+<div id="cubicOp104">
+{{{2.25, 2.5}, {4.5, 1}}}
+{{{2.25, 2.5}, {3.0833333333333321, 1.9999999999999973}, {4.0277778307596899, 1.2777777777777759}, {4.8611111640930176, 1}}}
+{{{2.25, 2.5}, {1.9476099234472042, 2.6814340459316774}, {1.6598502000264239, 2.8336073904096661}, {1.3973386287689209, 2.9246666431427002}}}
+{{{2.25, 2.5}, {1.2674896717071533, 3.1550068855285645}}}
+</div>
+
+<div id="cubicOp105">
+{{{2.4060275554656982, 3.4971563816070557}, {2.9702522134213849, 4.2195279679982622}, {3.8172613958721247, 5.0538091166976979}, {5, 6}}}
+{{{2.4060275554656982, 3.4971563816070557}, {3.4194286958002023, 3.5574883660881684}, {4.0077197935900575, 2.6628073781813661}, {2.2602717876434326, 0.33545622229576111}}}
+</div>
+
+<div id="cubicOp106">
+{{{0.80825299024581909, 1.9691258668899536}, {0.8601454496383667, 1.9885541200637817}, {0.92434978485107422, 2}, {1, 2}}}
+{{{0.80825299024581909, 1.9691258668899536}, {2.2400102615356445, 3.5966837406158447}, {2.5486805438995361, 3.362929105758667}, {2.494147777557373, 2.5976591110229492}}}
+{{{0.80825299024581909, 1.9691258668899536}, {2.494147777557373, 2.5976591110229492}}}
+{{{0.80825299024581909, 1.9691258668899536}, {1, 2}}}
+</div>
+
+<div id="cubicOp109">
+{{{5, 4}, {5.443139240552143931, 3.556860759447856069}, {5.297161243696338673, 3.702838775882067335}, {4.649086475372314453, 3.654751062393188477}}}
+{{{5, 4}, {4.876459521889748849, 3.876459521889748849}, {4.759596556247283949, 3.761504502886134915}, {4.649086475372314453, 3.654751062393188477}}}
+</div>
+
+<div id="skpwww_joomla_org_23">
+{{{421, 378}, {421, 380.209137f}, {418.761414f, 382}, {416, 382}}}
+{{{320, 378}, {421, 378.000031f}}}
+{{{421, 378.000031f}, {421, 383}}}
+{{{416, 383}, {418.761414f, 383}, {421, 380.761414f}, {421, 378}}}
+</div>
+
+<div id="xop1i">
+{{5.000,1.000}, {5.191,0.809}, {5.163,0.837}, {4.993,1.000}}
+{{5.000,1.000}, {4.968,1.024}}
+{{5.000,1.000}, {4.998,1.000}, {4.995,1.000}, {4.993,1.000}}
+</div>
+
+<div id="xop1u">
+{{3.500,3.500}, {3.000,4.000}, {2.500,4.500}, {1.000,4.000}}
+{{3.500,3.500}, {3.113,3.887}, {2.725,4.275}, {2.338,3.732}}
+</div>
+
+<div id="xOp2i">
+{{{2, 3}, {1.3475509011665685, 4.9573472965002949}, {2.8235509286078759, 3.5091759365574173}, {3.6505906581878662, 1.9883773326873779}}}
+{{{2, 3}, {2.4604574005585795, 2.654656949581065}, {3.0269255632437986, 2.3093137214344743}, {3.6505906581878662, 1.9883773326873779}}}
+{{{2, 3}, {1.0000000000000013, 3.7500000000000004}, {0.500000000000001, 4.5}, {1, 5}}}
+</div>
+
+<div id="testQuadratic56">
+{{{380.29449462890625, 140.44486999511719}, {379.59701460635523, 140.8207374882179}, {378.91729736328125, 141.23385620117187}}}
+{{{380.29449462890625, 140.44486999511719}, {387.29079954793264, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
+{{{380.29449462890625, 140.44486999511719}, {388.29925767018653, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
+</div>
+
+<div id="testQuad15">
+{{{1, 3}, {1, 1}}}
+{{{1, 3}, {0, 0}}}
+{{{1, 3}, {2, 0}, {0, 0}}}
+</div>
+
+<div id="testQuad21">
+{{{0, 0}, {1, 1}}}
+{{{0, 0}, {3, 0}, {2, 3}}}
+{{{0, 0}, {2, 3}}}
+{{{0, 0}, {2, 1}}}
+</div>
+
+<div id="testQuad22">
+{{{0, 0}, {1.2000000476837158, 0.80000001192092896}}}
+{{{0, 0}, {2, 0}}}
+{{{0, 0}, {0, 1}, {3, 2}}}
+{{{0, 0}, {1, 1}}}
+</div>
+
+<div id="testQuad23">
+{{{1, 3}, {1.9090908914804459, 1.1818182170391081}, {0.33884298801422119, 1.0165289640426636}}}
+{{{1, 3}, {0.33884298801422119, 1.0165289640426636}}}
+{{{1, 3}, {3, 0}}}
+</div>
+
+<div id="cubicOp35d">
+{{{2.211416482925415, 1.6971458196640015}, {1.2938009262874868, 2.8273619288830005}, {0.64690048634813535, 3.5876019453925414}, {0, 1}}}
+{{{2.211416482925415, 1.6971458196640015}, {1.0082962512969971, 1.997925877571106}}}
+{{{2.211416482925415, 1.6971458196640015}, {5, 1}}}
+</div>
+
+<div id="skpnational_com_au81">
+{{{1110.7071533203125, 817.29290771484375}, {1110.9998779296875, 817.58587646484375}, {1111, 818}}}
+{{{1110.7071533203125, 817.29290771484375}, {1110.526180767307778, 817.1119214508684081}, {1110.276144384999725, 817}, {1110, 817}}}
+{{{1110.7071533203125, 817.29290771484375}, {1110.888097894721341, 817.4738660071997174}, {1111, 817.7238677851287321}, {1111, 818}}}
+{{{1110.7071533203125, 817.29290771484375}, {1110.4140625, 817.0001220703125}, {1110, 817}}}
+</div>
+
+<div id="cubicOp85d">
+{{{1.0648664236068726, 2.9606373310089111}, {0.80208036362614099, 2.7936484180272374}, {0.49170560730211144, 2.2292640182552783}, {0, 1}}}
+{{{1.0648664236068726, 2.9606373310089111}, {0.6261905430171294, 3.2248910899179175}, {0.38860045191888659, 2.9430022595944321}, {0, 1}}}
+{{{1.0648664236068726, 2.9606373310089111}, {1.4282355765013004, 3.191542348791669}, {1.7006143409852195, 2.6626209548338378}, {2.2355968952178955, 2.0810616016387939}}}
+{{{1.0648664236068726, 2.9606373310089111}, {1.3437142856601656, 2.7926622975690494}, {1.7038131304059798, 2.4040122748806132}, {2.2355968952178955, 2.0810616016387939}}}
+</div>
+
+<div id="testQuads22">
+{{{0, 0}, {1.20000004768371582, 0.8000000119209289551}}}
+{{{0, 0}, {2, 0}}}
+{{{0, 0}, {0, 1}, {3, 2}}}
+{{{0, 0}, {1, 1}}}
+</div>
+
+<div id="cubicOp59d">
+{{{4, 1}, {4, 0.37698365082686142}, {4.3881493046286568, 2.4710128800004547}, {3.4716842174530029, 2.9292664527893066}}}
+{{{4, 1}, {0, 1}}}
+</div>
+
+<div id="findFirst1">
+{{{2.5767931938171387, 0.88524383306503296}, {2.4235948002142855, 0.88692501490384834}, {2.2328897699358898, 0.92237007668803672}, {2, 1}}}
+{{{2.5767931938171387, 0.88524383306503296}, {1.6008643534817426, 1.1609015907463158}, {1.1200849961943122, 1.8138386966264941}, {0.75343161821365356, 2.7170474529266357}}}
+{{{2.5767931938171387, 0.88524383306503296}, {4.0492746201577932, 0.86908498848619054}, {2.0567957107800288, 3.9721309710522448}, {0.75343161821365356, 2.7170474529266357}}}
+{{{2.5767931938171387, 0.88524383306503296}, {3.3470152174198557, 0.66768936887879282}, {4.4256496583071421, 0.68512993166142844}, {6, 1}}}
+{{{2.57679319, 0.885243833}, {5.15358639, 0.885243833}}}
+</div>
+
+<div id="testQuads54">
+{{1.000,1.000}, {1.500,0.500}, {1.500,0.250}}
+{{1.000,1.000}, {1.667,0.333}}
+{{1.000,1.000}, {2.000,3.000}}
+</div>
+
+<div id="testQuads45">
+{{{3, 3}, {3, 2.7999999523162842}, {2.880000114440918, 2.6400001049041748}}}
+{{{3, 3}, {3, 2}, {2, 0}}}
+{{{3, 3}, {2, 0}}}
+{{{3, 3}, {2.880000114440918, 2.6400001049041748}}}
+</div>
+
+<div id="testQuads59">
+{{{3, 1}, {3, 0}}}
+{{{3, 1}, {2.6666667461395264, 0.66666668653488159}}}
+{{{3, 1}, {2.8000003099441542, 1.1999996900558463}, {2.6800000667572021, 1.3600000143051147}}}
+{{{3, 1}, {2.6666667461395264, 1.3333333730697632}}}
+</div>
+
+<div id="skpcarrot_is24">
+{{{1020.08099, 672.161987}, {1020.08051, 651.450988}, {1011.68576, 632.700988}, {998.113511, 619.128738}}}
+{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
+{{{1020, 672}, {1020, 651.289307}, {1012.67767, 633.611633}, {998.03302, 618.96698}}}
+{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
+</div>
+
+<div id="skpcarrot_is24a">
+{{{1020, 672}, {1020, 651.289307}, {1012.67767, 633.611633}, {998.03302, 618.96698}}}
+{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
+</div>
+
+<div id="skpcarrot_is24b">
+{{{1020.08099, 672.161987}, {1020.08051, 651.450988}, {1011.68576, 632.700988}, {998.113511, 619.128738}}}
+{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
+</div>
+
+<div id="skpcarrot_is24c">
+{{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
+{{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}},
+</div>
+
+<div id="skpcarrot_is24d">
+{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
+{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
+</div>
+
+<div id="skpcarrot_is24e">
+{{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
+{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
+{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
+</div>
+
+<div id="slop1">
+{{{-378.22698974609375, -987.8935546875}, {-47.53326416015625, 482.7139892578125}, {-626.4708251953125, -338.62969970703125}, {-847.94854736328125, -861.42230224609375}}}
+{{{-378.61790442466736, -987.49146723747253}, {-282.51787429804051, -556.39065286764685}, {-278.55106873374694, -364.17984985308294}}}
+{{{-305.5273847156202, -615.99979442705023}, {-305.04071954345704, -612.87932617187505}}}
+</div>
+
+qT=0.98917687 cT=0.788725084 dist=312.188493 cross=-40759.4852
+<div id="slop2">
+{{{79.5137939,-249.867188}, {260.778931,-561.349854}, {343.375977,-472.657898}, {610.251465,97.8208008}}}
+{{{312.993284,-406.178762}, {418.053808,-326.9483}, {610.036929,97.2408578}}}
+{{{463.107827,-200.015424}, {602.008878,79.5702581}}}
+</div>
+
+qT=0.0192863061 cT=0.241285225 dist=652.007881 cross=528435.665
+<div id="slop3">
+{{{-895.015015,-523.545288}, {223.166992,-999.644531}, {615.428711,-767.162109}, {605.479736,480.355713}}}
+{{{-894.932414,-523.605499}, {-66.4040558,-889.938889}, {278.515212,-667.684158}}}
+{{{-207.851881,-740.109296}, {-831.819002,-550.955104}}}
+</div>
+
+qT=0.0245724525 cT=0.231316637 dist=407.103004 cross=-46286.5686
+<div id="slop4">
+{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
+{{{876.323133,-849.535824}, {594.868958,-415.229224}, {416.667192,-30.0277669}}}
+{{{638.343827,-458.798274}, {849.023879,-807.14691}}}
+</div>
+
+qT=0.000316393778 cT=0.248252634 dist=489.678412 cross=-57352.7653
+<div id="slop5">
+{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
+{{{876.147506,-849.184429}, {593.963775,-414.437342}, {416.842819,-30.3791618}}}
+{{{622.139843,-430.512844}, {876.135915,-849.166571}}}
+</div>
+
+qT=0.989562776 cT=0.760518485 dist=211.50589 cross=134901.42
+<div id="slop6">
+{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
+{{{416.141554,-30.4534414}, {237.846068,356.664216}, {335.719378,533.692585}}}
+{{{305.345404,315.701195}, {331.440368,525.591152}}}
+</div>
+
+qT=0.0978245708 cT=0.397465904 dist=959.737748 cross=158093.403
+<div id="slop7">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.666617,-82.159942}, {-661.943328,620.81113}, {-723.44072,537.121833}}}
+{{{-347.560585,421.003177}, {507.062151,-15.707855}}}
+</div>
+
+qT=0.169803964 cT=0.389326872 dist=658.039939 cross=107865.424
+<div id="slop8">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.536617,-81.7990275}, {-662.457623,620.485316}, {-723.31072,536.760918}}}
+{{{-330.996421,413.091598}, {257.080063,117.824582}}}
+</div>
+
+qT=0.0863836955 cT=0.387901231 dist=986.24777 cross=157348.113
+<div id="slop9">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.248316,-81.8984216}, {-662.339696,620.351182}, {-723.022419,536.860313}}}
+{{{-328.058099,411.68229}, {549.399512,-38.5985162}}}
+</div>
+
+qT=0.175359403 cT=0.390420692 dist=640.051938 cross=105488.084
+<div id="slop10">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.760605,-81.9577046}, {-661.301606,620.029216}, {-723.534707,536.919596}}}
+{{{-333.243516,414.168229}, {238.961251,127.37878}}}
+</div>
+
+qT=0.0986412358 cT=0.382365595 dist=921.951857 cross=145651.761
+<div id="slop11">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.919588,-82.1841825}, {-662.488256,620.04494}, {-723.693691,537.146073}}}
+{{{-316.541641,406.142013}, {504.067361,-14.0913644}}}
+</div>
+
+qT=0.146746849 cT=0.391456086 dist=750.006927 cross=123679.094
+<div id="slop12">
+{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
+{{{629.712675,-82.0366321}, {-661.487948,620.191832}, {-723.486777,536.998523}}}
+{{{-335.364605,415.183549}, {334.079508,77.0194322}}}
+</div>
+
+qT=0.00196158131 cT=0.20357489 dist=466.080185 cross=241741.95
+<div id="slop13">
+{{{-627.671509,-359.277039}, {222.414551,-791.598328}, {390.603027,-581.903687}, {-21.7962036,560.33728}}}
+{{{-627.675958,-359.282959}, {-52.012535,-659.029798}, {116.967835,-524.756101}}}
+{{{-192.427848,-541.033993}, {-622.696937,-361.871356}}}
+</div>
+
+qT=0.948725598 cT=0.744200608 dist=699.694313 cross=179509.878
+<div id="slop14">
+{{{-362.331848,427.292603}, {634.418701,-661.946533}, {438.438599,-626.147278}, {-893.060425,214.243408}}}
+{{{259.978301,-393.549091}, {181.692599,-474.452437}, {-892.389834,213.689096}}}
+{{{-95.1310032,-267.365579}, {-696.89984,89.6307768}}}
+</div>
+
+qT=0.971677129 cT=0.755306143 dist=771.998962 cross=189468.817
+<div id="slop15">
+{{{-362.331848,427.292603}, {634.418701,-661.946533}, {438.438599,-626.147278}, {-893.060425,214.243408}}}
+{{{259.662278,-393.355886}, {181.612843,-473.935297}, {-892.073812,213.495892}}}
+{{{-120.438346,-253.451518}, {-782.461182,143.673352}}}
+</div>
+
+qT=0.571797795 cT=0.773951562 dist=495.560209 cross=221091.889
+<div id="slop16">
+{{{447.192383,-883.210205}, {359.794678,-987.765808}, {755.427612,-754.328735}, {963.672119,746.545776}}}
+{{{635.795655,-580.726915}, {810.704547,-228.491534}, {963.345162,745.921688}}}
+{{{801.470356,-87.7105789}, {646.551495,-558.433498}}}
+</div>
+
+qT=0.579236693 cT=0.782683167 dist=281.750564 cross=65125.1655
+<div id="slop17">
+{{{-931.259155,-883.589966}, {-485.682007,-615.793701}, {-68.5913696,-928.695923}, {431.499268,-810.584778}}}
+{{{-177.087049,-804.265618}, {110.452267,-866.525236}, {430.718323,-810.414444}}}
+{{{116.080189,-836.904702}, {-164.080748,-807.017753}}}
+</div>
+
+qT=0.0102075348 cT=0.2448024 dist=855.408492 cross=463614.179
+<div id="slop18">
+{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
+{{{-866.7221,843.65105}, {-308.756317,-34.8353977}, {183.843514,-346.222431}}}
+{{{-336.612013,120.039627}, {-844.283739,808.5112}}}
+</div>
+
+qT=0.473968306 cT=0.266805943 dist=567.851844 cross=-461509.104
+<div id="slop19">
+{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
+{{{-867.218781,844.133445}, {-310.496711,-35.0458119}, {184.340195,-346.704825}}}
+{{{-290.018097,66.7065093}, {132.536746,-312.639141}}}
+</div>
+
+qT=0.0232589401 cT=0.241085469 dist=789.989464 cross=428119.544
+<div id="slop20">
+{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
+{{{-866.942271,843.928587}, {-309.178151,-34.0018497}, {184.063685,-346.499968}}}
+{{{-344.507162,129.265381}, {-815.30119,763.644082}}}
+</div>
+
+<div id="skpnamecheap_405">
+{{{141.008835f, 837.9646f}, {141.235291f, 1109.05884f}}}
+{{{141, 842}, {141.14502f, 1000}}}
+{{{141.14502f, 1000}, {140, 1000}}}
+</div>
+
+<div id="skpwww_dealnews_com_315">
+{{{969.87066650390625, 4279.810546875}, {967.7166748046875, 4260}}}
+{{{969.87066650390625, 4279.810546875}, {969.866972698829386, 4279.809233889284769}, {969.88751220703125, 4279.81640625}}}
+{{{969.87066650390625, 4279.810546875}, {970, 4281}}}
+{{{969.87066650390625, 4279.810546875}, {968.9217161046863112, 4279.473236623693992}, {968.17156982421875, 4278.53564453125}}}
+{{{969.8706626470486754, 4279.810469740555163}, {969.8790796016525064, 4279.813461598796493}, {969.88751220703125, 4279.81640625}}}
+</div>
+
+<div id="skpwww_dealnews_com_315_a">
+{{{969.8706626470486754, 4279.810469740555163}, {969.8790796016525064, 4279.813461598796493}, {969.88751220703125, 4279.81640625}}}
+{{{969.87066650390625, 4279.810546875}, {969.8790834585100811, 4279.81353873324133}}}
+{{{969.88751220703125, 4279.81640625}, {969.8790796016525064, 4279.813461598796493}}}
+</div>
+
+<div id="testQuads60">
+{{{2, 2}, {1.977731304590550021, 1.97773134708404541}, {1.95645439624786377, 1.95546269416809082}}}
+{{{2, 2}, {2, 3}}}
+{{{2, 2}, {2, 1.960000038146972656}}}
+{{{2, 2}, {1.955341815948486328, 1.955341815948486328}}}
+</div>
+
+<div id="testQuads60_a">
+{{{2, 0}, {1, 1}, {2, 2}}}
+{{{2, 2}, {0, 0}}}
+</div>
+
+<div id="testQuads60_b">
+{{2,1}, {0,2}, {3,2}},
+{{3,2}, {2,3}},
+{{2,3}, {2,1}},
+{{0,0}, {2,0}},
+{{2,0}, {1,1}, {2,2}},
+{{2,2}, {0,0}},
+</div>
+
+<div id="skpelpais_com_18">
+{{183,8507}, {552,8506.99023}},
+{{552,8506.99023}, {552,8508}},
+{{552,8508}, {183,8508}},
+{{183,8508}, {183,8507}},
+op intersect
+{{183,8508}, {183,8506.99023}},
+{{183,8506.99023}, {552,8507}},
+{{552,8507}, {552,8508}},
+</div>
+
+<div id="skpwww_cityads_ru_249">
+{{{1000, 10.4003992f}, {1000, 13.3527431f}}}
+{{{1000, 13.3527431f}, {999.917603f, 13.2607508f}, {999.82843f, 13.1715727f}}}
+{{{1000, 13}, {999.969971f, 37.0299988f}}}
+</div>
+
+<div id="skpwww_maturesupertube_com_21">
+    {{{{3.87867975f, 11831.8789f}, {4.7573595f, 11831}, {6, 11831}}},
+     {{{2, 11830}, {4.5f, 11832.5f}}}},
+</div>
+
+<div id="loop1">
+{{1, 4, 2, 6, 0, 5, 4.5f, 4.33333302f
+{{2, 6, 0, 5, 4.5f, 4.33333302f, 1, 4
+{{{3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}, {2, 4}}}
+{{{2, 4}, {3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}}}
+</div>
+
+<div id="serp1">
+{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
+{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
+</div>
+<div id="serp2">
+{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
+{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
+</div>
+<div id="serp3">
+{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
+{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
+</div>
+
+<div id="skpwww_seopack_blogspot_com_2153">
+{{{924, 245.472672f}, {1143, 247}}}
+{{{1000, 246}, {927.340759f, 245.505722f}}}
+{{{999.892212f, 246}, {927.340759f, 245.505722f}}}
+</div>
+
+<div id="self1">
+{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
+{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
+</div>
+
+<div id="skpwww_pindosiya_com_99">
+{{{901.0869140625, 547}, {899, 556}}}
+{{{900.0235595703125, 551.60284423828125}, {900.06072998046875, 551.29705810546875}, {900.15655517578125, 551.0157470703125}}}
+</div>
+
+<div id="cubicLineMiss1">
+{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875}, {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}}
+{{{-287.9506133720805678, -557.1376476615772617}, {-285.9506133720805678, -557.1376476615772617}}}
+</div>
+
+<div id="cubicLineMiss2">
+{{{-818.4456787109375, 248.218017578125}, {944.18505859375, -252.2330322265625}, {957.3946533203125, -45.43280029296875}, {-591.766357421875, 868.6187744140625}}}
+{{{435.1963493079119871, -16.42683763243891093}, {437.1963493079119871, -16.42683763243891093}}}
+</div>
+
+<div id="cubicLineMiss3">
+{{{-818.4456787109375, 248.218017578125}, {944.18505859375, -252.2330322265625}, {957.3946533203125, -45.43280029296875}, {-591.766357421875, 868.6187744140625}}}
+{{{397.5007682490800676, -17.35020084021140008}, {399.5007682490800676, -17.35020084021140008}}}
+</div>
+
+<div id="cubicLineMiss4">
+{{{-652.660888671875, -384.6475830078125}, {-551.7723388671875, -925.5025634765625}, {-321.06658935546875, -813.10345458984375}, {142.6982421875, -47.4503173828125}}}
+{{{-478.4372049758064236, -717.868282575075682}, {-476.4372049758064236, -717.868282575075682}}}
+</div>
+
+<div id="cubicLineErr1">
+{{{-954.4322509765625, 827.2216796875}, {-420.24017333984375, -7.80560302734375}, {799.134765625, -971.4295654296875}, {-556.23486328125, 344.400146484375}}}
+
+{{{58.57411390280688579, -302.8879316712078662}, {60.57411390280688579, -302.8879316712078662}}}
+</div>
+
+<div id="cubicLineErr2">
+{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875}, {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}}
+{{{-287.95061337208057, -557.13764766157726}, {-285.95061337208057, -557.13764766157726}}}
+{{{-308.65463091760211, -549.4520029924679} -308.65463091760211, -569.4520029924679
+</div>
+
+<div id="skpwww_educationalcraft_com_4">
+{{{974.91998291015625, 1481.7769775390625}, {974.91998291015625, 1481.7760009765625}, {977.3189697265625, 1484.6190185546875}, {975.10699462890625, 1486.97802734375}}}
+{{fX=974.91998291015625 fY=1481.7769775390625 }, {fX=974.92071342468262 fY=1481.7972941398621 }} }
+</div>
+
+<div id="skpwww_educationalcraft_com_4a">
+{{{962.10699462890625, 1485.654052734375}, {962.10699462890625, 1485.654052734375}, {960.58502197265625, 1483.595947265625}, {957.53900146484375, 1482.0970458984375}}}
+{{{963.21502685546875, 1486.6700439453125}, {962.7449951171875, 1486.6700439453125}, {962.10699462890625, 1485.654052734375}, {962.10699462890625, 1485.654052734375}}}
+</div>
+
+<div id="skpwww_educationalcraft_com_4b">
+{{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}}
+{{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}}
+</div>
+
+<div id="skpwww_aceinfographics_com_106">
+{{{168, 29.6722088f}, {166, 29.6773338f}}}
+{{{166.878677f, 29.6750813f}, {167.388f, 29.6763878f}, {168.019989f, 29.6769352f}}}
+</div>
+
+<div id="skpwww_tcmevents_org_13">
+{{{465.84668f, 547.288391f}, {467.274506f, 552.852356f}, {468.506836f, 560.718567f}}}
+{{{468.506836f, 560.718567f}, {467.336121f, 553.24585f}, {465.951904f, 547.960144f}}
+</div>
+
+<div id="skpwww_kitcheninspirations_wordpress_com_66">
+{{{60.8333359f, 27820.498f}, {47.1666679f, 27820.5f}}}
+{{{60.8333359f, 27820.668f}, {60.8333359f, 27820.498f}}}
+{{{47.1666679f, 27820.498f}, {60.8333359f, 27820.5f}}}
+{{{60.8333359f, 27820.5f}, {60.8333359f, 27820.668f}}}
+</div>
+
+<div id="skpwww_galaxystwo_com_4">
+{{{10105, 2510}, {10123, 2509.98999f}}}
+{{{10105, 2509.98999f}, {10123, 2510}}}
+</div>
+
+<div id="skpwww_wartepop_blogspot_com_br_6">
+{{{124.666672f, 152.333344f}, {125.909309f, 152.333344f}, {126.787994f, 153.309662f}}}
+{{fX=124.66666412353516 fY=152.33334350585937 }, {fX=126.78799438476562 fY=153.30966186523437 }} }
+{{fX=124.66666412353516 fY=152.33334350585937 }, {fX=127.02368927001953 fY=153.30966186523437 }} }
+</div>
+
+<div id="skpwww_wartepop_blogspot_com_br_6a">
+{{{124.666672f, 152.333344f}, {125.909309f, 152.333344f}, {126.787994f, 153.309662f}}}
+{{fX=124.66667175292969 fY=152.33334350585937 }, {fX=126.78799438476562 fY=153.30966186523437 }} }
+{{fX=124.66667175292969 fY=152.33334350585937 }, {fX=127.02368927001953 fY=153.30966186523437 }} }
+</div>
+
+<div id="skpcarrot_is24x">
+{{{1020.08099, 672.16198699999995}, {1020.08002, 630.73999000000003}, {986.50201400000003, 597.16198699999995}, {945.08099400000003, 597.16198699999995}}}
+{{{1020, 672}, {1020, 640.93395999999996}, {998.03301999999996, 618.96698000000004}}}
+</div>
+
+<div id="skpwww_9to5mac_com_64">
+{{{{365.848175,5081.15186}, {368,5103}}},
+{{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
+</div>
+
+<div id="issue2753">
+{{50.6,117.001}, {50.6,117.001}, {164.601,85.2}, {188.201,117.601}},
+{{188.201,117.601}, {188.201,117.601}, {174.801,93}, {39,124.001}},
+computed quadratics set
+{{50.6,117.001}, {52.4926111,116.112083}, {81.0298889,109.956333}},
+{{81.0298889,109.956333}, {109.567167,103.800583}, {142.037778,103.045}},
+{{142.037778,103.045}, {174.508389,102.289417}, {188.201,117.601}},
+computed quadratics set
+{{188.201,117.601}, {189.210269,116.85838}, {179.697259,112.371148}},
+{{179.697259,112.371148}, {170.18425,107.883917}, {138.037741,108.563519}},
+{{138.037741,108.563519}, {105.891231,109.24312}, {39,124.001}},
+</div>
+
+<div id="battle6001">
+{{{0.111722f, -59.999897f}, {0.0895366594f, -59.999939f}, {0.0673542097f, -59.9999695f}, {0.0451717526f, -59.9999847f}}}
+{{{0.0451734141f, -59.9999847f}, {0.0438041016f, -59.9999886f}, {0.0424379632f, -59.9999886f}, {0.0410718247f, -59.9999886f}}}
+</div>
+
+<div id="fuzz763_3084">
+{{{38.6568527f, 27.3431454f}, {41, 29.6862907f}, {41, 33}}}
+{{{39.131218f, 27.8554096f}, {41, 30.0406685f}, {41, 33}}}
+{{{44.6041069f, 27.9369583f}, {41.8078537f, 28.9057903f}, {39.131218f, 27.8554096f}}}
+</div>
+
+<div id="fuzz763_378">
+{{{-52.8062439,14.1493912}, {-53.6638947,10.948595}, {-52.0070419,8.07883835}}
+{{-52.8054848,14.1522331}, {-53.6633072,10.9514809}, {-52.0066071,8.08163643}}
+</div>
+
+<div id="fuzz763_378d">
+{{{-37.351398500000002, 10.0082998}, {-36.493801099999999, 13.209099800000001}, {-38.150600400000002, 16.0788002}}
+{{-37.350898700000002, 10.010299699999999}, {-36.493099200000003, 13.2110004}, {-38.149799299999998, 16.080900199999999}}}
+{{-37.320497331221297, 10.126736679362402}, {-37.320543141534543 fY=10.126556206903867 }}
+{{-37.514829818825397, 14.722977321623326}, {=-37.514249241879924 fY=14.725464892492159 }}
+</div>
+
+<div id="fuzz763_6411089">
+{{38.5810318, 38.7318115}, {38.5877266, 38.7252655}, {38.5931816, 38.7199173}}
+{{38.5931816, 38.7199173}, {38.5880508, 38.7249527}, {38.5810318, 38.7318115}}
+</div>
+
+</div>
+
+<script type="text/javascript">
+
+    var testDivs = [
+        fuzz763_6411089,
+        fuzz763_378d,
+        fuzz763_378,
+        fuzz763_3084,
+        battle6001,
+        issue2753,
+        skpwww_9to5mac_com_64,
+        skpcarrot_is24x,
+        skpwww_wartepop_blogspot_com_br_6,
+        skpwww_wartepop_blogspot_com_br_6a,
+        skpwww_galaxystwo_com_4,
+        skpwww_kitcheninspirations_wordpress_com_66,
+        skpwww_tcmevents_org_13,
+        skpwww_aceinfographics_com_106,
+        skpwww_educationalcraft_com_4b,
+        skpwww_educationalcraft_com_4a,
+        skpwww_educationalcraft_com_4,
+        cubicLineErr2,
+        cubicLineErr1,
+        cubicLineMiss1,
+        cubicLineMiss2,
+        cubicLineMiss3,
+        cubicLineMiss4,
+        skpwww_pindosiya_com_99,
+        self1,
+        skpwww_seopack_blogspot_com_2153,
+        serp1,
+        serp2,
+        serp3,
+        loop1,
+        skpwww_maturesupertube_com_21,
+        skpwww_cityads_ru_249,
+        skpelpais_com_18,
+        testQuads60_b,
+        testQuads60_a,
+        testQuads60,
+        skpwww_dealnews_com_315_a,
+        skpwww_dealnews_com_315,
+        skpnamecheap_405,
+        slop1,
+        slop2,
+        slop3,
+        slop4,
+        slop5,
+        slop6,
+        slop7,
+        slop8,
+        slop9,
+        slop10,
+        slop11,
+        slop12,
+        slop13,
+        slop14,
+        slop15,
+        slop16,
+        slop17,
+        slop18,
+        slop19,
+        slop20,
+        skpcarrot_is24e,
+        skpcarrot_is24d,
+        skpcarrot_is24c,
+        skpcarrot_is24b,
+        skpcarrot_is24a,
+        skpcarrot_is24,
+        testQuads59,
+        testQuads45,
+        testQuads54,
+        findFirst1,
+        cubicOp59d,
+        testQuads22,
+        cubicOp85d,
+        cubicOp104,
+        skpnational_com_au81,
+        cubicOp35d,
+        testQuad23,
+        testQuad22,
+        testQuad21,
+        testQuad15,
+        testQuadratic56,
+        xop1i,
+        xOp2i,
+        xop1u,
+        skpwww_joomla_org_23,
+        cubicOp109,
+        cubicOp106,
+        cubicOp105,
+        carpetplanet1,
+        eldorado1,
+        cubicOp25i,
+        cubicOp7d,
+        simplifyQuadratic27,
+        simplifyQuadratic56a,
+        simplifyQuadratic56,
+        quadratic58again,
+        simplifyQuadratic58a,
+        simplifyQuadratic58,
+        simplifyQuadratic36,
+        quad89987,
+        quad8741,
+        quad8b,
+        quad8a,
+        quad208,
+        quad67242,
+        quad37226,
+        quad35237,
+        quad108,
+        quad212,
+        quad3160,
+        quad640,
+        quad206,
+        quad136,
+        quad113,
+        quad999,
+        quad0,
+        quad179,
+        quad4a,
+        quad94,
+        quad77,
+        quad22a,
+        quad14a,
+        quad809,
+        quad653,
+        quad584,
+        quad413,
+        quad379,
+        quad159,
+        quad232,
+        quad40,
+        quad39,
+        quad38,
+        quad37,
+        quad36,
+        quad35,
+        quad34,
+        quad33,
+        quad32,
+        quad31,
+        quad30,
+        quad29,
+        quad28,
+        quad27,
+        quad26,
+        quad25,
+        quad24,
+        quad23,
+        quad22,
+        quad21,
+        quad20,
+        quad19,
+        quad18,
+        quad17,
+        quad16,
+        quad15,
+        quad14,
+        quad13,
+        quad12,
+        quad11,
+        cubic2,
+        cubic1,
+        quad1,
+        quad2,
+        quad3,
+        quad4,
+        quad5,
+        quad6,
+        quad7,
+        quad8,
+        quad9,
+        quad10,
+    ];
 
     var tests = [];
     var testTitles = [];
     var testIndex = 0;
     var ctx;
-
     var subscale = 1;
     var xmin, xmax, ymin, ymax;
     var scale;
@@ -274,15 +1194,14 @@ sect36,
     var lastX, lastY;
     var activeCurve = [];
     var activePt;
-    var ids = [];
 
-    var focus_on_selection = 0;
+    var decimal_places = 3;
+
     var draw_t = false;
     var draw_closest_t = false;
     var draw_cubic_red = false;
     var draw_derivative = false;
-    var draw_endpoints = 2;
-    var draw_id = false;
+    var draw_endpoints = true;
     var draw_midpoint = 0;
     var draw_mouse_xy = false;
     var draw_order = false;
@@ -299,12 +1218,6 @@ sect36,
         var curves = [];
         for (var c in curveStrs) {
             var curveStr = curveStrs[c];
-            var idPart = curveStr.split("id=");
-            var id = -1;
-            if (idPart.length == 2) {
-                id = parseInt(idPart[1]);
-                curveStr = idPart[0];
-            }
             var points = curveStr.match(pattern);
             var pts = [];
             for (var wd in points) {
@@ -312,13 +1225,8 @@ sect36,
                 if (isNaN(num)) continue;
                 pts.push(num);
             }
-            if (pts.length > 2) {
+            if (pts.length > 2)
                 curves.push(pts);
-            }
-            if (id >= 0) {
-                ids.push(id);
-                ids.push(pts);
-            }
         }
         if (curves.length >= 1) {
             tests.push(curves);
@@ -356,7 +1264,7 @@ sect36,
                 ymax = Math.max(ymax, curve[idx + 1]);
             }
         }
-        xmin -= Math.min(1, Math.max(xmax - xmin, ymax - ymin));
+        xmin -= 1;
         var testW = xmax - xmin;
         var testH = ymax - ymin;
         subscale = 1;
@@ -888,15 +1796,11 @@ function dxy_at_t(curve, t) {
                 ctx.strokeStyle = "rgba(0,0,255, 1)";
             }
             ctx.stroke();
-            if (draw_endpoints > 0) {
+            if (draw_endpoints) {
                 drawPoint(curve[0], curve[1]);
-                if (draw_endpoints > 1 || curve.length == 4) {
-                    drawPoint(curve[2], curve[3]);
-                }
-                if (curve.length == 6 || (draw_endpoints > 1 && curve.length == 8)) {
-                    drawPoint(curve[4], curve[5]);
-                }
-                if (curve.length == 8) drawPoint(curve[6], curve[7]);
+                drawPoint(curve[2], curve[3]);
+                if (curve.length > 4) drawPoint(curve[4], curve[5]);
+                if (curve.length > 6) drawPoint(curve[6], curve[7]);
             }
             if (draw_midpoint != 0) {
                 if ((curves == 0) == (midLeft == 0)) {
@@ -998,32 +1902,6 @@ function dxy_at_t(curve, t) {
             if (draw_t) {
                 drawPointAtT(curve);
             }
-            if (draw_id) {
-                var id = -1;
-                for (var i = 0; i < ids.length; i += 2) {
-                    if (ids[i + 1] == curve) {
-                        id = ids[i];
-                        break;
-                    }
-                }
-                if (id >= 0) {
-                    var px = x_at_t(curve, 0.5);
-                    var py = y_at_t(curve, 0.5);
-                    var _px = (px - srcLeft) * scale;
-                    var _py = (py - srcTop) * scale;
-                    ctx.beginPath();
-                    ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
-                    ctx.closePath();
-                    ctx.fillStyle = "white";
-                    ctx.fill();
-                    ctx.strokeStyle = "rgba(255,0,0, 1)";
-                    ctx.fillStyle = "rgba(255,0,0, 1)";
-                    ctx.stroke();
-                    ctx.font = "normal 16px Arial";
-                    ctx.textAlign = "center";
-                    ctx.fillText(id, _px, _py + 5);
-                }
-            }
         }
         if (draw_t) {
             drawCurveTControl();
@@ -1076,28 +1954,6 @@ function dxy_at_t(curve, t) {
     }
 
     function redraw() {
-        if (focus_on_selection > 0) {
-            var focusXmin = focusYmin = Infinity;
-            var focusXmax = focusYmax = -Infinity;
-            var choice = 0;
-            for (var curves in tests[testIndex]) {
-                if (++choice != focus_on_selection) {
-                    continue;
-                }
-                var curve = tests[testIndex][curves];
-                var last = curve.length;
-                for (var idx = 0; idx < last; idx += 2) {
-                    focusXmin = Math.min(focusXmin, curve[idx]);
-                    focusXmax = Math.max(focusXmax, curve[idx]);
-                    focusYmin = Math.min(focusYmin, curve[idx + 1]);
-                    focusYmax = Math.max(focusYmax, curve[idx + 1]);
-                }
-            }
-            focusXmin -= Math.min(1, Math.max(focusXmax - focusXmin, focusYmax - focusYmin));
-            if (focusXmin < focusXmax && focusYmin < focusYmax) {
-                setScale(focusXmin, focusXmax, focusYmin, focusYmax);
-            }
-        }
         ctx.beginPath();
         ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
         ctx.fillStyle = "white";
@@ -1107,7 +1963,6 @@ function dxy_at_t(curve, t) {
 
     function doKeyPress(evt) {
         var char = String.fromCharCode(evt.charCode);
-        var focusWasOn = false;
         switch (char) {
             case '0':
             case '1':
@@ -1123,29 +1978,15 @@ function dxy_at_t(curve, t) {
                 redraw();
                 break;
             case '-':
-                focusWasOn = focus_on_selection;
-                if (focusWasOn) {
-                    focus_on_selection = false;
-                    scale /= 1.2;
-                } else {
-                    scale /= 2;
-                }
+                scale /= 2;
                 calcLeftTop();
                 redraw();
-                focus_on_selection = focusWasOn;
                 break;
             case '=':
             case '+':
-                focusWasOn = focus_on_selection;
-                if (focusWasOn) {
-                    focus_on_selection = false;
-                    scale *= 1.2;
-                } else {
-                    scale *= 2;
-                }
+                scale *= 2;
                 calcLeftTop();
                 redraw();
-                focus_on_selection = focusWasOn;
                 break;
             case 'b':
                 draw_cubic_red ^= true;
@@ -1171,7 +2012,7 @@ function dxy_at_t(curve, t) {
                 redraw();
                 break;
             case 'e':
-                draw_endpoints = (draw_endpoints + 1) % 3;
+                draw_endpoints ^= true;
                 redraw();
                 break;
             case 'f':
@@ -1267,18 +2108,6 @@ function dxy_at_t(curve, t) {
                 retina_scale ^= true;
                 drawTop();
                 break;
-            case '`':
-                ++focus_on_selection;
-                if (focus_on_selection >= tests[testIndex].length) {
-                    focus_on_selection = 0;
-                }
-                setScale(xmin, xmax, ymin, ymax);
-                redraw();
-                break;
-            case '.':
-                draw_id ^= true;
-                redraw();
-                break;
         }
     }
 
@@ -1292,11 +2121,7 @@ function dxy_at_t(curve, t) {
                 }
                 if (--testIndex < 0)
                     testIndex = tests.length - 1;
-                if (evt.ctrlKey) {
-                    redraw();
-                } else {
-                    drawTop();
-                }
+                drawTop();
                 preventDefault = true;
                 break;
             case 39: // right arrow
@@ -1305,11 +2130,7 @@ function dxy_at_t(curve, t) {
                 }
                 if (++testIndex >= tests.length)
                     testIndex = 0;
-                if (evt.ctrlKey) {
-                    redraw();
-                } else {
-                    drawTop();
-                }
+                drawTop();
                 preventDefault = true;
                 break;
         }
index 0ca4637ea1f45625b3f1e6a0f136ec861ebe1028..f5482fd1f19ad7269feb2a84f93cd38353a0a077 100644 (file)
 <html>
 <head>
 <div height="0" hidden="true">
-
-<div id="skpwww_educationalcraft_com_4">
-seg=1 {{{941, 1494}, {941, 1464}}}
-seg=2 {{{941, 1464}, {985, 1464}}}
-seg=3 {{{985, 1464}, {985, 1494}}}
-seg=4 {{{985, 1494}, {941, 1494}}}
-op sect
-seg=5 {{{979.211975f, 1480.45496f}, {979.211975f, 1480.45496f}, {976.348999f, 1479.68506f}, {977.495972f, 1475.59497f}}}
-seg=6 {{{977.495972f, 1475.59497f}, {977.496033f, 1475.59497f}, {977.503296f, 1475.59961f}, {977.517029f, 1475.60864f}}}
-seg=7 {{{977.517029f, 1475.60864f}, {977.807861f, 1475.80164f}, {980.988281f, 1478.00073f}, {979.211975f, 1480.45496f}}}
-seg=8 {{{977.854004f, 1484.453f}, {977.854004f, 1484.453f}, {975.265991f, 1483.26099f}, {976.713989f, 1479.35205f}}}
-seg=9 {{{976.713989f, 1479.35205f}, {976.713989f, 1479.35205f}, {976.714722f, 1479.35278f}, {976.716125f, 1479.35413f}}}
-seg=10 {{{976.716125f, 1479.35413f}, {976.807983f, 1479.44055f}, {979.811707f, 1482.26868f}, {977.854004f, 1484.453f}}}
-seg=11 {{{980.226013f, 1476.229f}, {980.226013f, 1476.229f}, {977.078003f, 1476.349f}, {977.234985f, 1471.97095f}}}
-seg=12 {{{977.234985f, 1471.97095f}, {977.234985f, 1471.97095f}, {980.666992f, 1473.12903f}, {980.226013f, 1476.229f}}}
-seg=13 {{{984.546021f, 1478.31494f}, {984.546021f, 1478.31494f}, {983.187988f, 1481.93396f}, {980.026001f, 1481.276f}}}
-seg=14 {{{980.026001f, 1481.276f}, {980.026001f, 1481.276f}, {980.02594f, 1481.27551f}, {980.025818f, 1481.27441f}}}
-seg=15 {{{980.025818f, 1481.27441f}, {980.014954f, 1481.1969f}, {979.623779f, 1478.38806f}, {984.546021f, 1478.31494f}}}
-seg=16 {{{978.989014f, 1484.198f}, {978.989014f, 1484.198f}, {979.094971f, 1481.33496f}, {983.786011f, 1481.823f}}}
-seg=17 {{{983.786011f, 1481.823f}, {983.786011f, 1481.823f}, {982.070007f, 1485.49805f}, {978.989014f, 1484.198f}}}
-seg=18 {{{976.393005f, 1486.86804f}, {976.393005f, 1486.86804f}, {976.719971f, 1484.06494f}, {981.679016f, 1485.37f}}}
-seg=19 {{{981.679016f, 1485.37f}, {981.679016f, 1485.37f}, {979.169983f, 1488.40796f}, {976.393005f, 1486.86804f}}}
-seg=20 {{{969.156982f, 1490.40002f}, {969.156982f, 1490.40002f}, {971.478027f, 1488.23596f}, {974.869995f, 1491.21399f}}}
-seg=21 {{{974.869995f, 1491.21399f}, {974.869995f, 1491.21399f}, {974.857788f, 1491.21948f}, {974.834473f, 1491.22937f}}}
-seg=22 {{{974.834473f, 1491.22937f}, {974.433289f, 1491.40051f}, {970.736267f, 1492.88184f}, {969.156982f, 1490.40002f}}}
-seg=23 {{{972.825012f, 1483.93701f}, {972.825012f, 1483.93701f}, {973.971985f, 1487.98401f}, {971.161987f, 1488.94604f}}}
-seg=24 {{{971.161987f, 1488.94604f}, {971.161987f, 1488.94592f}, {971.154663f, 1488.93591f}, {971.141846f, 1488.9165f}}}
-seg=25 {{{971.141846f, 1488.9165f}, {970.948425f, 1488.625f}, {969.49884f, 1486.21948f}, {972.825012f, 1483.93701f}}}
-seg=26 {{{965.60199f, 1489.98499f}, {965.60199f, 1489.98499f}, {964.879028f, 1487.19202f}, {969.864014f, 1486.75f}}}
-seg=27 {{{969.864014f, 1486.75f}, {969.864014f, 1486.75f}, {968.749023f, 1490.672f}, {965.60199f, 1489.98499f}}}
-seg=28 {{{970.666992f, 1492.81604f}, {970.666992f, 1492.81604f}, {967.327026f, 1494.49695f}, {964.999023f, 1491.56299f}}}
-seg=29 {{{964.999023f, 1491.56299f}, {964.999023f, 1491.56299f}, {967.304016f, 1489.43896f}, {970.666992f, 1492.81604f}}}
-seg=30 {{{968.343994f, 1481.53796f}, {971.466064f, 1480.00305f}, {971.676941f, 1476.99573f}, {971.6875f, 1476.79639f}}}
-seg=31 {{{971.6875f, 1476.79639f}, {971.687866f, 1476.78955f}, {971.687988f, 1476.78601f}, {971.687988f, 1476.78601f}}}
-seg=32 {{{971.687988f, 1476.78601f}, {971.393982f, 1466.83398f}}}
-seg=33 {{{971.393982f, 1466.83398f}, {954.960999f, 1466.83398f}}}
-seg=34 {{{954.960999f, 1466.83398f}, {954.666016f, 1476.78601f}}}
-seg=35 {{{954.666016f, 1476.78601f}, {954.666016f, 1476.78601f}, {954.780029f, 1479.94995f}, {958.008972f, 1481.53796f}}}
-seg=36 {{{958.008972f, 1481.53796f}, {960.369873f, 1482.70056f}, {961.725403f, 1484.2323f}, {962.0755f, 1484.66101f}}}
-seg=37 {{{962.0755f, 1484.66101f}, {962.136475f, 1484.73572f}, {962.166992f, 1484.77698f}, {962.166992f, 1484.77698f}}}
-seg=38 {{{962.166992f, 1484.77698f}, {962.166992f, 1484.77698f}, {962.747986f, 1485.70105f}, {963.177979f, 1485.70105f}}}
-seg=39 {{{963.177979f, 1485.70105f}, {963.606995f, 1485.70105f}, {964.185974f, 1484.77698f}, {964.185974f, 1484.77698f}}}
-seg=40 {{{964.185974f, 1484.77698f}, {964.185974f, 1484.77698f}, {965.573975f, 1482.90295f}, {968.343994f, 1481.53796f}}}
-seg=41 {{{963.215027f, 1486.67004f}, {962.744995f, 1486.67004f}, {962.106995f, 1485.65405f}, {962.106995f, 1485.65405f}}}
-seg=42 {{{962.106995f, 1485.65405f}, {962.106995f, 1485.65405f}, {960.585022f, 1483.59595f}, {957.539001f, 1482.09705f}}}
-seg=43 {{{957.539001f, 1482.09705f}, {954.255432f, 1480.48206f}, {953.90448f, 1477.3844f}, {953.870422f, 1476.93176f}}}
-seg=44 {{{953.870422f, 1476.93176f}, {953.867676f, 1476.89526f}, {953.867004f, 1476.87598f}, {953.867004f, 1476.87598f}}}
-seg=45 {{{953.867004f, 1476.87598f}, {954.190002f, 1465.94397f}}}
-seg=46 {{{954.190002f, 1465.94397f}, {972.23999f, 1465.94397f}}}
-seg=47 {{{972.23999f, 1465.94397f}, {972.565002f, 1476.87695f}}}
-seg=48 {{{972.565002f, 1476.87695f}, {972.565002f, 1476.87695f}, {972.440979f, 1480.35303f}, {968.891968f, 1482.09802f}}}
-seg=49 {{{968.891968f, 1482.09802f}, {966.255737f, 1483.39539f}, {964.76178f, 1485.11145f}, {964.407593f, 1485.54968f}}}
-seg=50 {{{964.407593f, 1485.54968f}, {964.352539f, 1485.6178f}, {964.325012f, 1485.65503f}, {964.325012f, 1485.65503f}}}
-seg=51 {{{964.325012f, 1485.65503f}, {964.325012f, 1485.65503f}, {963.687012f, 1486.67004f}, {963.215027f, 1486.67004f}}}
-seg=52 {{{960.68103f, 1489.98499f}, {957.533997f, 1490.672f}, {956.417969f, 1486.75f}, {956.417969f, 1486.75f}}}
-seg=53 {{{956.417969f, 1486.75f}, {961.403015f, 1487.19202f}, {960.68103f, 1489.98499f}, {960.68103f, 1489.98499f}}}
-seg=54 {{{963.143005f, 1489.59802f}, {963.763f, 1489.59802f}, {964.265015f, 1490.09998f}, {964.265015f, 1490.72095f}}}
-seg=55 {{{964.265015f, 1490.72095f}, {964.265015f, 1491.34204f}, {963.763f, 1491.84399f}, {963.143005f, 1491.84399f}}}
-seg=56 {{{963.143005f, 1491.84399f}, {962.521973f, 1491.84399f}, {962.02002f, 1491.34204f}, {962.02002f, 1490.72095f}}}
-seg=57 {{{962.02002f, 1490.72095f}, {962.02002f, 1490.09998f}, {962.521973f, 1489.59802f}, {963.143005f, 1489.59802f}}}
-seg=58 {{{961.283997f, 1491.56299f}, {958.953979f, 1494.49695f}, {955.61499f, 1492.81604f}, {955.61499f, 1492.81604f}}}
-seg=59 {{{955.61499f, 1492.81604f}, {958.695923f, 1489.72131f}, {960.89093f, 1491.24622f}, {961.236389f, 1491.52283f}}}
-seg=60 {{{961.236389f, 1491.52283f}, {961.267883f, 1491.5481f}, {961.283997f, 1491.56299f}, {961.283997f, 1491.56299f}}}
-seg=61 {{{957.127014f, 1490.40002f}, {955.541504f, 1492.89014f}, {951.825745f, 1491.38965f}, {951.445557f, 1491.22766f}}}
-seg=62 {{{951.445557f, 1491.22766f}, {951.424805f, 1491.21887f}, {951.414001f, 1491.21399f}, {951.414001f, 1491.21399f}}}
-seg=63 {{{951.414001f, 1491.21399f}, {954.694214f, 1488.33154f}, {956.976746f, 1490.26636f}, {957.119873f, 1490.39355f}}}
-seg=64 {{{957.119873f, 1490.39355f}, {957.124634f, 1490.39783f}, {957.127014f, 1490.40002f}, {957.127014f, 1490.40002f}}}
-seg=65 {{{949.890991f, 1486.86804f}, {947.178772f, 1488.37146f}, {944.723022f, 1485.51147f}, {944.608215f, 1485.375f}}}
-seg=66 {{{944.608215f, 1485.375f}, {944.605408f, 1485.3717f}, {944.604004f, 1485.37f}, {944.604004f, 1485.37f}}}
-seg=67 {{{944.604004f, 1485.37f}, {949.562012f, 1484.06494f}, {949.890991f, 1486.86804f}, {949.890991f, 1486.86804f}}}
-seg=68 {{{947.070984f, 1480.45496f}, {945.211975f, 1477.88501f}, {948.786011f, 1475.59497f}, {948.786011f, 1475.59497f}}}
-seg=69 {{{948.786011f, 1475.59497f}, {949.835938f, 1479.33569f}, {947.530884f, 1480.29919f}, {947.129333f, 1480.43652f}}}
-seg=70 {{{947.129333f, 1480.43652f}, {947.091858f, 1480.44934f}, {947.070984f, 1480.45496f}, {947.070984f, 1480.45496f}}}
-seg=71 {{{946.054016f, 1476.229f}, {945.61499f, 1473.12903f}, {949.046997f, 1471.97095f}, {949.046997f, 1471.97095f}}}
-seg=72 {{{949.046997f, 1471.97095f}, {949.191528f, 1475.95117f}, {946.599548f, 1476.21362f}, {946.127258f, 1476.22852f}}}
-seg=73 {{{946.127258f, 1476.22852f}, {946.080078f, 1476.22998f}, {946.054016f, 1476.229f}, {946.054016f, 1476.229f}}}
-seg=74 {{{948.427002f, 1484.453f}, {946.440002f, 1482.23499f}, {949.567993f, 1479.35205f}, {949.567993f, 1479.35205f}}}
-seg=75 {{{949.567993f, 1479.35205f}, {951.015991f, 1483.26099f}, {948.427002f, 1484.453f}, {948.427002f, 1484.453f}}}
-seg=76 {{{947.294006f, 1484.198f}, {944.210999f, 1485.49805f}, {942.495972f, 1481.823f}, {942.495972f, 1481.823f}}}
-seg=77 {{{942.495972f, 1481.823f}, {947.187988f, 1481.33496f}, {947.294006f, 1484.198f}, {947.294006f, 1484.198f}}}
-seg=78 {{{946.255005f, 1481.276f}, {943.094971f, 1481.93396f}, {941.736023f, 1478.31494f}, {941.736023f, 1478.31494f}}}
-seg=79 {{{941.736023f, 1478.31494f}, {946.484619f, 1478.38538f}, {946.288147f, 1481.00122f}, {946.25769f, 1481.2561f}}}
-seg=80 {{{946.25769f, 1481.2561f}, {946.256104f, 1481.26917f}, {946.255005f, 1481.276f}, {946.255005f, 1481.276f}}}
-seg=81 {{{945.312988f, 1478.18005f}, {942.359741f, 1477.83667f}, {942.572632f, 1474.58496f}, {942.638794f, 1473.97607f}}}
-seg=82 {{{942.638794f, 1473.97607f}, {942.645691f, 1473.91284f}, {942.651001f, 1473.87805f}, {942.651001f, 1473.87805f}}}
-seg=83 {{{942.651001f, 1473.87805f}, {946.562988f, 1475.66199f}, {945.312988f, 1478.18005f}, {945.312988f, 1478.18005f}}}
-seg=84 {{{945.382019f, 1474.328f}, {942.924011f, 1472.729f}, {944.492004f, 1469.48706f}, {944.492004f, 1469.48706f}}}
-seg=85 {{{944.492004f, 1469.48706f}, {947.388977f, 1471.95703f}, {945.382019f, 1474.328f}, {945.382019f, 1474.328f}}}
-seg=86 {{{946.797974f, 1470.27405f}, {944.819641f, 1468.07397f}, {946.75708f, 1465.85327f}, {947.048523f, 1465.54285f}}}
-seg=87 {{{947.048523f, 1465.54285f}, {947.071289f, 1465.51855f}, {947.083984f, 1465.50598f}, {947.083984f, 1465.50598f}}}
-seg=88 {{{947.083984f, 1465.50598f}, {949.145996f, 1468.82605f}, {946.797974f, 1470.27405f}, {946.797974f, 1470.27405f}}}
-seg=89 {{{947.392029f, 1471.64197f}, {947.604919f, 1468.81628f}, {950.769897f, 1468.35559f}, {951.289185f, 1468.29895f}}}
-seg=90 {{{951.289185f, 1468.29895f}, {951.335754f, 1468.29382f}, {951.361023f, 1468.29199f}, {951.361023f, 1468.29199f}}}
-seg=91 {{{951.361023f, 1468.29199f}, {950.554016f, 1471.98499f}, {947.392029f, 1471.64197f}, {947.392029f, 1471.64197f}}}
-seg=92 {{{948.64801f, 1468.15002f}, {948.638977f, 1465.22095f}, {952.265991f, 1464.46399f}, {952.265991f, 1464.46399f}}}
-seg=93 {{{952.265991f, 1464.46399f}, {951.707275f, 1468.29565f}, {948.98999f, 1468.17932f}, {948.677368f, 1468.15283f}}}
-seg=94 {{{948.677368f, 1468.15283f}, {948.658142f, 1468.15125f}, {948.64801f, 1468.15002f}, {948.64801f, 1468.15002f}}}
-seg=95 {{{951.176025f, 1486.97803f}, {949.194519f, 1484.8667f}, {950.909729f, 1482.36658f}, {951.290283f, 1481.86658f}}}
-seg=96 {{{951.290283f, 1481.86658f}, {951.334778f, 1481.80811f}, {951.361023f, 1481.77698f}, {951.361023f, 1481.77698f}}}
-seg=97 {{{951.361023f, 1481.77698f}, {953.644836f, 1485.34509f}, {951.363281f, 1486.86157f}, {951.186646f, 1486.97144f}}}
-seg=98 {{{951.186646f, 1486.97144f}, {951.179688f, 1486.97583f}, {951.176025f, 1486.97803f}, {951.176025f, 1486.97803f}}}
-seg=99 {{{947.51001f, 1488.53101f}, {947.51001f, 1488.53101f}, {951.596985f, 1486.32202f}, {953.234009f, 1489.08997f}}}
-seg=100 {{{953.234009f, 1489.08997f}, {953.234009f, 1489.08997f}, {951.158997f, 1491.03601f}, {947.51001f, 1488.53101f}}}
-seg=101 {{{955.120972f, 1488.94495f}, {952.309021f, 1487.98303f}, {953.458984f, 1483.93604f}, {953.458984f, 1483.93604f}}}
-seg=102 {{{953.458984f, 1483.93604f}, {957.004028f, 1486.37097f}, {955.120972f, 1488.94495f}, {955.120972f, 1488.94495f}}}
-seg=103 {{{978.770996f, 1488.53101f}, {975.204224f, 1490.98022f}, {973.141174f, 1489.17444f}, {973.051086f, 1489.09277f}}}
-seg=104 {{{973.051086f, 1489.09277f}, {973.049011f, 1489.09094f}, {973.047974f, 1489.08997f}, {973.047974f, 1489.08997f}}}
-seg=105 {{{973.047974f, 1489.08997f}, {974.651978f, 1486.37781f}, {978.607178f, 1488.44397f}, {978.766052f, 1488.52844f}}}
-seg=106 {{{978.766052f, 1488.52844f}, {978.770996f, 1488.53101f}}}
-seg=107 {{{975.106995f, 1486.97803f}, {975.106995f, 1486.97803f}, {972.546997f, 1485.48706f}, {974.919983f, 1481.77698f}}}
-seg=108 {{{974.919983f, 1481.77698f}, {974.919983f, 1481.776f}, {977.31897f, 1484.61902f}, {975.106995f, 1486.97803f}}}
-seg=109 {{{974.016968f, 1464.46399f}, {974.016968f, 1464.46399f}, {977.643982f, 1465.22095f}, {977.633972f, 1468.15002f}}}
-seg=110 {{{977.633972f, 1468.15002f}, {977.633972f, 1468.15002f}, {974.611023f, 1468.53101f}, {974.016968f, 1464.46399f}}}
-seg=111 {{{974.919983f, 1468.29199f}, {974.919983f, 1468.29199f}, {978.658997f, 1468.56299f}, {978.890015f, 1471.64197f}}}
-seg=112 {{{978.890015f, 1471.64197f}, {978.890015f, 1471.64197f}, {975.72699f, 1471.98499f}, {974.919983f, 1468.29199f}}}
-seg=113 {{{979.197998f, 1465.50598f}, {979.197998f, 1465.50598f}, {981.619019f, 1467.90198f}, {979.481995f, 1470.27405f}}}
-seg=114 {{{979.481995f, 1470.27405f}, {979.481995f, 1470.27405f}, {977.138f, 1468.82605f}, {979.197998f, 1465.50598f}}}
-seg=115 {{{980.900024f, 1474.328f}, {980.900024f, 1474.328f}, {978.893005f, 1471.95703f}, {981.791016f, 1469.48706f}}}
-seg=116 {{{981.791016f, 1469.48706f}, {981.791016f, 1469.48596f}, {983.358032f, 1472.729f}, {980.900024f, 1474.328f}}}
-seg=117 {{{980.968994f, 1478.18005f}, {980.968994f, 1478.18005f}, {979.718018f, 1475.66199f}, {983.632019f, 1473.87805f}}}
-seg=118 {{{983.632019f, 1473.87805f}, {983.632019f, 1473.87805f}, {984.229004f, 1477.80103f}, {980.968994f, 1478.18005f}}}
-debugShowLineIntersection wtTs[0]=0 {{{941,1464}, {985,1464}}} {{941,1464}} wnTs[0]=1 {{{941,1494}, {941,1464}}}
-debugShowLineIntersection wtTs[0]=1 {{{985,1494}, {941,1494}}} {{941,1494}} wnTs[0]=0 {{{941,1494}, {941,1464}}}
-debugShowLineIntersection wtTs[0]=0 {{{985,1464}, {985,1494}}} {{985,1464}} wnTs[0]=1 {{{941,1464}, {985,1464}}}
-debugShowLineIntersection wtTs[0]=0 {{{985,1494}, {941,1494}}} {{985,1494}} wnTs[0]=1 {{{985,1464}, {985,1494}}}
-debugShowCubicIntersection wtTs[0]=1 {{{948.64801,1468.15002}, {948.638977,1465.22095}, {952.265991,1464.46399}, {952.265991,1464.46399}}} {{952.265991,1464.46399}} wnTs[0]=0 {{{952.265991,1464.46399}, {951.707275,1468.29565}, {948.98999,1468.17932}, {948.677368,1468.15283}}}
-debugShowCubicIntersection wtTs[0]=0 {{{948.64801,1468.15002}, {948.638977,1465.22095}, {952.265991,1464.46399}, {952.265991,1464.46399}}} {{948.64801,1468.15002}} wnTs[0]=1 {{{948.677368,1468.15283}, {948.658142,1468.15125}, {948.64801,1468.15002}, {948.64801,1468.15002}}}
-debugShowCubicIntersection wtTs[0]=1 {{{952.265991,1464.46399}, {951.707275,1468.29565}, {948.98999,1468.17932}, {948.677368,1468.15283}}} {{948.677368,1468.15283}} wnTs[0]=0 {{{948.677368,1468.15283}, {948.658142,1468.15125}, {948.64801,1468.15002}, {948.64801,1468.15002}}}
-debugShowCubicIntersection wtTs[0]=0 {{{974.016968,1464.46399}, {974.016968,1464.46399}, {977.643982,1465.22095}, {977.633972,1468.15002}}} {{974.016968,1464.46399}} wtTs[1]=1 {{977.633972,1468.15002}} wnTs[0]=1 {{{977.633972,1468.15002}, {977.633972,1468.15002}, {974.611023,1468.53101}, {974.016968,1464.46399}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=1 {{{946.797974,1470.27405}, {944.819641,1468.07397}, {946.75708,1465.85327}, {947.048523,1465.54285}}} {{947.048523,1465.54285}} wnTs[0]=0 {{{947.048523,1465.54285}, {947.071289,1465.51855}, {947.083984,1465.50598}, {947.083984,1465.50598}}}
-debugShowCubicIntersection wtTs[0]=0 {{{946.797974,1470.27405}, {944.819641,1468.07397}, {946.75708,1465.85327}, {947.048523,1465.54285}}} {{946.797974,1470.27405}} wnTs[0]=1 {{{947.083984,1465.50598}, {949.145996,1468.82605}, {946.797974,1470.27405}, {946.797974,1470.27405}}}
-debugShowCubicIntersection wtTs[0]=1 {{{947.048523,1465.54285}, {947.071289,1465.51855}, {947.083984,1465.50598}, {947.083984,1465.50598}}} {{947.083984,1465.50598}} wnTs[0]=0 {{{947.083984,1465.50598}, {949.145996,1468.82605}, {946.797974,1470.27405}, {946.797974,1470.27405}}}
-1 id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [1,3]
-2 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{947.083984,1465.50598}, {949.145996,1468.82605}, {946.797974,1470.27405}, {946.797974,1470.27405}}} {{{947.392029,1471.64197}, {947.604919,1468.81628}, {950.769897,1468.35559}, {951.289185,1468.29895}}}
-debugShowCubicIntersection no intersect {{{947.083984,1465.50598}, {949.145996,1468.82605}, {946.797974,1470.27405}, {946.797974,1470.27405}}} {{{951.361023,1468.29199}, {950.554016,1471.98499}, {947.392029,1471.64197}, {947.392029,1471.64197}}}
-debugShowCubicIntersection no intersect {{{946.797974,1470.27405}, {944.819641,1468.07397}, {946.75708,1465.85327}, {947.048523,1465.54285}}} {{{944.492004,1469.48706}, {947.388977,1471.95703}, {945.382019,1474.328}, {945.382019,1474.328}}}
-debugShowCubicIntersection wtTs[0]=0 {{{979.197998,1465.50598}, {979.197998,1465.50598}, {981.619019,1467.90198}, {979.481995,1470.27405}}} {{979.197998,1465.50598}} wtTs[1]=1 {{979.481995,1470.27405}} wnTs[0]=1 {{{979.481995,1470.27405}, {979.481995,1470.27405}, {977.138,1468.82605}, {979.197998,1465.50598}}} wnTs[1]=0
-3 id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [1,3]
-4 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{979.481995,1470.27405}, {979.481995,1470.27405}, {977.138,1468.82605}, {979.197998,1465.50598}}} {{{974.919983,1468.29199}, {974.919983,1468.29199}, {978.658997,1468.56299}, {978.890015,1471.64197}}}
-debugShowCubicIntersection no intersect {{{979.481995,1470.27405}, {979.481995,1470.27405}, {977.138,1468.82605}, {979.197998,1465.50598}}} {{{978.890015,1471.64197}, {978.890015,1471.64197}, {975.72699,1471.98499}, {974.919983,1468.29199}}}
-debugShowCubicIntersection no intersect {{{979.197998,1465.50598}, {979.197998,1465.50598}, {981.619019,1467.90198}, {979.481995,1470.27405}}} {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}}
-debugShowCubicIntersection wtTs[0]=1 {{{963.215027,1486.67004}, {962.744995,1486.67004}, {962.106995,1485.65405}, {962.106995,1485.65405}}} {{962.106995,1485.65405}} wnTs[0]=0 {{{962.106995,1485.65405}, {962.106995,1485.65405}, {960.585022,1483.59595}, {957.539001,1482.09705}}}
-debugShowCubicIntersection wtTs[0]=0 {{{963.215027,1486.67004}, {962.744995,1486.67004}, {962.106995,1485.65405}, {962.106995,1485.65405}}} {{963.215027,1486.67004}} wnTs[0]=1 {{{964.325012,1485.65503}, {964.325012,1485.65503}, {963.687012,1486.67004}, {963.215027,1486.67004}}}
-debugShowCubicIntersection wtTs[0]=1 {{{962.106995,1485.65405}, {962.106995,1485.65405}, {960.585022,1483.59595}, {957.539001,1482.09705}}} {{957.539001,1482.09705}} wnTs[0]=0 {{{957.539001,1482.09705}, {954.255432,1480.48206}, {953.90448,1477.3844}, {953.870422,1476.93176}}}
-debugShowCubicIntersection wtTs[0]=1 {{{957.539001,1482.09705}, {954.255432,1480.48206}, {953.90448,1477.3844}, {953.870422,1476.93176}}} {{953.870422,1476.93176}} wnTs[0]=0 {{{953.870422,1476.93176}, {953.867676,1476.89526}, {953.867004,1476.87598}, {953.867004,1476.87598}}}
-debugShowCubicLineIntersection wtTs[0]=1 {{{953.870422,1476.93176}, {953.867676,1476.89526}, {953.867004,1476.87598}, {953.867004,1476.87598}}} {{953.867004,1476.87598}} wnTs[0]=0 {{{953.867004,1476.87598}, {954.190002,1465.94397}}}
-debugShowLineIntersection wtTs[0]=1 {{{953.867004,1476.87598}, {954.190002,1465.94397}}} {{954.190002,1465.94397}} wnTs[0]=0 {{{954.190002,1465.94397}, {972.23999,1465.94397}}}
-debugShowLineIntersection wtTs[0]=0 {{{972.23999,1465.94397}, {972.565002,1476.87695}}} {{972.23999,1465.94397}} wnTs[0]=1 {{{954.190002,1465.94397}, {972.23999,1465.94397}}}
-debugShowCubicLineIntersection wtTs[0]=0 {{{972.565002,1476.87695}, {972.565002,1476.87695}, {972.440979,1480.35303}, {968.891968,1482.09802}}} {{972.565002,1476.87695}} wnTs[0]=1 {{{972.23999,1465.94397}, {972.565002,1476.87695}}}
-debugShowCubicIntersection wtTs[0]=1 {{{972.565002,1476.87695}, {972.565002,1476.87695}, {972.440979,1480.35303}, {968.891968,1482.09802}}} {{968.891968,1482.09802}} wnTs[0]=0 {{{968.891968,1482.09802}, {966.255737,1483.39539}, {964.76178,1485.11145}, {964.407593,1485.54968}}}
-debugShowCubicIntersection wtTs[0]=1 {{{968.891968,1482.09802}, {966.255737,1483.39539}, {964.76178,1485.11145}, {964.407593,1485.54968}}} {{964.407593,1485.54968}} wnTs[0]=0 {{{964.407593,1485.54968}, {964.352539,1485.6178}, {964.325012,1485.65503}, {964.325012,1485.65503}}}
-5 id=1 1=(0,1) [2] id=2 2=(0,0) [1]
-6 id=1 3=(1,1) [2] id=2 2=(0,0) [3]
-debugShowCubicIntersection wtTs[0]=1 {{{964.407593,1485.54968}, {964.352539,1485.6178}, {964.325012,1485.65503}, {964.325012,1485.65503}}} {{964.325012,1485.65503}} wnTs[0]=0 {{{964.325012,1485.65503}, {964.325012,1485.65503}, {963.687012,1486.67004}, {963.215027,1486.67004}}}
-debugShowCubicIntersection no intersect {{{963.215027,1486.67004}, {962.744995,1486.67004}, {962.106995,1485.65405}, {962.106995,1485.65405}}} {{{962.166992,1484.77698}, {962.166992,1484.77698}, {962.747986,1485.70105}, {963.177979,1485.70105}}}
-debugShowCubicIntersection no intersect {{{963.215027,1486.67004}, {962.744995,1486.67004}, {962.106995,1485.65405}, {962.106995,1485.65405}}} {{{963.177979,1485.70105}, {963.606995,1485.70105}, {964.185974,1484.77698}, {964.185974,1484.77698}}}
-7 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{962.106995,1485.65405}, {962.106995,1485.65405}, {960.585022,1483.59595}, {957.539001,1482.09705}}} {{{958.008972,1481.53796}, {960.369873,1482.70056}, {961.725403,1484.2323}, {962.0755,1484.66101}}}
-debugShowCubicIntersection no intersect {{{962.106995,1485.65405}, {962.106995,1485.65405}, {960.585022,1483.59595}, {957.539001,1482.09705}}} {{{962.0755,1484.66101}, {962.136475,1484.73572}, {962.166992,1484.77698}, {962.166992,1484.77698}}}
-8 id=1 1=(0,0.5) [2] id=2 2=(0,1) [1]
-9 id=1 1=(0,0.5) [4] id=2 4=(0.5,1) [1]
-10 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{957.539001,1482.09705}, {954.255432,1480.48206}, {953.90448,1477.3844}, {953.870422,1476.93176}}} {{{954.666016,1476.78601}, {954.666016,1476.78601}, {954.780029,1479.94995}, {958.008972,1481.53796}}}
-11 id=1 3=(0.5,1) [2] id=2 2=(0,1) [3]
-12 id=1 3=(0.5,1) [2] id=2 2=(0,0.5) [3]
-13 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{972.565002,1476.87695}, {972.565002,1476.87695}, {972.440979,1480.35303}, {968.891968,1482.09802}}} {{{968.343994,1481.53796}, {971.466064,1480.00305}, {971.676941,1476.99573}, {971.6875,1476.79639}}}
-14 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{968.891968,1482.09802}, {966.255737,1483.39539}, {964.76178,1485.11145}, {964.407593,1485.54968}}} {{{964.185974,1484.77698}, {964.185974,1484.77698}, {965.573975,1482.90295}, {968.343994,1481.53796}}}
-debugShowCubicIntersection no intersect {{{964.325012,1485.65503}, {964.325012,1485.65503}, {963.687012,1486.67004}, {963.215027,1486.67004}}} {{{963.177979,1485.70105}, {963.606995,1485.70105}, {964.185974,1484.77698}, {964.185974,1484.77698}}}
-debugShowCubicIntersection wtTs[0]=1 {{{968.343994,1481.53796}, {971.466064,1480.00305}, {971.676941,1476.99573}, {971.6875,1476.79639}}} {{971.6875,1476.79639}} wnTs[0]=0 {{{971.6875,1476.79639}, {971.687866,1476.78955}, {971.687988,1476.78601}, {971.687988,1476.78601}}}
-debugShowCubicIntersection wtTs[0]=0 {{{968.343994,1481.53796}, {971.466064,1480.00305}, {971.676941,1476.99573}, {971.6875,1476.79639}}} {{968.343994,1481.53796}} wnTs[0]=1 {{{964.185974,1484.77698}, {964.185974,1484.77698}, {965.573975,1482.90295}, {968.343994,1481.53796}}}
-debugShowCubicLineIntersection wtTs[0]=1 {{{971.6875,1476.79639}, {971.687866,1476.78955}, {971.687988,1476.78601}, {971.687988,1476.78601}}} {{971.687988,1476.78601}} wnTs[0]=0 {{{971.687988,1476.78601}, {971.393982,1466.83398}}}
-debugShowLineIntersection wtTs[0]=1 {{{971.687988,1476.78601}, {971.393982,1466.83398}}} {{971.393982,1466.83398}} wnTs[0]=0 {{{971.393982,1466.83398}, {954.960999,1466.83398}}}
-debugShowLineIntersection wtTs[0]=0 {{{954.960999,1466.83398}, {954.666016,1476.78601}}} {{954.960999,1466.83398}} wnTs[0]=1 {{{971.393982,1466.83398}, {954.960999,1466.83398}}}
-debugShowCubicLineIntersection wtTs[0]=0 {{{954.666016,1476.78601}, {954.666016,1476.78601}, {954.780029,1479.94995}, {958.008972,1481.53796}}} {{954.666016,1476.78601}} wnTs[0]=1 {{{954.960999,1466.83398}, {954.666016,1476.78601}}}
-debugShowCubicIntersection wtTs[0]=1 {{{954.666016,1476.78601}, {954.666016,1476.78601}, {954.780029,1479.94995}, {958.008972,1481.53796}}} {{958.008972,1481.53796}} wnTs[0]=0 {{{958.008972,1481.53796}, {960.369873,1482.70056}, {961.725403,1484.2323}, {962.0755,1484.66101}}}
-debugShowCubicIntersection wtTs[0]=1 {{{958.008972,1481.53796}, {960.369873,1482.70056}, {961.725403,1484.2323}, {962.0755,1484.66101}}} {{962.0755,1484.66101}} wnTs[0]=0 {{{962.0755,1484.66101}, {962.136475,1484.73572}, {962.166992,1484.77698}, {962.166992,1484.77698}}}
-15 id=1 1=(0,1) [2] id=2 2=(0,0) [1]
-16 id=1 3=(1,1) [2] id=2 2=(0,0) [3]
-debugShowCubicIntersection wtTs[0]=1 {{{962.0755,1484.66101}, {962.136475,1484.73572}, {962.166992,1484.77698}, {962.166992,1484.77698}}} {{962.166992,1484.77698}} wnTs[0]=0 {{{962.166992,1484.77698}, {962.166992,1484.77698}, {962.747986,1485.70105}, {963.177979,1485.70105}}}
-debugShowCubicIntersection wtTs[0]=1 {{{962.166992,1484.77698}, {962.166992,1484.77698}, {962.747986,1485.70105}, {963.177979,1485.70105}}} {{963.177979,1485.70105}} wnTs[0]=0 {{{963.177979,1485.70105}, {963.606995,1485.70105}, {964.185974,1484.77698}, {964.185974,1484.77698}}}
-debugShowCubicIntersection wtTs[0]=1 {{{963.177979,1485.70105}, {963.606995,1485.70105}, {964.185974,1484.77698}, {964.185974,1484.77698}}} {{964.185974,1484.77698}} wnTs[0]=0 {{{964.185974,1484.77698}, {964.185974,1484.77698}, {965.573975,1482.90295}, {968.343994,1481.53796}}}
-debugShowCubicIntersection wtTs[0]=1 {{{947.392029,1471.64197}, {947.604919,1468.81628}, {950.769897,1468.35559}, {951.289185,1468.29895}}} {{951.289185,1468.29895}} wnTs[0]=0 {{{951.289185,1468.29895}, {951.335754,1468.29382}, {951.361023,1468.29199}, {951.361023,1468.29199}}}
-debugShowCubicIntersection wtTs[0]=0 {{{947.392029,1471.64197}, {947.604919,1468.81628}, {950.769897,1468.35559}, {951.289185,1468.29895}}} {{947.392029,1471.64197}} wnTs[0]=1 {{{951.361023,1468.29199}, {950.554016,1471.98499}, {947.392029,1471.64197}, {947.392029,1471.64197}}}
-debugShowCubicIntersection wtTs[0]=1 {{{951.289185,1468.29895}, {951.335754,1468.29382}, {951.361023,1468.29199}, {951.361023,1468.29199}}} {{951.361023,1468.29199}} wnTs[0]=0 {{{951.361023,1468.29199}, {950.554016,1471.98499}, {947.392029,1471.64197}, {947.392029,1471.64197}}}
-debugShowCubicIntersection wtTs[0]=0 {{{974.919983,1468.29199}, {974.919983,1468.29199}, {978.658997,1468.56299}, {978.890015,1471.64197}}} {{974.919983,1468.29199}} wtTs[1]=1 {{978.890015,1471.64197}} wnTs[0]=1 {{{978.890015,1471.64197}, {978.890015,1471.64197}, {975.72699,1471.98499}, {974.919983,1468.29199}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=0 {{{945.382019,1474.328}, {942.924011,1472.729}, {944.492004,1469.48706}, {944.492004,1469.48706}}} {{945.382019,1474.328}} wtTs[1]=1 {{944.492004,1469.48706}} wnTs[0]=1 {{{944.492004,1469.48706}, {947.388977,1471.95703}, {945.382019,1474.328}, {945.382019,1474.328}}} wnTs[1]=0
-17 id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [1,3]
-18 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{944.492004,1469.48706}, {947.388977,1471.95703}, {945.382019,1474.328}, {945.382019,1474.328}}} {{{946.054016,1476.229}, {945.61499,1473.12903}, {949.046997,1471.97095}, {949.046997,1471.97095}}}
-debugShowCubicIntersection no intersect {{{945.382019,1474.328}, {942.924011,1472.729}, {944.492004,1469.48706}, {944.492004,1469.48706}}} {{{945.312988,1478.18005}, {942.359741,1477.83667}, {942.572632,1474.58496}, {942.638794,1473.97607}}}
-debugShowCubicIntersection no intersect {{{945.382019,1474.328}, {942.924011,1472.729}, {944.492004,1469.48706}, {944.492004,1469.48706}}} {{{942.651001,1473.87805}, {946.562988,1475.66199}, {945.312988,1478.18005}, {945.312988,1478.18005}}}
-debugShowCubicIntersection no intersect {{{944.492004,1469.48706}, {947.388977,1471.95703}, {945.382019,1474.328}, {945.382019,1474.328}}} {{{945.312988,1478.18005}, {942.359741,1477.83667}, {942.572632,1474.58496}, {942.638794,1473.97607}}}
-19 id=1 3=(0.5,1) [2] id=2 2=(0,1) [3]
-20 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{944.492004,1469.48706}, {947.388977,1471.95703}, {945.382019,1474.328}, {945.382019,1474.328}}} {{{942.651001,1473.87805}, {946.562988,1475.66199}, {945.312988,1478.18005}, {945.312988,1478.18005}}}
-21 id=1 1=(0,1) [2,4] id=2 2=(0,0) [1] 4=(1,1) [1]
-22 id=1 1=(0,0) [4] 3=(0.5,1) [2] id=2 2=(0,0) [3] 4=(1,1) [1]
-23 id=1 1=(0,0) [4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(1,1) [1]
-SkTCoincident<struct SkDCubic>::setPerp cPt=(980.258766,1472.83377) != fPerpPt=(982.186748,1472.53542)
-debugShowCubicIntersection wtTs[0]=0 {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}} {{980.900024,1474.328}} wtTs[1]=1 {{981.791016,1469.48706}} wnTs[0]=1 {{{981.791016,1469.48706}, {981.791016,1469.48596}, {983.358032,1472.729}, {980.900024,1474.328}}} wnTs[1]=0
-debugShowCubicIntersection no intersect {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}} {{{980.226013,1476.229}, {980.226013,1476.229}, {977.078003,1476.349}, {977.234985,1471.97095}}}
-24 id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [1,3]
-25 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}} {{{977.234985,1471.97095}, {977.234985,1471.97095}, {980.666992,1473.12903}, {980.226013,1476.229}}}
-26 id=1 1=(0,0.5) [2] id=2 2=(0,1) [1]
-27 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}} {{{980.968994,1478.18005}, {980.968994,1478.18005}, {979.718018,1475.66199}, {983.632019,1473.87805}}}
-debugShowCubicIntersection no intersect {{{980.900024,1474.328}, {980.900024,1474.328}, {978.893005,1471.95703}, {981.791016,1469.48706}}} {{{983.632019,1473.87805}, {983.632019,1473.87805}, {984.229004,1477.80103}, {980.968994,1478.18005}}}
-debugShowCubicIntersection no intersect {{{981.791016,1469.48706}, {981.791016,1469.48596}, {983.358032,1472.729}, {980.900024,1474.328}}} {{{980.968994,1478.18005}, {980.968994,1478.18005}, {979.718018,1475.66199}, {983.632019,1473.87805}}}
-debugShowCubicIntersection no intersect {{{981.791016,1469.48706}, {981.791016,1469.48596}, {983.358032,1472.729}, {980.900024,1474.328}}} {{{983.632019,1473.87805}, {983.632019,1473.87805}, {984.229004,1477.80103}, {980.968994,1478.18005}}}
-debugShowCubicIntersection wtTs[0]=1 {{{946.054016,1476.229}, {945.61499,1473.12903}, {949.046997,1471.97095}, {949.046997,1471.97095}}} {{949.046997,1471.97095}} wnTs[0]=0 {{{949.046997,1471.97095}, {949.191528,1475.95117}, {946.599548,1476.21362}, {946.127258,1476.22852}}}
-debugShowCubicIntersection wtTs[0]=0 {{{946.054016,1476.229}, {945.61499,1473.12903}, {949.046997,1471.97095}, {949.046997,1471.97095}}} {{946.054016,1476.229}} wnTs[0]=1 {{{946.127258,1476.22852}, {946.080078,1476.22998}, {946.054016,1476.229}, {946.054016,1476.229}}}
-debugShowCubicIntersection wtTs[0]=1 {{{949.046997,1471.97095}, {949.191528,1475.95117}, {946.599548,1476.21362}, {946.127258,1476.22852}}} {{946.127258,1476.22852}} wnTs[0]=0 {{{946.127258,1476.22852}, {946.080078,1476.22998}, {946.054016,1476.229}, {946.054016,1476.229}}}
-debugShowCubicIntersection no intersect {{{946.054016,1476.229}, {945.61499,1473.12903}, {949.046997,1471.97095}, {949.046997,1471.97095}}} {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}}
-debugShowCubicIntersection no intersect {{{946.054016,1476.229}, {945.61499,1473.12903}, {949.046997,1471.97095}, {949.046997,1471.97095}}} {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}}
-28 id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-29 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{949.046997,1471.97095}, {949.191528,1475.95117}, {946.599548,1476.21362}, {946.127258,1476.22852}}} {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}}
-30 id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-31 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{949.046997,1471.97095}, {949.191528,1475.95117}, {946.599548,1476.21362}, {946.127258,1476.22852}}} {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}}
-debugShowCubicIntersection wtTs[0]=0 {{{980.226013,1476.229}, {980.226013,1476.229}, {977.078003,1476.349}, {977.234985,1471.97095}}} {{980.226013,1476.229}} wtTs[1]=1 {{977.234985,1471.97095}} wnTs[0]=1 {{{977.234985,1471.97095}, {977.234985,1471.97095}, {980.666992,1473.12903}, {980.226013,1476.229}}} wnTs[1]=0
-32 id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-33 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{980.226013,1476.229}, {980.226013,1476.229}, {977.078003,1476.349}, {977.234985,1471.97095}}} {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}}
-34 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{980.226013,1476.229}, {980.226013,1476.229}, {977.078003,1476.349}, {977.234985,1471.97095}}} {{{977.495972,1475.59497}, {977.496033,1475.59497}, {977.503296,1475.59961}, {977.517029,1475.60864}}}
-35 id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-36 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{980.226013,1476.229}, {980.226013,1476.229}, {977.078003,1476.349}, {977.234985,1471.97095}}} {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}}
-debugShowCubicIntersection no intersect {{{977.234985,1471.97095}, {977.234985,1471.97095}, {980.666992,1473.12903}, {980.226013,1476.229}}} {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}}
-debugShowCubicIntersection no intersect {{{977.234985,1471.97095}, {977.234985,1471.97095}, {980.666992,1473.12903}, {980.226013,1476.229}}} {{{977.495972,1475.59497}, {977.496033,1475.59497}, {977.503296,1475.59961}, {977.517029,1475.60864}}}
-debugShowCubicIntersection no intersect {{{977.234985,1471.97095}, {977.234985,1471.97095}, {980.666992,1473.12903}, {980.226013,1476.229}}} {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}}
-debugShowCubicIntersection wtTs[0]=1 {{{945.312988,1478.18005}, {942.359741,1477.83667}, {942.572632,1474.58496}, {942.638794,1473.97607}}} {{942.638794,1473.97607}} wnTs[0]=0 {{{942.638794,1473.97607}, {942.645691,1473.91284}, {942.651001,1473.87805}, {942.651001,1473.87805}}}
-debugShowCubicIntersection wtTs[0]=0 {{{945.312988,1478.18005}, {942.359741,1477.83667}, {942.572632,1474.58496}, {942.638794,1473.97607}}} {{945.312988,1478.18005}} wnTs[0]=1 {{{942.651001,1473.87805}, {946.562988,1475.66199}, {945.312988,1478.18005}, {945.312988,1478.18005}}}
-debugShowCubicIntersection wtTs[0]=1 {{{942.638794,1473.97607}, {942.645691,1473.91284}, {942.651001,1473.87805}, {942.651001,1473.87805}}} {{942.651001,1473.87805}} wnTs[0]=0 {{{942.651001,1473.87805}, {946.562988,1475.66199}, {945.312988,1478.18005}, {945.312988,1478.18005}}}
-debugShowCubicIntersection wtTs[0]=0 {{{980.968994,1478.18005}, {980.968994,1478.18005}, {979.718018,1475.66199}, {983.632019,1473.87805}}} {{980.968994,1478.18005}} wtTs[1]=1 {{983.632019,1473.87805}} wnTs[0]=1 {{{983.632019,1473.87805}, {983.632019,1473.87805}, {984.229004,1477.80103}, {980.968994,1478.18005}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=1 {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}} {{948.786011,1475.59497}} wnTs[0]=0 {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}}
-debugShowCubicIntersection wtTs[0]=0 {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}} {{947.070984,1480.45496}} wnTs[0]=1 {{{947.129333,1480.43652}, {947.091858,1480.44934}, {947.070984,1480.45496}, {947.070984,1480.45496}}}
-debugShowCubicIntersection wtTs[0]=1 {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}} {{947.129333,1480.43652}} wnTs[0]=0 {{{947.129333,1480.43652}, {947.091858,1480.44934}, {947.070984,1480.45496}, {947.070984,1480.45496}}}
-37 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}} {{{948.427002,1484.453}, {946.440002,1482.23499}, {949.567993,1479.35205}, {949.567993,1479.35205}}}
-debugShowCubicIntersection no intersect {{{947.070984,1480.45496}, {945.211975,1477.88501}, {948.786011,1475.59497}, {948.786011,1475.59497}}} {{{949.567993,1479.35205}, {951.015991,1483.26099}, {948.427002,1484.453}, {948.427002,1484.453}}}
-38 id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-39 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}} {{{948.427002,1484.453}, {946.440002,1482.23499}, {949.567993,1479.35205}, {949.567993,1479.35205}}}
-40 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{948.786011,1475.59497}, {949.835938,1479.33569}, {947.530884,1480.29919}, {947.129333,1480.43652}}} {{{949.567993,1479.35205}, {951.015991,1483.26099}, {948.427002,1484.453}, {948.427002,1484.453}}}
-debugShowCubicIntersection wtTs[0]=1 {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}} {{977.495972,1475.59497}} wnTs[0]=0 {{{977.495972,1475.59497}, {977.496033,1475.59497}, {977.503296,1475.59961}, {977.517029,1475.60864}}}
-debugShowCubicIntersection wtTs[0]=0 {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}} {{979.211975,1480.45496}} wnTs[0]=1 {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}}
-debugShowCubicIntersection wtTs[0]=1 {{{977.495972,1475.59497}, {977.496033,1475.59497}, {977.503296,1475.59961}, {977.517029,1475.60864}}} {{977.517029,1475.60864}} wnTs[0]=0 {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}}
-41 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}} {{{977.854004,1484.453}, {977.854004,1484.453}, {975.265991,1483.26099}, {976.713989,1479.35205}}}
-42 id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-43 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{979.211975,1480.45496}, {979.211975,1480.45496}, {976.348999,1479.68506}, {977.495972,1475.59497}}} {{{976.716125,1479.35413}, {976.807983,1479.44055}, {979.811707,1482.26868}, {977.854004,1484.453}}}
-debugShowCubicIntersection no intersect {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}} {{{977.854004,1484.453}, {977.854004,1484.453}, {975.265991,1483.26099}, {976.713989,1479.35205}}}
-debugShowCubicIntersection no intersect {{{977.517029,1475.60864}, {977.807861,1475.80164}, {980.988281,1478.00073}, {979.211975,1480.45496}}} {{{976.716125,1479.35413}, {976.807983,1479.44055}, {979.811707,1482.26868}, {977.854004,1484.453}}}
-debugShowCubicIntersection wtTs[0]=1 {{{946.255005,1481.276}, {943.094971,1481.93396}, {941.736023,1478.31494}, {941.736023,1478.31494}}} {{941.736023,1478.31494}} wnTs[0]=0 {{{941.736023,1478.31494}, {946.484619,1478.38538}, {946.288147,1481.00122}, {946.25769,1481.2561}}}
-debugShowCubicIntersection wtTs[0]=0 {{{946.255005,1481.276}, {943.094971,1481.93396}, {941.736023,1478.31494}, {941.736023,1478.31494}}} {{946.255005,1481.276}} wnTs[0]=1 {{{946.25769,1481.2561}, {946.256104,1481.26917}, {946.255005,1481.276}, {946.255005,1481.276}}}
-debugShowCubicIntersection wtTs[0]=1 {{{941.736023,1478.31494}, {946.484619,1478.38538}, {946.288147,1481.00122}, {946.25769,1481.2561}}} {{946.25769,1481.2561}} wnTs[0]=0 {{{946.25769,1481.2561}, {946.256104,1481.26917}, {946.255005,1481.276}, {946.255005,1481.276}}}
-debugShowCubicIntersection wtTs[0]=1 {{{984.546021,1478.31494}, {984.546021,1478.31494}, {983.187988,1481.93396}, {980.026001,1481.276}}} {{980.026001,1481.276}} wnTs[0]=0 {{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27551}, {980.025818,1481.27441}}}
-debugShowCubicIntersection wtTs[0]=0 {{{984.546021,1478.31494}, {984.546021,1478.31494}, {983.187988,1481.93396}, {980.026001,1481.276}}} {{984.546021,1478.31494}} wnTs[0]=1 {{{980.025818,1481.27441}, {980.014954,1481.1969}, {979.623779,1478.38806}, {984.546021,1478.31494}}}
-debugShowCubicIntersection wtTs[0]=1 {{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27551}, {980.025818,1481.27441}}} {{980.025818,1481.27441}} wnTs[0]=0 {{{980.025818,1481.27441}, {980.014954,1481.1969}, {979.623779,1478.38806}, {984.546021,1478.31494}}}
-debugShowCubicIntersection wtTs[0]=0 {{{948.427002,1484.453}, {946.440002,1482.23499}, {949.567993,1479.35205}, {949.567993,1479.35205}}} {{948.427002,1484.453}} wtTs[1]=1 {{949.567993,1479.35205}} wnTs[0]=1 {{{949.567993,1479.35205}, {951.015991,1483.26099}, {948.427002,1484.453}, {948.427002,1484.453}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=1 {{{977.854004,1484.453}, {977.854004,1484.453}, {975.265991,1483.26099}, {976.713989,1479.35205}}} {{976.713989,1479.35205}} wnTs[0]=0 {{{976.713989,1479.35205}, {976.713989,1479.35205}, {976.714722,1479.35278}, {976.716125,1479.35413}}}
-debugShowCubicIntersection wtTs[0]=0 {{{977.854004,1484.453}, {977.854004,1484.453}, {975.265991,1483.26099}, {976.713989,1479.35205}}} {{977.854004,1484.453}} wnTs[0]=1 {{{976.716125,1479.35413}, {976.807983,1479.44055}, {979.811707,1482.26868}, {977.854004,1484.453}}}
-debugShowCubicIntersection wtTs[0]=1 {{{976.713989,1479.35205}, {976.713989,1479.35205}, {976.714722,1479.35278}, {976.716125,1479.35413}}} {{976.716125,1479.35413}} wnTs[0]=0 {{{976.716125,1479.35413}, {976.807983,1479.44055}, {979.811707,1482.26868}, {977.854004,1484.453}}}
-debugShowCubicIntersection wtTs[0]=0 {{{947.294006,1484.198}, {944.210999,1485.49805}, {942.495972,1481.823}, {942.495972,1481.823}}} {{947.294006,1484.198}} wtTs[1]=1 {{942.495972,1481.823}} wnTs[0]=1 {{{942.495972,1481.823}, {947.187988,1481.33496}, {947.294006,1484.198}, {947.294006,1484.198}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=0 {{{978.989014,1484.198}, {978.989014,1484.198}, {979.094971,1481.33496}, {983.786011,1481.823}}} {{978.989014,1484.198}} wtTs[1]=1 {{983.786011,1481.823}} wnTs[0]=1 {{{983.786011,1481.823}, {983.786011,1481.823}, {982.070007,1485.49805}, {978.989014,1484.198}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=1 {{{951.176025,1486.97803}, {949.194519,1484.8667}, {950.909729,1482.36658}, {951.290283,1481.86658}}} {{951.290283,1481.86658}} wnTs[0]=0 {{{951.290283,1481.86658}, {951.334778,1481.80811}, {951.361023,1481.77698}, {951.361023,1481.77698}}}
-debugShowCubicIntersection no intersect {{{951.176025,1486.97803}, {949.194519,1484.8667}, {950.909729,1482.36658}, {951.290283,1481.86658}}} {{{951.361023,1481.77698}, {953.644836,1485.34509}, {951.363281,1486.86157}, {951.186646,1486.97144}}}
-debugShowCubicIntersection wtTs[0]=0 {{{951.176025,1486.97803}, {949.194519,1484.8667}, {950.909729,1482.36658}, {951.290283,1481.86658}}} {{951.176025,1486.97803}} wnTs[0]=1 {{{951.186646,1486.97144}, {951.179688,1486.97583}, {951.176025,1486.97803}, {951.176025,1486.97803}}}
-debugShowCubicIntersection wtTs[0]=1 {{{951.290283,1481.86658}, {951.334778,1481.80811}, {951.361023,1481.77698}, {951.361023,1481.77698}}} {{951.361023,1481.77698}} wnTs[0]=0 {{{951.361023,1481.77698}, {953.644836,1485.34509}, {951.363281,1486.86157}, {951.186646,1486.97144}}}
-debugShowCubicIntersection wtTs[0]=1 {{{951.361023,1481.77698}, {953.644836,1485.34509}, {951.363281,1486.86157}, {951.186646,1486.97144}}} {{951.186646,1486.97144}} wnTs[0]=0 {{{951.186646,1486.97144}, {951.179688,1486.97583}, {951.176025,1486.97803}, {951.176025,1486.97803}}}
-debugShowCubicIntersection wtTs[0]=0 {{{975.106995,1486.97803}, {975.106995,1486.97803}, {972.546997,1485.48706}, {974.919983,1481.77698}}} {{975.106995,1486.97803}} wtTs[1]=1 {{974.919983,1481.77698}} wnTs[0]=1 {{{974.919983,1481.77698}, {974.919983,1481.776}, {977.31897,1484.61902}, {975.106995,1486.97803}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=0 {{{955.120972,1488.94495}, {952.309021,1487.98303}, {953.458984,1483.93604}, {953.458984,1483.93604}}} {{955.120972,1488.94495}} wtTs[1]=1 {{953.458984,1483.93604}} wnTs[0]=1 {{{953.458984,1483.93604}, {957.004028,1486.37097}, {955.120972,1488.94495}, {955.120972,1488.94495}}} wnTs[1]=0
-44 id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-45 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{955.120972,1488.94495}, {952.309021,1487.98303}, {953.458984,1483.93604}, {953.458984,1483.93604}}} {{{947.51001,1488.53101}, {947.51001,1488.53101}, {951.596985,1486.32202}, {953.234009,1489.08997}}}
-debugShowCubicIntersection no intersect {{{955.120972,1488.94495}, {952.309021,1487.98303}, {953.458984,1483.93604}, {953.458984,1483.93604}}} {{{953.234009,1489.08997}, {953.234009,1489.08997}, {951.158997,1491.03601}, {947.51001,1488.53101}}}
-debugShowCubicIntersection wtTs[0]=1 {{{972.825012,1483.93701}, {972.825012,1483.93701}, {973.971985,1487.98401}, {971.161987,1488.94604}}} {{971.161987,1488.94604}} wnTs[0]=0 {{{971.161987,1488.94604}, {971.161987,1488.94592}, {971.154663,1488.93591}, {971.141846,1488.9165}}}
-debugShowCubicIntersection wtTs[0]=0 {{{972.825012,1483.93701}, {972.825012,1483.93701}, {973.971985,1487.98401}, {971.161987,1488.94604}}} {{972.825012,1483.93701}} wnTs[0]=1 {{{971.141846,1488.9165}, {970.948425,1488.625}, {969.49884,1486.21948}, {972.825012,1483.93701}}}
-debugShowCubicIntersection wtTs[0]=1 {{{971.161987,1488.94604}, {971.161987,1488.94592}, {971.154663,1488.93591}, {971.141846,1488.9165}}} {{971.141846,1488.9165}} wnTs[0]=0 {{{971.141846,1488.9165}, {970.948425,1488.625}, {969.49884,1486.21948}, {972.825012,1483.93701}}}
-debugShowCubicIntersection no intersect {{{972.825012,1483.93701}, {972.825012,1483.93701}, {973.971985,1487.98401}, {971.161987,1488.94604}}} {{{978.770996,1488.53101}, {975.204224,1490.98022}, {973.141174,1489.17444}, {973.051086,1489.09277}}}
-46 id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-47 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{972.825012,1483.93701}, {972.825012,1483.93701}, {973.971985,1487.98401}, {971.161987,1488.94604}}} {{{973.047974,1489.08997}, {974.651978,1486.37781}, {978.607178,1488.44397}, {978.766052,1488.52844}}}
-debugShowCubicIntersection wtTs[0]=1 {{{949.890991,1486.86804}, {947.178772,1488.37146}, {944.723022,1485.51147}, {944.608215,1485.375}}} {{944.608215,1485.375}} wnTs[0]=0 {{{944.608215,1485.375}, {944.605408,1485.3717}, {944.604004,1485.37}, {944.604004,1485.37}}}
-debugShowCubicIntersection wtTs[0]=0 {{{949.890991,1486.86804}, {947.178772,1488.37146}, {944.723022,1485.51147}, {944.608215,1485.375}}} {{949.890991,1486.86804}} wnTs[0]=1 {{{944.604004,1485.37}, {949.562012,1484.06494}, {949.890991,1486.86804}, {949.890991,1486.86804}}}
-debugShowCubicIntersection wtTs[0]=1 {{{944.608215,1485.375}, {944.605408,1485.3717}, {944.604004,1485.37}, {944.604004,1485.37}}} {{944.604004,1485.37}} wnTs[0]=0 {{{944.604004,1485.37}, {949.562012,1484.06494}, {949.890991,1486.86804}, {949.890991,1486.86804}}}
-debugShowCubicIntersection wtTs[0]=0 {{{976.393005,1486.86804}, {976.393005,1486.86804}, {976.719971,1484.06494}, {981.679016,1485.37}}} {{976.393005,1486.86804}} wtTs[1]=1 {{981.679016,1485.37}} wnTs[0]=1 {{{981.679016,1485.37}, {981.679016,1485.37}, {979.169983,1488.40796}, {976.393005,1486.86804}}} wnTs[1]=0
-debugShowCubicIntersection wtTs[0]=0 {{{960.68103,1489.98499}, {957.533997,1490.672}, {956.417969,1486.75}, {956.417969,1486.75}}} {{960.68103,1489.98499}} wtTs[1]=1 {{956.417969,1486.75}} wnTs[0]=1 {{{956.417969,1486.75}, {961.403015,1487.19202}, {960.68103,1489.98499}, {960.68103,1489.98499}}} wnTs[1]=0
-debugShowCubicIntersection no intersect {{{960.68103,1489.98499}, {957.533997,1490.672}, {956.417969,1486.75}, {956.417969,1486.75}}} {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}}
-debugShowCubicIntersection no intersect {{{956.417969,1486.75}, {961.403015,1487.19202}, {960.68103,1489.98499}, {960.68103,1489.98499}}} {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}}
-debugShowCubicIntersection wtTs[0]=0 {{{965.60199,1489.98499}, {965.60199,1489.98499}, {964.879028,1487.19202}, {969.864014,1486.75}}} {{965.60199,1489.98499}} wtTs[1]=1 {{969.864014,1486.75}} wnTs[0]=1 {{{969.864014,1486.75}, {969.864014,1486.75}, {968.749023,1490.672}, {965.60199,1489.98499}}} wnTs[1]=0
-debugShowCubicIntersection no intersect {{{965.60199,1489.98499}, {965.60199,1489.98499}, {964.879028,1487.19202}, {969.864014,1486.75}}} {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}}
-debugShowCubicIntersection no intersect {{{969.864014,1486.75}, {969.864014,1486.75}, {968.749023,1490.672}, {965.60199,1489.98499}}} {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}}
-debugShowCubicIntersection wtTs[0]=0 {{{947.51001,1488.53101}, {947.51001,1488.53101}, {951.596985,1486.32202}, {953.234009,1489.08997}}} {{947.51001,1488.53101}} wtTs[1]=1 {{953.234009,1489.08997}} wnTs[0]=1 {{{953.234009,1489.08997}, {953.234009,1489.08997}, {951.158997,1491.03601}, {947.51001,1488.53101}}} wnTs[1]=0
-debugShowCubicIntersection no intersect {{{953.234009,1489.08997}, {953.234009,1489.08997}, {951.158997,1491.03601}, {947.51001,1488.53101}}} {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}}
-debugShowCubicIntersection wtTs[0]=1 {{{978.770996,1488.53101}, {975.204224,1490.98022}, {973.141174,1489.17444}, {973.051086,1489.09277}}} {{973.051086,1489.09277}} wnTs[0]=0 {{{973.051086,1489.09277}, {973.049011,1489.09094}, {973.047974,1489.08997}, {973.047974,1489.08997}}}
-debugShowCubicIntersection no intersect {{{978.770996,1488.53101}, {975.204224,1490.98022}, {973.141174,1489.17444}, {973.051086,1489.09277}}} {{{973.047974,1489.08997}, {974.651978,1486.37781}, {978.607178,1488.44397}, {978.766052,1488.52844}}}
-debugShowCubicLineIntersection wtTs[0]=0 {{{978.770996,1488.53101}, {975.204224,1490.98022}, {973.141174,1489.17444}, {973.051086,1489.09277}}} {{978.770996,1488.53101}} wnTs[0]=1 {{{978.766052,1488.52844}, {978.770996,1488.53101}}}
-debugShowCubicIntersection wtTs[0]=1 {{{973.051086,1489.09277}, {973.049011,1489.09094}, {973.047974,1489.08997}, {973.047974,1489.08997}}} {{973.047974,1489.08997}} wnTs[0]=0 {{{973.047974,1489.08997}, {974.651978,1486.37781}, {978.607178,1488.44397}, {978.766052,1488.52844}}}
-debugShowCubicLineIntersection wtTs[0]=1 {{{973.047974,1489.08997}, {974.651978,1486.37781}, {978.607178,1488.44397}, {978.766052,1488.52844}}} {{978.766052,1488.52844}} wnTs[0]=0 {{{978.766052,1488.52844}, {978.770996,1488.53101}}}
-debugShowCubicIntersection no intersect {{{978.770996,1488.53101}, {975.204224,1490.98022}, {973.141174,1489.17444}, {973.051086,1489.09277}}} {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}}
-debugShowCubicIntersection wtTs[0]=1 {{{963.143005,1489.59802}, {963.763,1489.59802}, {964.265015,1490.09998}, {964.265015,1490.72095}}} {{964.265015,1490.72095}} wnTs[0]=0 {{{964.265015,1490.72095}, {964.265015,1491.34204}, {963.763,1491.84399}, {963.143005,1491.84399}}}
-debugShowCubicIntersection no intersect {{{963.143005,1489.59802}, {963.763,1489.59802}, {964.265015,1490.09998}, {964.265015,1490.72095}}} {{{963.143005,1491.84399}, {962.521973,1491.84399}, {962.02002,1491.34204}, {962.02002,1490.72095}}}
-debugShowCubicIntersection wtTs[0]=0 {{{963.143005,1489.59802}, {963.763,1489.59802}, {964.265015,1490.09998}, {964.265015,1490.72095}}} {{963.143005,1489.59802}} wnTs[0]=1 {{{962.02002,1490.72095}, {962.02002,1490.09998}, {962.521973,1489.59802}, {963.143005,1489.59802}}}
-debugShowCubicIntersection wtTs[0]=1 {{{964.265015,1490.72095}, {964.265015,1491.34204}, {963.763,1491.84399}, {963.143005,1491.84399}}} {{963.143005,1491.84399}} wnTs[0]=0 {{{963.143005,1491.84399}, {962.521973,1491.84399}, {962.02002,1491.34204}, {962.02002,1490.72095}}}
-debugShowCubicIntersection no intersect {{{964.265015,1490.72095}, {964.265015,1491.34204}, {963.763,1491.84399}, {963.143005,1491.84399}}} {{{962.02002,1490.72095}, {962.02002,1490.09998}, {962.521973,1489.59802}, {963.143005,1489.59802}}}
-debugShowCubicIntersection wtTs[0]=1 {{{963.143005,1491.84399}, {962.521973,1491.84399}, {962.02002,1491.34204}, {962.02002,1490.72095}}} {{962.02002,1490.72095}} wnTs[0]=0 {{{962.02002,1490.72095}, {962.02002,1490.09998}, {962.521973,1489.59802}, {963.143005,1489.59802}}}
-debugShowCubicIntersection wtTs[0]=1 {{{957.127014,1490.40002}, {955.541504,1492.89014}, {951.825745,1491.38965}, {951.445557,1491.22766}}} {{951.445557,1491.22766}} wnTs[0]=0 {{{951.445557,1491.22766}, {951.424805,1491.21887}, {951.414001,1491.21399}, {951.414001,1491.21399}}}
-debugShowCubicIntersection no intersect {{{957.127014,1490.40002}, {955.541504,1492.89014}, {951.825745,1491.38965}, {951.445557,1491.22766}}} {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}}
-debugShowCubicIntersection wtTs[0]=0 {{{957.127014,1490.40002}, {955.541504,1492.89014}, {951.825745,1491.38965}, {951.445557,1491.22766}}} {{957.127014,1490.40002}} wnTs[0]=1 {{{957.119873,1490.39355}, {957.124634,1490.39783}, {957.127014,1490.40002}, {957.127014,1490.40002}}}
-debugShowCubicIntersection wtTs[0]=1 {{{951.445557,1491.22766}, {951.424805,1491.21887}, {951.414001,1491.21399}, {951.414001,1491.21399}}} {{951.414001,1491.21399}} wnTs[0]=0 {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}}
-debugShowCubicIntersection wtTs[0]=1 {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}} {{957.119873,1490.39355}} wnTs[0]=0 {{{957.119873,1490.39355}, {957.124634,1490.39783}, {957.127014,1490.40002}, {957.127014,1490.40002}}}
-debugShowCubicIntersection no intersect {{{957.127014,1490.40002}, {955.541504,1492.89014}, {951.825745,1491.38965}, {951.445557,1491.22766}}} {{{961.283997,1491.56299}, {958.953979,1494.49695}, {955.61499,1492.81604}, {955.61499,1492.81604}}}
-debugShowCubicIntersection no intersect {{{957.127014,1490.40002}, {955.541504,1492.89014}, {951.825745,1491.38965}, {951.445557,1491.22766}}} {{{955.61499,1492.81604}, {958.695923,1489.72131}, {960.89093,1491.24622}, {961.236389,1491.52283}}}
-48 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{951.414001,1491.21399}, {954.694214,1488.33154}, {956.976746,1490.26636}, {957.119873,1490.39355}}} {{{955.61499,1492.81604}, {958.695923,1489.72131}, {960.89093,1491.24622}, {961.236389,1491.52283}}}
-debugShowCubicIntersection wtTs[0]=1 {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}} {{974.869995,1491.21399}} wnTs[0]=0 {{{974.869995,1491.21399}, {974.869995,1491.21399}, {974.857788,1491.21948}, {974.834473,1491.22937}}}
-debugShowCubicIntersection wtTs[0]=0 {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}} {{969.156982,1490.40002}} wnTs[0]=1 {{{974.834473,1491.22937}, {974.433289,1491.40051}, {970.736267,1492.88184}, {969.156982,1490.40002}}}
-debugShowCubicIntersection wtTs[0]=1 {{{974.869995,1491.21399}, {974.869995,1491.21399}, {974.857788,1491.21948}, {974.834473,1491.22937}}} {{974.834473,1491.22937}} wnTs[0]=0 {{{974.834473,1491.22937}, {974.433289,1491.40051}, {970.736267,1492.88184}, {969.156982,1490.40002}}}
-49 id=1 (empty) id=2 (empty)
-debugShowCubicIntersection no intersect {{{969.156982,1490.40002}, {969.156982,1490.40002}, {971.478027,1488.23596}, {974.869995,1491.21399}}} {{{964.999023,1491.56299}, {964.999023,1491.56299}, {967.304016,1489.43896}, {970.666992,1492.81604}}}
-debugShowCubicIntersection no intersect {{{974.834473,1491.22937}, {974.433289,1491.40051}, {970.736267,1492.88184}, {969.156982,1490.40002}}} {{{970.666992,1492.81604}, {970.666992,1492.81604}, {967.327026,1494.49695}, {964.999023,1491.56299}}}
-debugShowCubicIntersection no intersect {{{974.834473,1491.22937}, {974.433289,1491.40051}, {970.736267,1492.88184}, {969.156982,1490.40002}}} {{{964.999023,1491.56299}, {964.999023,1491.56299}, {967.304016,1489.43896}, {970.666992,1492.81604}}}
-debugShowCubicIntersection wtTs[0]=1 {{{961.283997,1491.56299}, {958.953979,1494.49695}, {955.61499,1492.81604}, {955.61499,1492.81604}}} {{955.61499,1492.81604}} wnTs[0]=0 {{{955.61499,1492.81604}, {958.695923,1489.72131}, {960.89093,1491.24622}, {961.236389,1491.52283}}}
-debugShowCubicIntersection wtTs[0]=0 {{{961.283997,1491.56299}, {958.953979,1494.49695}, {955.61499,1492.81604}, {955.61499,1492.81604}}} {{961.283997,1491.56299}} wnTs[0]=1 {{{961.236389,1491.52283}, {961.267883,1491.5481}, {961.283997,1491.56299}, {961.283997,1491.56299}}}
-debugShowCubicIntersection wtTs[0]=1 {{{955.61499,1492.81604}, {958.695923,1489.72131}, {960.89093,1491.24622}, {961.236389,1491.52283}}} {{961.236389,1491.52283}} wnTs[0]=0 {{{961.236389,1491.52283}, {961.267883,1491.5481}, {961.283997,1491.56299}, {961.283997,1491.56299}}}
-debugShowCubicIntersection wtTs[0]=0 {{{970.666992,1492.81604}, {970.666992,1492.81604}, {967.327026,1494.49695}, {964.999023,1491.56299}}} {{970.666992,1492.81604}} wtTs[1]=1 {{964.999023,1491.56299}} wnTs[0]=1 {{{964.999023,1491.56299}, {964.999023,1491.56299}, {967.304016,1489.43896}, {970.666992,1492.81604}}} wnTs[1]=0
-SkOpSegment::debugShowActiveSpans id=1 (941,1494 941,1464) t=0 (941,1494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=2 (941,1464 985,1464) t=0 (941,1464) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=3 (985,1464 985,1494) t=0 (985,1464) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=4 (985,1494 941,1494) t=0 (985,1494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=92 (948.64801,1468.15002 948.638977,1465.22095 952.265991,1464.46399 952.265991,1464.46399) t=0 (948.64801,1468.15002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=93 (952.265991,1464.46399 951.707275,1468.29565 948.98999,1468.17932 948.677368,1468.15283) t=0 (952.265991,1464.46399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=94 (948.677368,1468.15283 948.658142,1468.15125 948.64801,1468.15002 948.64801,1468.15002) t=0 (948.677368,1468.15283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 (974.016968,1464.46399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=110 (977.633972,1468.15002 977.633972,1468.15002 974.611023,1468.53101 974.016968,1464.46399) t=0 (977.633972,1468.15002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 (946.797974,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 (947.048523,1465.54285) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 (947.083984,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 (979.197998,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 (979.481995,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [1/2] next=2/1 sect=23/23  s=1 [2] e=0 [1] sgn=1 windVal=1 windSum=?
-SkOpAngle::dumpOne [2/1] next=1/2 sect=31/31  s=0 [3] e=1 [4] sgn=-1 windVal=1 windSum=? stop
-SkOpSegment::markWinding id=1 (941,1494 941,1464) t=0 [1] (941,1494) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=2 (941,1464 985,1464) t=0 [3] (941,1464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=3 (985,1464 985,1494) t=0 [5] (985,1464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=4 (985,1494 941,1494) t=0 [7] (985,1494) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (941,1494 941,1464) t=0 [1] (941,1494) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::activeOp id=1 t=0 tEnd=1 op=sect miFrom=1 miTo=0 suFrom=0 suTo=0 result=0
-SkOpSegment::markDone id=1 (941,1494 941,1464) t=0 [1] (941,1494) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDone id=2 (941,1464 985,1464) t=0 [3] (941,1464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDone id=3 (985,1464 985,1494) t=0 [5] (985,1464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDone id=4 (985,1494 941,1494) t=0 [7] (985,1494) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=92 (948.64801,1468.15002 948.638977,1465.22095 952.265991,1464.46399 952.265991,1464.46399) t=0 (948.64801,1468.15002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=93 (952.265991,1464.46399 951.707275,1468.29565 948.98999,1468.17932 948.677368,1468.15283) t=0 (952.265991,1464.46399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=94 (948.677368,1468.15283 948.658142,1468.15125 948.64801,1468.15002 948.64801,1468.15002) t=0 (948.677368,1468.15283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 (974.016968,1464.46399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=110 (977.633972,1468.15002 977.633972,1468.15002 974.611023,1468.53101 974.016968,1464.46399) t=0 (977.633972,1468.15002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 (946.797974,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 (947.048523,1465.54285) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 (947.083984,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 (979.197998,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 (979.481995,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [92/4] next=93/3 sect=17/21  s=1 [184] e=0 [183] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [93/3] next=92/4 sect=21/21  s=0 [185] e=1 [186] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.253737016 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=92 index=183 endIndex=184 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.253737016 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=92 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=92 (948.64801,1468.15002 948.638977,1465.22095 952.265991,1464.46399 952.265991,1464.46399) t=0 [183] (948.64801,1468.15002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=93 (952.265991,1464.46399 951.707275,1468.29565 948.98999,1468.17932 948.677368,1468.15283) t=0 [185] (952.265991,1464.46399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=94 (948.677368,1468.15283 948.658142,1468.15125 948.64801,1468.15002 948.64801,1468.15002) t=0 [187] (948.677368,1468.15283) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=92 (948.64801,1468.15002 948.638977,1465.22095 952.265991,1464.46399 952.265991,1464.46399) t=0 [183] (948.64801,1468.15002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=92 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=92 (948.64801,1468.15002 948.638977,1465.22095 952.265991,1464.46399 952.265991,1464.46399) t=0 [183] (948.64801,1468.15002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=92 from=(948.64801,1468.15002) to=(952.265991,1464.46399)
-path.moveTo(948.64801,1468.15002);
-path.cubicTo(948.638977,1465.22095, 952.265991,1464.46399, 952.265991,1464.46399);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=93 (952.265991,1464.46399 951.707275,1468.29565 948.98999,1468.17932 948.677368,1468.15283) t=0 [185] (952.265991,1464.46399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=93 from=(952.265991,1464.46399) to=(948.677368,1468.15283)
-path.cubicTo(951.707275,1468.29565, 948.98999,1468.17932, 948.677368,1468.15283);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=94 (948.677368,1468.15283 948.658142,1468.15125 948.64801,1468.15002 948.64801,1468.15002) t=0 [187] (948.677368,1468.15283) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=94 from=(948.677368,1468.15283) to=(948.64801,1468.15002)
-path.cubicTo(948.658142,1468.15125, 948.64801,1468.15002, 948.64801,1468.15002);
-path.close();
-SkOpSegment::debugShowActiveSpans id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 (974.016968,1464.46399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=110 (977.633972,1468.15002 977.633972,1468.15002 974.611023,1468.53101 974.016968,1464.46399) t=0 (977.633972,1468.15002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 (946.797974,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 (947.048523,1465.54285) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 (947.083984,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 (979.197998,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 (979.481995,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [110/7] next=109/8 sect=25/25  s=1 [220] e=0 [219] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [109/8] next=110/7 sect=29/25  s=0 [217] e=1 [218] sgn=-1 windVal=1 windSum=? operand
-FindSortableTop current=109 index=217 endIndex=218 tHit=0.158904053 hitDx=0 try=1 vert=0
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.83034446 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=109 index=217 endIndex=218 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.83034446 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=109 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 [217] (974.016968,1464.46399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=110 (977.633972,1468.15002 977.633972,1468.15002 974.611023,1468.53101 974.016968,1464.46399) t=0 [219] (977.633972,1468.15002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 [217] (974.016968,1464.46399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=109 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=109 (974.016968,1464.46399 974.016968,1464.46399 977.643982,1465.22095 977.633972,1468.15002) t=0 [217] (974.016968,1464.46399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=109 from=(974.016968,1464.46399) to=(977.633972,1468.15002)
-path.moveTo(974.016968,1464.46399);
-path.cubicTo(974.016968,1464.46399, 977.643982,1465.22095, 977.633972,1468.15002);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=110 (977.633972,1468.15002 977.633972,1468.15002 974.611023,1468.53101 974.016968,1464.46399) t=0 [219] (977.633972,1468.15002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=110 from=(977.633972,1468.15002) to=(974.016968,1464.46399)
-path.cubicTo(977.633972,1468.15002, 974.611023,1468.53101, 974.016968,1464.46399);
-path.close();
-SkOpSegment::debugShowActiveSpans id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 (946.797974,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 (947.048523,1465.54285) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 (947.083984,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 (979.197998,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 (979.481995,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [87/12] next=88/11 sect=17/21  s=1 [174] e=0 [173] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [88/11] next=87/12 sect=25/21  s=0 [175] e=1 [176] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.138264049 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=87 index=173 endIndex=174 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.138264049 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=87 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 [173] (947.048523,1465.54285) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 [175] (947.083984,1465.50598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 [171] (946.797974,1470.27405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 [173] (947.048523,1465.54285) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=87 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=87 (947.048523,1465.54285 947.071289,1465.51855 947.083984,1465.50598 947.083984,1465.50598) t=0 [173] (947.048523,1465.54285) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=87 from=(947.048523,1465.54285) to=(947.083984,1465.50598)
-path.moveTo(947.048523,1465.54285);
-path.cubicTo(947.071289,1465.51855, 947.083984,1465.50598, 947.083984,1465.50598);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=88 (947.083984,1465.50598 949.145996,1468.82605 946.797974,1470.27405 946.797974,1470.27405) t=0 [175] (947.083984,1465.50598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=88 from=(947.083984,1465.50598) to=(946.797974,1470.27405)
-path.cubicTo(949.145996,1468.82605, 946.797974,1470.27405, 946.797974,1470.27405);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=86 (946.797974,1470.27405 944.819641,1468.07397 946.75708,1465.85327 947.048523,1465.54285) t=0 [171] (946.797974,1470.27405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=86 from=(946.797974,1470.27405) to=(947.048523,1465.54285)
-path.cubicTo(944.819641,1468.07397, 946.75708,1465.85327, 947.048523,1465.54285);
-path.close();
-SkOpSegment::debugShowActiveSpans id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 (979.197998,1465.50598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 (979.481995,1470.27405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [114/15] next=113/16 sect=21/25  s=1 [228] e=0 [227] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [113/16] next=114/15 sect=29/25  s=0 [225] e=1 [226] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.85694053 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=114 index=227 endIndex=228 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.85694053 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=114 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 [227] (979.481995,1470.27405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 [225] (979.197998,1465.50598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 [227] (979.481995,1470.27405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=114 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=114 (979.481995,1470.27405 979.481995,1470.27405 977.138,1468.82605 979.197998,1465.50598) t=0 [227] (979.481995,1470.27405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=114 from=(979.481995,1470.27405) to=(979.197998,1465.50598)
-path.moveTo(979.481995,1470.27405);
-path.cubicTo(979.481995,1470.27405, 977.138,1468.82605, 979.197998,1465.50598);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=113 (979.197998,1465.50598 979.197998,1465.50598 981.619019,1467.90198 979.481995,1470.27405) t=0 [225] (979.197998,1465.50598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=113 from=(979.197998,1465.50598) to=(979.481995,1470.27405)
-path.cubicTo(979.197998,1465.50598, 981.619019,1467.90198, 979.481995,1470.27405);
-path.close();
-SkOpSegment::debugShowActiveSpans id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 (963.215027,1486.67004) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 (962.106995,1485.65405) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 (957.539001,1482.09705) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 (953.870422,1476.93176) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 (953.867004,1476.87598) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 (954.190002,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 (972.23999,1465.94397) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 (972.565002,1476.87695) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 (968.891968,1482.09802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 (964.407593,1485.54968) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 (964.325012,1485.65503) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [45/20] next=46/19 sect=21/21  s=1 [90] e=0 [89] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [46/19] next=45/20 sect=31/31  s=0 [91] e=1 [92] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.299038974 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=45 index=89 endIndex=90 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.299038974 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=45 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 [89] (953.867004,1476.87598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 [91] (954.190002,1465.94397) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 [93] (972.23999,1465.94397) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 [95] (972.565002,1476.87695) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 [97] (968.891968,1482.09802) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 [99] (964.407593,1485.54968) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 [101] (964.325012,1485.65503) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 [81] (963.215027,1486.67004) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 [83] (962.106995,1485.65405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 [85] (957.539001,1482.09705) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 [87] (953.870422,1476.93176) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 [89] (953.867004,1476.87598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=45 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=45 (953.867004,1476.87598 954.190002,1465.94397) t=0 [89] (953.867004,1476.87598) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=45 from=(953.867004,1476.87598) to=(954.190002,1465.94397)
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=46 (954.190002,1465.94397 972.23999,1465.94397) t=0 [91] (954.190002,1465.94397) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=46 from=(954.190002,1465.94397) to=(972.23999,1465.94397)
-path.moveTo(953.867004,1476.87598);
-path.lineTo(954.190002,1465.94397);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=47 (972.23999,1465.94397 972.565002,1476.87695) t=0 [93] (972.23999,1465.94397) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=47 from=(972.23999,1465.94397) to=(972.565002,1476.87695)
-path.lineTo(972.23999,1465.94397);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=48 (972.565002,1476.87695 972.565002,1476.87695 972.440979,1480.35303 968.891968,1482.09802) t=0 [95] (972.565002,1476.87695) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=48 from=(972.565002,1476.87695) to=(968.891968,1482.09802)
-path.lineTo(972.565002,1476.87695);
-path.cubicTo(972.565002,1476.87695, 972.440979,1480.35303, 968.891968,1482.09802);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=49 (968.891968,1482.09802 966.255737,1483.39539 964.76178,1485.11145 964.407593,1485.54968) t=0 [97] (968.891968,1482.09802) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=49 from=(968.891968,1482.09802) to=(964.407593,1485.54968)
-path.cubicTo(966.255737,1483.39539, 964.76178,1485.11145, 964.407593,1485.54968);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=50 (964.407593,1485.54968 964.352539,1485.6178 964.325012,1485.65503 964.325012,1485.65503) t=0 [99] (964.407593,1485.54968) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=50 from=(964.407593,1485.54968) to=(964.325012,1485.65503)
-path.cubicTo(964.352539,1485.6178, 964.325012,1485.65503, 964.325012,1485.65503);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=51 (964.325012,1485.65503 964.325012,1485.65503 963.687012,1486.67004 963.215027,1486.67004) t=0 [101] (964.325012,1485.65503) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=51 from=(964.325012,1485.65503) to=(963.215027,1486.67004)
-path.cubicTo(964.325012,1485.65503, 963.687012,1486.67004, 963.215027,1486.67004);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=41 (963.215027,1486.67004 962.744995,1486.67004 962.106995,1485.65405 962.106995,1485.65405) t=0 [81] (963.215027,1486.67004) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=41 from=(963.215027,1486.67004) to=(962.106995,1485.65405)
-path.cubicTo(962.744995,1486.67004, 962.106995,1485.65405, 962.106995,1485.65405);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=42 (962.106995,1485.65405 962.106995,1485.65405 960.585022,1483.59595 957.539001,1482.09705) t=0 [83] (962.106995,1485.65405) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=42 from=(962.106995,1485.65405) to=(957.539001,1482.09705)
-path.cubicTo(962.106995,1485.65405, 960.585022,1483.59595, 957.539001,1482.09705);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=43 (957.539001,1482.09705 954.255432,1480.48206 953.90448,1477.3844 953.870422,1476.93176) t=0 [85] (957.539001,1482.09705) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=43 from=(957.539001,1482.09705) to=(953.870422,1476.93176)
-path.cubicTo(954.255432,1480.48206, 953.90448,1477.3844, 953.870422,1476.93176);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=44 (953.870422,1476.93176 953.867676,1476.89526 953.867004,1476.87598 953.867004,1476.87598) t=0 [87] (953.870422,1476.93176) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=44 from=(953.870422,1476.93176) to=(953.867004,1476.87598)
-path.cubicTo(953.867676,1476.89526, 953.867004,1476.87598, 953.867004,1476.87598);
-path.close();
-SkOpSegment::debugShowActiveSpans id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 (968.343994,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 (971.6875,1476.79639) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 (971.687988,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 (971.393982,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 (954.960999,1466.83398) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 (954.666016,1476.78601) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 (958.008972,1481.53796) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 (962.0755,1484.66101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 (962.166992,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 (963.177979,1485.70105) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 (964.185974,1484.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [34/23] next=33/24 sect=21/21  s=0 [67] e=1 [68] sgn=-1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [33/24] next=34/23 sect=31/31  s=1 [66] e=0 [65] sgn=1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=46 opp=0 tHit=0.0410812529 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-FindSortableTop current=34 index=68 endIndex=67 tHit=0.1 hitDx=18.0499878 try=0 vert=0
-SkOpSegment::windingAtT id=46 opp=1 tHit=0.0410812529 t=0 oldWinding=-1 windValue=0 dx=+ winding=-1
-SkOpSegment::initWinding id=34 oldWinding=-1 hitDx=+ dx=- windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 [67] (954.960999,1466.83398) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 [65] (971.393982,1466.83398) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 [63] (971.687988,1476.78601) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 [61] (971.6875,1476.79639) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 [59] (968.343994,1481.53796) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 [79] (964.185974,1484.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 [77] (963.177979,1485.70105) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 [75] (962.166992,1484.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 [73] (962.0755,1484.66101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 [71] (958.008972,1481.53796) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 [69] (954.666016,1476.78601) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 [67] (954.960999,1466.83398) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=34 t=1 tEnd=0 op=sect miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=34 (954.960999,1466.83398 954.666016,1476.78601) t=0 [67] (954.960999,1466.83398) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=34 from=(954.666016,1476.78601) to=(954.960999,1466.83398)
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=33 (971.393982,1466.83398 954.960999,1466.83398) t=0 [65] (971.393982,1466.83398) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=33 from=(954.960999,1466.83398) to=(971.393982,1466.83398)
-path.moveTo(954.666016,1476.78601);
-path.lineTo(954.960999,1466.83398);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=32 (971.687988,1476.78601 971.393982,1466.83398) t=0 [63] (971.687988,1476.78601) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=32 from=(971.393982,1466.83398) to=(971.687988,1476.78601)
-path.lineTo(971.393982,1466.83398);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=31 (971.6875,1476.79639 971.687866,1476.78955 971.687988,1476.78601 971.687988,1476.78601) t=0 [61] (971.6875,1476.79639) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=31 from=(971.687988,1476.78601) to=(971.6875,1476.79639)
-path.lineTo(971.687988,1476.78601);
-path.cubicTo(971.687988,1476.78601, 971.687866,1476.78955, 971.6875,1476.79639);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=30 (968.343994,1481.53796 971.466064,1480.00305 971.676941,1476.99573 971.6875,1476.79639) t=0 [59] (968.343994,1481.53796) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=30 from=(971.6875,1476.79639) to=(968.343994,1481.53796)
-path.cubicTo(971.676941,1476.99573, 971.466064,1480.00305, 968.343994,1481.53796);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=40 (964.185974,1484.77698 964.185974,1484.77698 965.573975,1482.90295 968.343994,1481.53796) t=0 [79] (964.185974,1484.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=40 from=(968.343994,1481.53796) to=(964.185974,1484.77698)
-path.cubicTo(965.573975,1482.90295, 964.185974,1484.77698, 964.185974,1484.77698);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=39 (963.177979,1485.70105 963.606995,1485.70105 964.185974,1484.77698 964.185974,1484.77698) t=0 [77] (963.177979,1485.70105) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=39 from=(964.185974,1484.77698) to=(963.177979,1485.70105)
-path.cubicTo(964.185974,1484.77698, 963.606995,1485.70105, 963.177979,1485.70105);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=38 (962.166992,1484.77698 962.166992,1484.77698 962.747986,1485.70105 963.177979,1485.70105) t=0 [75] (962.166992,1484.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=38 from=(963.177979,1485.70105) to=(962.166992,1484.77698)
-path.cubicTo(962.747986,1485.70105, 962.166992,1484.77698, 962.166992,1484.77698);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=37 (962.0755,1484.66101 962.136475,1484.73572 962.166992,1484.77698 962.166992,1484.77698) t=0 [73] (962.0755,1484.66101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=37 from=(962.166992,1484.77698) to=(962.0755,1484.66101)
-path.cubicTo(962.166992,1484.77698, 962.136475,1484.73572, 962.0755,1484.66101);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=36 (958.008972,1481.53796 960.369873,1482.70056 961.725403,1484.2323 962.0755,1484.66101) t=0 [71] (958.008972,1481.53796) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=36 from=(962.0755,1484.66101) to=(958.008972,1481.53796)
-path.cubicTo(961.725403,1484.2323, 960.369873,1482.70056, 958.008972,1481.53796);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=35 (954.666016,1476.78601 954.666016,1476.78601 954.780029,1479.94995 958.008972,1481.53796) t=0 [69] (954.666016,1476.78601) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=35 from=(958.008972,1481.53796) to=(954.666016,1476.78601)
-path.cubicTo(954.780029,1479.94995, 954.666016,1476.78601, 954.666016,1476.78601);
-path.close();
-SkOpSegment::debugShowActiveSpans id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 (947.392029,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 (951.289185,1468.29895) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 (951.361023,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [90/28] next=91/27 sect=17/17  s=1 [180] e=0 [179] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [91/27] next=90/28 sect=21/17  s=0 [181] e=1 [182] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=93 opp=0 tHit=0.286794409 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=90 index=179 endIndex=180 tHit=0.9 hitDx=-4.26454926 try=0 vert=0
-SkOpSegment::windingAtT id=93 opp=1 tHit=0.286794409 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=90 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 [179] (951.289185,1468.29895) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 [181] (951.361023,1468.29199) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 [177] (947.392029,1471.64197) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 [179] (951.289185,1468.29895) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=90 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=90 (951.289185,1468.29895 951.335754,1468.29382 951.361023,1468.29199 951.361023,1468.29199) t=0 [179] (951.289185,1468.29895) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=90 from=(951.289185,1468.29895) to=(951.361023,1468.29199)
-path.moveTo(951.289185,1468.29895);
-path.cubicTo(951.335754,1468.29382, 951.361023,1468.29199, 951.361023,1468.29199);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=91 (951.361023,1468.29199 950.554016,1471.98499 947.392029,1471.64197 947.392029,1471.64197) t=0 [181] (951.361023,1468.29199) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=91 from=(951.361023,1468.29199) to=(947.392029,1471.64197)
-path.cubicTo(950.554016,1471.98499, 947.392029,1471.64197, 947.392029,1471.64197);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=89 (947.392029,1471.64197 947.604919,1468.81628 950.769897,1468.35559 951.289185,1468.29895) t=0 [177] (947.392029,1471.64197) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=89 from=(947.392029,1471.64197) to=(951.289185,1468.29895)
-path.cubicTo(947.604919,1468.81628, 950.769897,1468.35559, 951.289185,1468.29895);
-path.close();
-SkOpSegment::debugShowActiveSpans id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 (974.919983,1468.29199) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 (978.890015,1471.64197) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [112/31] next=111/32 sect=25/29  s=1 [224] e=0 [223] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [111/32] next=112/31 sect=29/29  s=0 [221] e=1 [222] sgn=-1 windVal=1 windSum=? operand
-FindSortableTop current=111 index=221 endIndex=222 tHit=0.175786454 hitDx=0 try=1 vert=0
-SkOpSegment::windingAtT id=114 opp=0 tHit=0.428082798 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=111 index=221 endIndex=222 tHit=0.9 hitDx=-2.31073737 try=0 vert=0
-SkOpSegment::windingAtT id=114 opp=1 tHit=0.428082798 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=111 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 [221] (974.919983,1468.29199) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 [223] (978.890015,1471.64197) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 [221] (974.919983,1468.29199) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=111 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=111 (974.919983,1468.29199 974.919983,1468.29199 978.658997,1468.56299 978.890015,1471.64197) t=0 [221] (974.919983,1468.29199) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=111 from=(974.919983,1468.29199) to=(978.890015,1471.64197)
-path.moveTo(974.919983,1468.29199);
-path.cubicTo(974.919983,1468.29199, 978.658997,1468.56299, 978.890015,1471.64197);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=112 (978.890015,1471.64197 978.890015,1471.64197 975.72699,1471.98499 974.919983,1468.29199) t=0 [223] (978.890015,1471.64197) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=112 from=(978.890015,1471.64197) to=(974.919983,1468.29199)
-path.cubicTo(978.890015,1471.64197, 975.72699,1471.98499, 974.919983,1468.29199);
-path.close();
-SkOpSegment::debugShowActiveSpans id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 (945.382019,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 (944.492004,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [84/36] next=85/35 sect=21/25  s=1 [168] e=0 [167] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [85/35] next=84/36 sect=29/25  s=0 [169] e=1 [170] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.0784218528 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=84 index=167 endIndex=168 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.0784218528 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=84 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 [167] (945.382019,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 [169] (944.492004,1469.48706) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 [167] (945.382019,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=84 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=84 (945.382019,1474.328 942.924011,1472.729 944.492004,1469.48706 944.492004,1469.48706) t=0 [167] (945.382019,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=84 from=(945.382019,1474.328) to=(944.492004,1469.48706)
-path.moveTo(945.382019,1474.328);
-path.cubicTo(942.924011,1472.729, 944.492004,1469.48706, 944.492004,1469.48706);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=85 (944.492004,1469.48706 947.388977,1471.95703 945.382019,1474.328 945.382019,1474.328) t=0 [169] (944.492004,1469.48706) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=85 from=(944.492004,1469.48706) to=(945.382019,1474.328)
-path.cubicTo(947.388977,1471.95703, 945.382019,1474.328, 945.382019,1474.328);
-path.close();
-SkOpSegment::debugShowActiveSpans id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 (980.900024,1474.328) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 (981.791016,1469.48706) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [116/39] next=115/40 sect=6/25  s=0 [231] e=1 [232] sgn=-1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [115/40] next=116/39 sect=17/21  s=1 [230] e=0 [229] sgn=1 windVal=1 windSum=? operand
-SkOpSegment::findTop swap=1 inflections=0 monotonic=0
-FindSortableTop current=115 index=229 endIndex=230 tHit=0.967308254 hitDx=0 try=1 vert=0
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.910496105 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=115 index=229 endIndex=230 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.910496105 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=115 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 [229] (980.900024,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 [231] (981.791016,1469.48706) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 [229] (980.900024,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=115 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=115 (980.900024,1474.328 980.900024,1474.328 978.893005,1471.95703 981.791016,1469.48706) t=0 [229] (980.900024,1474.328) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=115 from=(980.900024,1474.328) to=(981.791016,1469.48706)
-path.moveTo(980.900024,1474.328);
-path.cubicTo(980.900024,1474.328, 978.893005,1471.95703, 981.791016,1469.48706);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=116 (981.791016,1469.48706 981.791016,1469.48596 983.358032,1472.729 980.900024,1474.328) t=0 [231] (981.791016,1469.48706) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=116 from=(981.791016,1469.48706) to=(980.900024,1474.328)
-path.cubicTo(981.791016,1469.48596, 983.358032,1472.729, 980.900024,1474.328);
-path.close();
-SkOpSegment::debugShowActiveSpans id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 (946.054016,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 (949.046997,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 (946.127258,1476.22852) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [71/44] next=72/43 sect=17/21  s=1 [142] e=0 [141] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [72/43] next=71/44 sect=25/21  s=0 [143] e=1 [144] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=91 opp=0 tHit=0.523045686 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=71 index=141 endIndex=142 tHit=0.9 hitDx=-5.28365183 try=0 vert=0
-SkOpSegment::windingAtT id=91 opp=1 tHit=0.523045686 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=71 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 [141] (946.054016,1476.229) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 [143] (949.046997,1471.97095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 [145] (946.127258,1476.22852) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 [141] (946.054016,1476.229) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=71 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=71 (946.054016,1476.229 945.61499,1473.12903 949.046997,1471.97095 949.046997,1471.97095) t=0 [141] (946.054016,1476.229) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=71 from=(946.054016,1476.229) to=(949.046997,1471.97095)
-path.moveTo(946.054016,1476.229);
-path.cubicTo(945.61499,1473.12903, 949.046997,1471.97095, 949.046997,1471.97095);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=72 (949.046997,1471.97095 949.191528,1475.95117 946.599548,1476.21362 946.127258,1476.22852) t=0 [143] (949.046997,1471.97095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=72 from=(949.046997,1471.97095) to=(946.127258,1476.22852)
-path.cubicTo(949.191528,1475.95117, 946.599548,1476.21362, 946.127258,1476.22852);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=73 (946.127258,1476.22852 946.080078,1476.22998 946.054016,1476.229 946.054016,1476.229) t=0 [145] (946.127258,1476.22852) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=73 from=(946.127258,1476.22852) to=(946.054016,1476.229)
-path.cubicTo(946.080078,1476.22998, 946.054016,1476.229, 946.054016,1476.229);
-path.close();
-SkOpSegment::debugShowActiveSpans id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 (980.226013,1476.229) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 (977.234985,1471.97095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [11/48] next=12/47 sect=21/25  s=1 [22] e=0 [21] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [12/47] next=11/48 sect=29/25  s=0 [23] e=1 [24] sgn=-1 windVal=1 windSum=? operand
-FindSortableTop current=12 index=23 endIndex=24 tHit=0.0682163132 hitDx=0 try=1 vert=0
-SkOpSegment::windingAtT id=115 opp=0 tHit=0.511875601 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=12 index=23 endIndex=24 tHit=0.9 hitDx=-0.730849624 try=0 vert=0
-SkOpSegment::windingAtT id=115 opp=1 tHit=0.511875601 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=12 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 [23] (977.234985,1471.97095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 [21] (980.226013,1476.229) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 [23] (977.234985,1471.97095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=12 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=12 (977.234985,1471.97095 977.234985,1471.97095 980.666992,1473.12903 980.226013,1476.229) t=0 [23] (977.234985,1471.97095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=12 from=(977.234985,1471.97095) to=(980.226013,1476.229)
-path.moveTo(977.234985,1471.97095);
-path.cubicTo(977.234985,1471.97095, 980.666992,1473.12903, 980.226013,1476.229);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=11 (980.226013,1476.229 980.226013,1476.229 977.078003,1476.349 977.234985,1471.97095) t=0 [21] (980.226013,1476.229) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=11 from=(980.226013,1476.229) to=(977.234985,1471.97095)
-path.cubicTo(980.226013,1476.229, 977.078003,1476.349, 977.234985,1471.97095);
-path.close();
-SkOpSegment::debugShowActiveSpans id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 (945.312988,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 (942.638794,1473.97607) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 (942.651001,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [82/52] next=83/51 sect=21/21  s=1 [164] e=0 [163] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [83/51] next=82/52 sect=29/25  s=0 [165] e=1 [166] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.037518588 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=82 index=163 endIndex=164 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.037518588 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=82 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 [163] (942.638794,1473.97607) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 [165] (942.651001,1473.87805) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 [161] (945.312988,1478.18005) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 [163] (942.638794,1473.97607) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=82 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=82 (942.638794,1473.97607 942.645691,1473.91284 942.651001,1473.87805 942.651001,1473.87805) t=0 [163] (942.638794,1473.97607) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=82 from=(942.638794,1473.97607) to=(942.651001,1473.87805)
-path.moveTo(942.638794,1473.97607);
-path.cubicTo(942.645691,1473.91284, 942.651001,1473.87805, 942.651001,1473.87805);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=83 (942.651001,1473.87805 946.562988,1475.66199 945.312988,1478.18005 945.312988,1478.18005) t=0 [165] (942.651001,1473.87805) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=83 from=(942.651001,1473.87805) to=(945.312988,1478.18005)
-path.cubicTo(946.562988,1475.66199, 945.312988,1478.18005, 945.312988,1478.18005);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=81 (945.312988,1478.18005 942.359741,1477.83667 942.572632,1474.58496 942.638794,1473.97607) t=0 [161] (945.312988,1478.18005) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=81 from=(945.312988,1478.18005) to=(942.638794,1473.97607)
-path.cubicTo(942.359741,1477.83667, 942.572632,1474.58496, 942.638794,1473.97607);
-path.close();
-SkOpSegment::debugShowActiveSpans id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 (980.968994,1478.18005) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 (983.632019,1473.87805) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [117/56] next=118/55 sect=17/21  s=1 [234] e=0 [233] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [118/55] next=117/56 sect=25/21  s=0 [235] e=1 [236] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.945598256 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=117 index=233 endIndex=234 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.945598256 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=117 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 [233] (980.968994,1478.18005) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 [235] (983.632019,1473.87805) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 [233] (980.968994,1478.18005) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=117 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=117 (980.968994,1478.18005 980.968994,1478.18005 979.718018,1475.66199 983.632019,1473.87805) t=0 [233] (980.968994,1478.18005) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=117 from=(980.968994,1478.18005) to=(983.632019,1473.87805)
-path.moveTo(980.968994,1478.18005);
-path.cubicTo(980.968994,1478.18005, 979.718018,1475.66199, 983.632019,1473.87805);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=118 (983.632019,1473.87805 983.632019,1473.87805 984.229004,1477.80103 980.968994,1478.18005) t=0 [235] (983.632019,1473.87805) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=118 from=(983.632019,1473.87805) to=(980.968994,1478.18005)
-path.cubicTo(983.632019,1473.87805, 984.229004,1477.80103, 980.968994,1478.18005);
-path.close();
-SkOpSegment::debugShowActiveSpans id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 (947.070984,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 (948.786011,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 (947.129333,1480.43652) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [68/60] next=69/59 sect=17/21  s=1 [136] e=0 [135] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [69/59] next=68/60 sect=25/21  s=0 [137] e=1 [138] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=72 opp=0 tHit=0.260810896 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=68 index=135 endIndex=136 tHit=0.9 hitDx=-2.85768771 try=0 vert=0
-SkOpSegment::windingAtT id=72 opp=1 tHit=0.260810896 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=68 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 [135] (947.070984,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 [137] (948.786011,1475.59497) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 [139] (947.129333,1480.43652) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 [135] (947.070984,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=68 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=68 (947.070984,1480.45496 945.211975,1477.88501 948.786011,1475.59497 948.786011,1475.59497) t=0 [135] (947.070984,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=68 from=(947.070984,1480.45496) to=(948.786011,1475.59497)
-path.moveTo(947.070984,1480.45496);
-path.cubicTo(945.211975,1477.88501, 948.786011,1475.59497, 948.786011,1475.59497);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=69 (948.786011,1475.59497 949.835938,1479.33569 947.530884,1480.29919 947.129333,1480.43652) t=0 [137] (948.786011,1475.59497) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=69 from=(948.786011,1475.59497) to=(947.129333,1480.43652)
-path.cubicTo(949.835938,1479.33569, 947.530884,1480.29919, 947.129333,1480.43652);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=70 (947.129333,1480.43652 947.091858,1480.44934 947.070984,1480.45496 947.070984,1480.45496) t=0 [139] (947.129333,1480.43652) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=70 from=(947.129333,1480.43652) to=(947.070984,1480.45496)
-path.cubicTo(947.091858,1480.44934, 947.070984,1480.45496, 947.070984,1480.45496);
-path.close();
-SkOpSegment::debugShowActiveSpans id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 (979.211975,1480.45496) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 (977.495972,1475.59497) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 (977.517029,1475.60864) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [5/64] next=6/63 sect=21/25  s=1 [10] e=0 [9] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [6/63] next=5/64 sect=31/31  s=0 [11] e=1 [12] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=11 opp=0 tHit=0.912541398 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=5 index=9 endIndex=10 tHit=0.9 hitDx=-1.11527574 try=0 vert=0
-SkOpSegment::windingAtT id=11 opp=1 tHit=0.912541398 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=5 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 [9] (979.211975,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 [11] (977.495972,1475.59497) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 [13] (977.517029,1475.60864) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 [9] (979.211975,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=5 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=5 (979.211975,1480.45496 979.211975,1480.45496 976.348999,1479.68506 977.495972,1475.59497) t=0 [9] (979.211975,1480.45496) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=5 from=(979.211975,1480.45496) to=(977.495972,1475.59497)
-path.moveTo(979.211975,1480.45496);
-path.cubicTo(979.211975,1480.45496, 976.348999,1479.68506, 977.495972,1475.59497);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=6 (977.495972,1475.59497 977.496033,1475.59497 977.503296,1475.59961 977.517029,1475.60864) t=0 [11] (977.495972,1475.59497) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=6 from=(977.495972,1475.59497) to=(977.517029,1475.60864)
-path.cubicTo(977.496033,1475.59497, 977.503296,1475.59961, 977.517029,1475.60864);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=7 (977.517029,1475.60864 977.807861,1475.80164 980.988281,1478.00073 979.211975,1480.45496) t=0 [13] (977.517029,1475.60864) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=7 from=(977.517029,1475.60864) to=(979.211975,1480.45496)
-path.cubicTo(977.807861,1475.80164, 980.988281,1478.00073, 979.211975,1480.45496);
-path.close();
-SkOpSegment::debugShowActiveSpans id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 (946.255005,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 (941.736023,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 (946.25769,1481.2561) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [78/68] next=79/67 sect=25/29  s=1 [156] e=0 [155] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [79/67] next=78/68 sect=29/29  s=0 [157] e=1 [158] sgn=-1 windVal=1 windSum=? operand
-FindSortableTop current=79 index=157 endIndex=158 tHit=0.0029007474 hitDx=0 try=1 vert=0
-contourRangeCheckY [79] mid=0.9->0.869170195 s=0 (941.736023,1478.31494) m=0.9 (946.266724,1481.11377) n=0.869170195 (946.266724,1481.04578) e=1 (946.25769,1481.2561)
-SkOpSegment::windingAtT id=72 opp=0 tHit=0.925227745 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=79 index=157 endIndex=158 tHit=0.869170195 hitDx=-2.28638268 try=0 vert=0
-contourRangeCheckY [79] mid=0.9->0.869170195 s=0 (941.736023,1478.31494) m=0.9 (946.266724,1481.11377) n=0.869170195 (946.266724,1481.04578) e=1 (946.25769,1481.2561)
-SkOpSegment::windingAtT id=72 opp=1 tHit=0.925227745 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=79 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 [157] (941.736023,1478.31494) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 [159] (946.25769,1481.2561) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 [155] (946.255005,1481.276) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 [157] (941.736023,1478.31494) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=79 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=79 (941.736023,1478.31494 946.484619,1478.38538 946.288147,1481.00122 946.25769,1481.2561) t=0 [157] (941.736023,1478.31494) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=79 from=(941.736023,1478.31494) to=(946.25769,1481.2561)
-path.moveTo(941.736023,1478.31494);
-path.cubicTo(946.484619,1478.38538, 946.288147,1481.00122, 946.25769,1481.2561);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=80 (946.25769,1481.2561 946.256104,1481.26917 946.255005,1481.276 946.255005,1481.276) t=0 [159] (946.25769,1481.2561) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=80 from=(946.25769,1481.2561) to=(946.255005,1481.276)
-path.cubicTo(946.256104,1481.26917, 946.255005,1481.276, 946.255005,1481.276);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=78 (946.255005,1481.276 943.094971,1481.93396 941.736023,1478.31494 941.736023,1478.31494) t=0 [155] (946.255005,1481.276) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=78 from=(946.255005,1481.276) to=(941.736023,1478.31494)
-path.cubicTo(943.094971,1481.93396, 941.736023,1478.31494, 941.736023,1478.31494);
-path.close();
-SkOpSegment::debugShowActiveSpans id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 (984.546021,1478.31494) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 (980.025818,1481.27441) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [15/71] next=13/72 sect=17/17  s=1 [30] e=0 [29] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [13/72] next=15/71 sect=21/17  s=0 [25] e=1 [26] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=118 opp=0 tHit=0.631980856 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=15 index=29 endIndex=30 tHit=0.9 hitDx=-3.07305765 try=0 vert=0
-SkOpSegment::windingAtT id=118 opp=1 tHit=0.631980856 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=15 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 [29] (980.025818,1481.27441) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 [25] (984.546021,1478.31494) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 [27] (980.026001,1481.276) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 [29] (980.025818,1481.27441) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=15 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=15 (980.025818,1481.27441 980.014954,1481.1969 979.623779,1478.38806 984.546021,1478.31494) t=0 [29] (980.025818,1481.27441) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=15 from=(980.025818,1481.27441) to=(984.546021,1478.31494)
-path.moveTo(980.025818,1481.27441);
-path.cubicTo(980.014954,1481.1969, 979.623779,1478.38806, 984.546021,1478.31494);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=13 (984.546021,1478.31494 984.546021,1478.31494 983.187988,1481.93396 980.026001,1481.276) t=0 [25] (984.546021,1478.31494) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=13 from=(984.546021,1478.31494) to=(980.026001,1481.276)
-path.cubicTo(984.546021,1478.31494, 983.187988,1481.93396, 980.025818,1481.27441);
-path.close();
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 (948.427002,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 (949.567993,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [74/76] next=75/75 sect=17/21  s=1 [148] e=0 [147] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [75/75] next=74/76 sect=25/21  s=0 [149] e=1 [150] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=91 opp=0 tHit=0.424462136 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=74 index=147 endIndex=148 tHit=0.9 hitDx=-5.43667603 try=0 vert=0
-SkOpSegment::windingAtT id=91 opp=1 tHit=0.424462136 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=74 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 [147] (948.427002,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 [149] (949.567993,1479.35205) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 [147] (948.427002,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=74 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=74 (948.427002,1484.453 946.440002,1482.23499 949.567993,1479.35205 949.567993,1479.35205) t=0 [147] (948.427002,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=74 from=(948.427002,1484.453) to=(949.567993,1479.35205)
-path.moveTo(948.427002,1484.453);
-path.cubicTo(946.440002,1482.23499, 949.567993,1479.35205, 949.567993,1479.35205);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=75 (949.567993,1479.35205 951.015991,1483.26099 948.427002,1484.453 948.427002,1484.453) t=0 [149] (949.567993,1479.35205) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=75 from=(949.567993,1479.35205) to=(948.427002,1484.453)
-path.cubicTo(951.015991,1483.26099, 948.427002,1484.453, 948.427002,1484.453);
-path.close();
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 (977.854004,1484.453) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 (976.713989,1479.35205) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 (976.716125,1479.35413) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [8/80] next=9/79 sect=21/25  s=1 [16] e=0 [15] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [9/79] next=8/80 sect=28/29  s=0 [17] e=1 [18] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=112 opp=0 tHit=0.650469079 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=8 index=15 endIndex=16 tHit=0.9 hitDx=-5.33921242 try=0 vert=0
-SkOpSegment::windingAtT id=112 opp=1 tHit=0.650469079 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=8 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 [15] (977.854004,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 [17] (976.713989,1479.35205) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 [19] (976.716125,1479.35413) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 [15] (977.854004,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=8 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=8 (977.854004,1484.453 977.854004,1484.453 975.265991,1483.26099 976.713989,1479.35205) t=0 [15] (977.854004,1484.453) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=8 from=(977.854004,1484.453) to=(976.713989,1479.35205)
-path.moveTo(977.854004,1484.453);
-path.cubicTo(977.854004,1484.453, 975.265991,1483.26099, 976.713989,1479.35205);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=9 (976.713989,1479.35205 976.713989,1479.35205 976.714722,1479.35278 976.716125,1479.35413) t=0 [17] (976.713989,1479.35205) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=9 from=(976.713989,1479.35205) to=(976.716125,1479.35413)
-path.cubicTo(976.713989,1479.35205, 976.714722,1479.35278, 976.716125,1479.35413);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=10 (976.716125,1479.35413 976.807983,1479.44055 979.811707,1482.26868 977.854004,1484.453) t=0 [19] (976.716125,1479.35413) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=10 from=(976.716125,1479.35413) to=(977.854004,1484.453)
-path.cubicTo(976.807983,1479.44055, 979.811707,1482.26868, 977.854004,1484.453);
-path.close();
-SkOpSegment::debugShowActiveSpans id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 (980.026001,1481.276) tEnd=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [15/73] next=14/74 sect=9/1  s=0 [29] e=1 [30] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done operand
-SkOpAngle::dumpOne [14/74] next=15/73 sect=25/25  s=1 [28] e=0 [27] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand stop
-SkOpSegment::activeOp id=14 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=14 (980.026001,1481.276 980.026001,1481.276 980.02594,1481.27551 980.025818,1481.27441) t=0 [27] (980.026001,1481.276) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=14 from=(980.026001,1481.276) to=(980.025818,1481.27441)
-path.moveTo(980.026001,1481.276);
-path.lineTo(980.025818,1481.27441);
-SkOpSegment::debugShowActiveSpans id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 (947.294006,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 (942.495972,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [77/83] next=76/84 sect=9/13  s=1 [154] e=0 [153] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [76/84] next=77/83 sect=17/13  s=0 [151] e=1 [152] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=69 opp=0 tHit=0.907098883 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=77 index=153 endIndex=154 tHit=0.9 hitDx=-2.12952447 try=0 vert=0
-SkOpSegment::windingAtT id=69 opp=1 tHit=0.907098883 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=77 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 [153] (942.495972,1481.823) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 [151] (947.294006,1484.198) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 [153] (942.495972,1481.823) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=77 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=77 (942.495972,1481.823 947.187988,1481.33496 947.294006,1484.198 947.294006,1484.198) t=0 [153] (942.495972,1481.823) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=77 from=(942.495972,1481.823) to=(947.294006,1484.198)
-path.moveTo(942.495972,1481.823);
-path.cubicTo(947.187988,1481.33496, 947.294006,1484.198, 947.294006,1484.198);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=76 (947.294006,1484.198 944.210999,1485.49805 942.495972,1481.823 942.495972,1481.823) t=0 [151] (947.294006,1484.198) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=76 from=(947.294006,1484.198) to=(942.495972,1481.823)
-path.cubicTo(944.210999,1485.49805, 942.495972,1481.823, 942.495972,1481.823);
-path.close();
-SkOpSegment::debugShowActiveSpans id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 (978.989014,1484.198) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 (983.786011,1481.823) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [16/87] next=17/88 sect=13/17  s=1 [32] e=0 [31] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [17/88] next=16/87 sect=21/17  s=0 [33] e=1 [34] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=13 opp=0 tHit=0.681648299 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=16 index=31 endIndex=32 tHit=0.9 hitDx=-6.17578888 try=0 vert=0
-SkOpSegment::windingAtT id=13 opp=1 tHit=0.681648299 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=16 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 [31] (978.989014,1484.198) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 [33] (983.786011,1481.823) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 [31] (978.989014,1484.198) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=16 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=16 (978.989014,1484.198 978.989014,1484.198 979.094971,1481.33496 983.786011,1481.823) t=0 [31] (978.989014,1484.198) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=16 from=(978.989014,1484.198) to=(983.786011,1481.823)
-path.moveTo(978.989014,1484.198);
-path.cubicTo(978.989014,1484.198, 979.094971,1481.33496, 983.786011,1481.823);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=17 (983.786011,1481.823 983.786011,1481.823 982.070007,1485.49805 978.989014,1484.198) t=0 [33] (983.786011,1481.823) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=17 from=(983.786011,1481.823) to=(978.989014,1484.198)
-path.cubicTo(983.786011,1481.823, 982.070007,1485.49805, 978.989014,1484.198);
-path.close();
-SkOpSegment::debugShowActiveSpans id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 (951.176025,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 (951.290283,1481.86658) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 (951.361023,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 (951.186646,1486.97144) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [96/92] next=97/91 sect=21/21  s=1 [192] e=0 [191] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [97/91] next=96/92 sect=25/21  s=0 [193] e=1 [194] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=91 opp=0 tHit=0.000327423835 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=96 index=191 endIndex=192 tHit=0.9 hitDx=-2.42564511 try=0 vert=0
-SkOpSegment::windingAtT id=91 opp=1 tHit=0.000327423835 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=96 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 [191] (951.290283,1481.86658) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 [193] (951.361023,1481.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 [195] (951.186646,1486.97144) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 [189] (951.176025,1486.97803) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 [191] (951.290283,1481.86658) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=96 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=96 (951.290283,1481.86658 951.334778,1481.80811 951.361023,1481.77698 951.361023,1481.77698) t=0 [191] (951.290283,1481.86658) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=96 from=(951.290283,1481.86658) to=(951.361023,1481.77698)
-path.moveTo(951.290283,1481.86658);
-path.cubicTo(951.334778,1481.80811, 951.361023,1481.77698, 951.361023,1481.77698);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=97 (951.361023,1481.77698 953.644836,1485.34509 951.363281,1486.86157 951.186646,1486.97144) t=0 [193] (951.361023,1481.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=97 from=(951.361023,1481.77698) to=(951.186646,1486.97144)
-path.cubicTo(953.644836,1485.34509, 951.363281,1486.86157, 951.186646,1486.97144);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=98 (951.186646,1486.97144 951.179688,1486.97583 951.176025,1486.97803 951.176025,1486.97803) t=0 [195] (951.186646,1486.97144) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=98 from=(951.186646,1486.97144) to=(951.176025,1486.97803)
-path.cubicTo(951.179688,1486.97583, 951.176025,1486.97803, 951.176025,1486.97803);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=95 (951.176025,1486.97803 949.194519,1484.8667 950.909729,1482.36658 951.290283,1481.86658) t=0 [189] (951.176025,1486.97803) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=95 from=(951.176025,1486.97803) to=(951.290283,1481.86658)
-path.cubicTo(949.194519,1484.8667, 950.909729,1482.36658, 951.290283,1481.86658);
-path.close();
-SkOpSegment::debugShowActiveSpans id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 (975.106995,1486.97803) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 (974.919983,1481.77698) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [107/96] next=108/95 sect=21/25  s=1 [214] e=0 [213] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [108/95] next=107/96 sect=6/25  s=0 [215] e=1 [216] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=110 opp=0 tHit=0.873323816 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=107 index=213 endIndex=214 tHit=0.9 hitDx=-3.36580896 try=0 vert=0
-SkOpSegment::windingAtT id=110 opp=1 tHit=0.873323816 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=107 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 [213] (975.106995,1486.97803) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 [215] (974.919983,1481.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 [213] (975.106995,1486.97803) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=107 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=107 (975.106995,1486.97803 975.106995,1486.97803 972.546997,1485.48706 974.919983,1481.77698) t=0 [213] (975.106995,1486.97803) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=107 from=(975.106995,1486.97803) to=(974.919983,1481.77698)
-path.moveTo(975.106995,1486.97803);
-path.cubicTo(975.106995,1486.97803, 972.546997,1485.48706, 974.919983,1481.77698);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=108 (974.919983,1481.77698 974.919983,1481.776 977.31897,1484.61902 975.106995,1486.97803) t=0 [215] (974.919983,1481.77698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=108 from=(974.919983,1481.77698) to=(975.106995,1486.97803)
-path.cubicTo(974.919983,1481.776, 977.31897,1484.61902, 975.106995,1486.97803);
-path.close();
-SkOpSegment::debugShowActiveSpans id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 (955.120972,1488.94495) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 (953.458984,1483.93604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [101/100] next=102/99 sect=21/25  s=1 [202] e=0 [201] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [102/99] next=101/100 sect=29/25  s=0 [203] e=1 [204] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.282491511 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=101 index=201 endIndex=202 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.282491511 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=101 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 [201] (955.120972,1488.94495) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 [203] (953.458984,1483.93604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 [201] (955.120972,1488.94495) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=101 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=101 (955.120972,1488.94495 952.309021,1487.98303 953.458984,1483.93604 953.458984,1483.93604) t=0 [201] (955.120972,1488.94495) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=101 from=(955.120972,1488.94495) to=(953.458984,1483.93604)
-path.moveTo(955.120972,1488.94495);
-path.cubicTo(952.309021,1487.98303, 953.458984,1483.93604, 953.458984,1483.93604);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=102 (953.458984,1483.93604 957.004028,1486.37097 955.120972,1488.94495 955.120972,1488.94495) t=0 [203] (953.458984,1483.93604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=102 from=(953.458984,1483.93604) to=(955.120972,1488.94495)
-path.cubicTo(957.004028,1486.37097, 955.120972,1488.94495, 955.120972,1488.94495);
-path.close();
-SkOpSegment::debugShowActiveSpans id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 (972.825012,1483.93701) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 (971.161987,1488.94604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 (971.141846,1488.9165) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [25/103] next=23/104 sect=17/21  s=1 [50] e=0 [49] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [23/104] next=25/103 sect=25/21  s=0 [45] e=1 [46] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=48 opp=0 tHit=0.531455174 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=25 index=49 endIndex=50 tHit=0.9 hitDx=-3.19249606 try=0 vert=0
-SkOpSegment::windingAtT id=48 opp=1 tHit=0.531455174 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=25 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 [49] (971.141846,1488.9165) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 [45] (972.825012,1483.93701) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 [47] (971.161987,1488.94604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 [49] (971.141846,1488.9165) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=25 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=25 (971.141846,1488.9165 970.948425,1488.625 969.49884,1486.21948 972.825012,1483.93701) t=0 [49] (971.141846,1488.9165) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=25 from=(971.141846,1488.9165) to=(972.825012,1483.93701)
-path.moveTo(971.141846,1488.9165);
-path.cubicTo(970.948425,1488.625, 969.49884,1486.21948, 972.825012,1483.93701);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=23 (972.825012,1483.93701 972.825012,1483.93701 973.971985,1487.98401 971.161987,1488.94604) t=0 [45] (972.825012,1483.93701) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=23 from=(972.825012,1483.93701) to=(971.161987,1488.94604)
-path.cubicTo(972.825012,1483.93701, 973.971985,1487.98401, 971.161987,1488.94604);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=24 (971.161987,1488.94604 971.161987,1488.94592 971.154663,1488.93591 971.141846,1488.9165) t=0 [47] (971.161987,1488.94604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=24 from=(971.161987,1488.94604) to=(971.141846,1488.9165)
-path.cubicTo(971.161987,1488.94592, 971.154663,1488.93591, 971.141846,1488.9165);
-path.close();
-SkOpSegment::debugShowActiveSpans id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 (949.890991,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 (944.608215,1485.375) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 (944.604004,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [67/107] next=65/108 sect=9/13  s=1 [134] e=0 [133] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [65/108] next=67/107 sect=17/13  s=0 [129] e=1 [130] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=75 opp=0 tHit=0.356952125 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=67 index=133 endIndex=134 tHit=0.9 hitDx=-1.76933026 try=0 vert=0
-SkOpSegment::windingAtT id=75 opp=1 tHit=0.356952125 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=67 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 [133] (944.604004,1485.37) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 [129] (949.890991,1486.86804) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 [131] (944.608215,1485.375) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 [133] (944.604004,1485.37) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=67 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=67 (944.604004,1485.37 949.562012,1484.06494 949.890991,1486.86804 949.890991,1486.86804) t=0 [133] (944.604004,1485.37) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=67 from=(944.604004,1485.37) to=(949.890991,1486.86804)
-path.moveTo(944.604004,1485.37);
-path.cubicTo(949.562012,1484.06494, 949.890991,1486.86804, 949.890991,1486.86804);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=65 (949.890991,1486.86804 947.178772,1488.37146 944.723022,1485.51147 944.608215,1485.375) t=0 [129] (949.890991,1486.86804) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=65 from=(949.890991,1486.86804) to=(944.608215,1485.375)
-path.cubicTo(947.178772,1488.37146, 944.723022,1485.51147, 944.608215,1485.375);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=66 (944.608215,1485.375 944.605408,1485.3717 944.604004,1485.37 944.604004,1485.37) t=0 [131] (944.608215,1485.375) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=66 from=(944.608215,1485.375) to=(944.604004,1485.37)
-path.cubicTo(944.605408,1485.3717, 944.604004,1485.37, 944.604004,1485.37);
-path.close();
-SkOpSegment::debugShowActiveSpans id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 (976.393005,1486.86804) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 (981.679016,1485.37) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [18/111] next=19/112 sect=13/17  s=1 [36] e=0 [35] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [19/112] next=18/111 sect=21/17  s=0 [37] e=1 [38] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=17 opp=0 tHit=0.844496375 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=18 index=35 endIndex=36 tHit=0.9 hitDx=-7.94395161 try=0 vert=0
-SkOpSegment::windingAtT id=17 opp=1 tHit=0.844496375 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=18 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 [35] (976.393005,1486.86804) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 [37] (981.679016,1485.37) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 [35] (976.393005,1486.86804) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=18 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=18 (976.393005,1486.86804 976.393005,1486.86804 976.719971,1484.06494 981.679016,1485.37) t=0 [35] (976.393005,1486.86804) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=18 from=(976.393005,1486.86804) to=(981.679016,1485.37)
-path.moveTo(976.393005,1486.86804);
-path.cubicTo(976.393005,1486.86804, 976.719971,1484.06494, 981.679016,1485.37);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=19 (981.679016,1485.37 981.679016,1485.37 979.169983,1488.40796 976.393005,1486.86804) t=0 [37] (981.679016,1485.37) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=19 from=(981.679016,1485.37) to=(976.393005,1486.86804)
-path.cubicTo(981.679016,1485.37, 979.169983,1488.40796, 976.393005,1486.86804);
-path.close();
-SkOpSegment::debugShowActiveSpans id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 (960.68103,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 (956.417969,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [52/116] next=53/115 sect=25/29  s=1 [104] e=0 [103] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [53/115] next=52/116 sect=29/29  s=0 [105] e=1 [106] sgn=-1 windVal=1 windSum=? operand
-FindSortableTop current=53 index=105 endIndex=106 tHit=0.00230789847 hitDx=0 try=1 vert=0
-contourRangeCheckY [53] mid=0.9->0.68738267 s=0 (956.417969,1486.75) m=0.9 (960.696289,1489.90637) n=0.68738267 (960.696289,1489.32324) e=1 (960.68103,1489.98499)
-SkOpSegment::windingAtT id=42 opp=0 tHit=0.555775366 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=53 index=105 endIndex=106 tHit=0.68738267 hitDx=-5.07717228 try=0 vert=0
-contourRangeCheckY [53] mid=0.9->0.68738267 s=0 (956.417969,1486.75) m=0.9 (960.696289,1489.90637) n=0.68738267 (960.696289,1489.32324) e=1 (960.68103,1489.98499)
-SkOpSegment::windingAtT id=42 opp=1 tHit=0.555775366 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=53 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 [105] (956.417969,1486.75) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 [103] (960.68103,1489.98499) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 [105] (956.417969,1486.75) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=53 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=53 (956.417969,1486.75 961.403015,1487.19202 960.68103,1489.98499 960.68103,1489.98499) t=0 [105] (956.417969,1486.75) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=53 from=(956.417969,1486.75) to=(960.68103,1489.98499)
-path.moveTo(956.417969,1486.75);
-path.cubicTo(961.403015,1487.19202, 960.68103,1489.98499, 960.68103,1489.98499);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=52 (960.68103,1489.98499 957.533997,1490.672 956.417969,1486.75 956.417969,1486.75) t=0 [103] (960.68103,1489.98499) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=52 from=(960.68103,1489.98499) to=(956.417969,1486.75)
-path.cubicTo(957.533997,1490.672, 956.417969,1486.75, 956.417969,1486.75);
-path.close();
-SkOpSegment::debugShowActiveSpans id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 (965.60199,1489.98499) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 (969.864014,1486.75) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [26/120] next=27/119 sect=17/17  s=1 [52] e=0 [51] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [27/119] next=26/120 sect=21/17  s=0 [53] e=1 [54] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=49 opp=0 tHit=0.0462757563 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=26 index=51 endIndex=52 tHit=0.9 hitDx=-7.59155035 try=0 vert=0
-SkOpSegment::windingAtT id=49 opp=1 tHit=0.0462757563 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=26 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 [51] (965.60199,1489.98499) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 [53] (969.864014,1486.75) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 [51] (965.60199,1489.98499) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=26 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=26 (965.60199,1489.98499 965.60199,1489.98499 964.879028,1487.19202 969.864014,1486.75) t=0 [51] (965.60199,1489.98499) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=26 from=(965.60199,1489.98499) to=(969.864014,1486.75)
-path.moveTo(965.60199,1489.98499);
-path.cubicTo(965.60199,1489.98499, 964.879028,1487.19202, 969.864014,1486.75);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=27 (969.864014,1486.75 969.864014,1486.75 968.749023,1490.672 965.60199,1489.98499) t=0 [53] (969.864014,1486.75) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=27 from=(969.864014,1486.75) to=(965.60199,1489.98499)
-path.cubicTo(969.864014,1486.75, 968.749023,1490.672, 965.60199,1489.98499);
-path.close();
-SkOpSegment::debugShowActiveSpans id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 (947.51001,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 (953.234009,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [99/123] next=100/124 sect=9/13  s=1 [198] e=0 [197] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [100/124] next=99/123 sect=17/13  s=0 [199] e=1 [200] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=2 opp=1 tHit=0.265362826 t=0 oldWinding=0 windValue=0 dx=+ winding=0
-FindSortableTop current=99 index=197 endIndex=198 tHit=0.9 hitDx=44 try=0 vert=0
-SkOpSegment::windingAtT id=2 opp=0 tHit=0.265362826 t=0 oldWinding=-1 windValue=1 dx=+ winding=-1
-SkOpSegment::initWinding id=99 oldWinding=0 hitDx=+ dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 [197] (947.51001,1488.53101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 [199] (953.234009,1489.08997) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 [197] (947.51001,1488.53101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=99 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=99 (947.51001,1488.53101 947.51001,1488.53101 951.596985,1486.32202 953.234009,1489.08997) t=0 [197] (947.51001,1488.53101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=99 from=(947.51001,1488.53101) to=(953.234009,1489.08997)
-path.moveTo(947.51001,1488.53101);
-path.cubicTo(947.51001,1488.53101, 951.596985,1486.32202, 953.234009,1489.08997);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=100 (953.234009,1489.08997 953.234009,1489.08997 951.158997,1491.03601 947.51001,1488.53101) t=0 [199] (953.234009,1489.08997) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=100 from=(953.234009,1489.08997) to=(947.51001,1488.53101)
-path.cubicTo(953.234009,1489.08997, 951.158997,1491.03601, 947.51001,1488.53101);
-path.close();
-SkOpSegment::debugShowActiveSpans id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 (978.770996,1488.53101) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 (973.051086,1489.09277) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 (973.047974,1489.08997) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 (978.766052,1488.52844) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [105/127] next=106/128 sect=13/17  s=1 [210] e=0 [209] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [106/128] next=105/127 sect=29/29  s=0 [211] e=1 [212] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=19 opp=0 tHit=0.72037974 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=105 index=209 endIndex=210 tHit=0.9 hitDx=-7.35572147 try=0 vert=0
-SkOpSegment::windingAtT id=19 opp=1 tHit=0.72037974 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=105 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 [209] (973.047974,1489.08997) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 [211] (978.766052,1488.52844) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 [205] (978.770996,1488.53101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 [207] (973.051086,1489.09277) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 [209] (973.047974,1489.08997) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=105 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=105 (973.047974,1489.08997 974.651978,1486.37781 978.607178,1488.44397 978.766052,1488.52844) t=0 [209] (973.047974,1489.08997) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=105 from=(973.047974,1489.08997) to=(978.766052,1488.52844)
-path.moveTo(973.047974,1489.08997);
-path.cubicTo(974.651978,1486.37781, 978.607178,1488.44397, 978.766052,1488.52844);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=106 (978.766052,1488.52844 978.770996,1488.53101) t=0 [211] (978.766052,1488.52844) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=106 from=(978.766052,1488.52844) to=(978.770996,1488.53101)
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=103 (978.770996,1488.53101 975.204224,1490.98022 973.141174,1489.17444 973.051086,1489.09277) t=0 [205] (978.770996,1488.53101) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=103 from=(978.770996,1488.53101) to=(973.051086,1489.09277)
-path.lineTo(978.770996,1488.53101);
-path.cubicTo(975.204224,1490.98022, 973.141174,1489.17444, 973.051086,1489.09277);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=104 (973.051086,1489.09277 973.049011,1489.09094 973.047974,1489.08997 973.047974,1489.08997) t=0 [207] (973.051086,1489.09277) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=104 from=(973.051086,1489.09277) to=(973.047974,1489.08997)
-path.cubicTo(973.049011,1489.09094, 973.047974,1489.08997, 973.047974,1489.08997);
-path.close();
-SkOpSegment::debugShowActiveSpans id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 (963.143005,1489.59802) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 (964.265015,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 (963.143005,1491.84399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 (962.02002,1490.72095) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
+<div id="fuzz763_1026368">
+  RunTestSet [fuzz763_1026368]
+
+{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}},
+{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}},
+{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}},
+{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}},
+{{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}},
+{{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}},
+{{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}},
+{{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}},
+{{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}},
+{{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}},
+{{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}},
+{{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}},
+{{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}},
+{{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}},
+{{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}},
+{{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}},
+{{27.3431988,27.3431988}, {27.3431454,27.3431454}},
+{{38.6398277,38.6738319}, {38.6447258,38.6689186}},
+{{38.6447258,38.6689186}, {38.6447449,38.6689377}},
+{{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}},
+op union
+{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}},
+{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}},
+{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}},
+{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}},
+{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}},
+{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}},
+{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}},
+{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}},
+debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
+debugShowQuadLineIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
+debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{41,33}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
+debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6341171,38.6795731}} wnTs[0]=0 {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}} {{38.6227531,38.6909218}} wnTs[0]=0 {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{32.9638329,41.0290833}} wnTs[0]=0 {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}}
+debugShowQuadIntersection wtTs[0]=1 {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}} {{27.3090477,38.6809464}} wnTs[0]=0 {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}} {{24.9708939,33.0220299}} wnTs[0]=0 {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}}
+debugShowQuadIntersection wtTs[0]=1 {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{27.319025,27.3672428}} wnTs[0]=0 {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}}
+debugShowQuadIntersection wtTs[0]=1 {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}} {{27.3229256,27.3633518}} wnTs[0]=0 {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}} {{27.3270645,27.3592148}} wnTs[0]=0 {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{27.3354416,27.3508568}} wnTs[0]=0 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}}
+debugShowQuadIntersection no intersect {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}} {{27.331131,27.3551788}} wnTs[0]=0 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}}
+debugShowQuadLineIntersection wtTs[0]=1 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}} {{27.3431988,27.3431988}} wnTs[0]=0 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
+debugShowQuadIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wtTs[1]=1 {{33,25}} wnTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} wnTs[1]=1
+debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
+debugShowQuadIntersection wtTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{33,25}} wnTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}
+debugShowQuadIntersection wtTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{33,25}} wtTs[1]=1 {{38.6568527,27.3431454}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} wnTs[1]=1
+debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{41,33}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
+debugShowQuadIntersection wtTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{38.6568527,27.3431454}} wnTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
+debugShowQuadIntersection wtTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{38.6568527,27.3431454}} wtTs[1]=1 {{41,33}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} wnTs[1]=1
+debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wtTs[1]=1 {{38.6568527,38.6568527}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} wnTs[1]=1
+debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wnTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
+debugShowQuadIntersection wtTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6568527,38.6568527}} wnTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
+debugShowQuadIntersection wtTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection wtTs[0]=0.0149880862 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6445847,38.6690979}} wnTs[0]=0.00261623 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection no intersect {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}} {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection no intersect {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection no intersect {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
+debugShowQuadIntersection no intersect {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}} {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
+debugShowQuadIntersection no intersect {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}} {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
+debugShowQuadIntersection no intersect {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
+debugShowQuadIntersection no intersect {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection no intersect {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection no intersect {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection no intersect {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection wtTs[0]=0 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}} {{27.3354416,27.3508568}} wtTs[1]=1 {{27.331131,27.3551788}} wnTs[0]=0.998355 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} wnTs[1]=0.99743327
+debugShowQuadIntersection wtTs[0]=0.0266527086 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}} {{27.3314419,27.3548698}} wnTs[0]=0.997499 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadLineIntersection wtTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
+debugShowQuadLineIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
+debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
+debugShowQuadLineIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}}
+debugShowQuadLineIntersection no intersect {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
+debugShowQuadLineIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
+debugShowQuadIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}}
+debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
+debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wnTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
+debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{33,41}} wnTs[0]=0 {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
+debugShowQuadIntersection wtTs[0]=1 {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}} {{27.3431454,38.6568527}} wnTs[0]=0 {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}} {{25,33}} wnTs[0]=0 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
+debugShowQuadIntersection wtTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} {{27.3431454,27.3431454}} wnTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}
+debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
+debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
+debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
+debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
+debugShowQuadIntersection wtTs[0]=0.00258220891 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6447449,38.6689377}} wtTs[1]=0.00362998223 {{38.6398277,38.6738319}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} wnTs[1]=1
+debugShowLineIntersection wtTs[0]=1 {{38.6398277,38.6738319}, {38.6447258,38.6689186}} {{38.6447258,38.6689186}} wnTs[0]=0 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
+debugShowQuadLineIntersection wtTs[0]=1 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} {{38.6398277,38.6738319}} wnTs[0]=0 {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
+debugShowQuadLineIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
+SkOpSegment::debugShowTs - id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0]
+SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand
+SkOpSegment::debugShowTs + id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0]
+SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand
+SkOpSegment::debugShowTs - id=1 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
+SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand
+SkOpSegment::debugShowTs + id=1 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
+SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand
+SkOpSegment::debugShowTs - id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0]
+SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand
+SkOpSegment::debugShowTs + id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0]
+SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand
+SkOpSegment::debugShowTs - id=3 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
+SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand
+SkOpSegment::debugShowTs + id=3 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
+SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand
+SkOpSegment::debugShowTs - id=14 [o=13 t=0 27.3354416,27.3508568 w=1 o=0] [o=15 t=1 27.331131,27.3551788 w=1 o=0]
+SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=1 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
+SkOpSegment::addTPair addTPair this=14 0 other=24 0.998354892
+SkOpSegment::addTPair id=14 lower=0 upper=1 other=24 oLower=2 oUpper=2
+SkOpSegment::addTPair addTPair this=24 0.99743327 other=14 1
+SkOpSegment::addTPair id=24 lower=1 upper=1 other=14 oLower=2 oUpper=3
+SkOpSegment::debugShowTs + id=14 [o=24,13 t=0 27.3354416,27.3508568 w=1 o=0] [o=24,15 t=1 27.331131,27.3551788 w=1 o=0]
+SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=1 o=0] [o=14 t=0.997 27.331131,27.3551788 w=1 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=1 o=0] [o=14 t=0.998 27.3354416,27.3508568 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
+SkOpSegment::debugShowTs - id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
+SkOpSegment::debugShowTs o id=19 [o=18,5,4 t=0 38.6447449,38.6689377 w=1 o=0] [o=17 t=1 38.6398277,38.6738319 w=1 o=0]
+SkOpSegment::addTPair addTPair this=21 0.00258220891 other=19 0
+SkOpSegment::addTPair id=21 lower=3 upper=3 other=19 oLower=0 oUpper=3
+SkOpSegment::addTPair addTPair this=19 1 other=21 0.00362998223
+SkOpSegment::addTPair id=19 lower=4 upper=5 other=21 oLower=5 oUpper=5
+SkOpSegment::debugShowTs + id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=19 t=0.00258 38.6447449,38.6689377 w=1 o=0] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=0] [o=19 t=0.00363 38.6398277,38.6738319 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
+SkOpSegment::debugShowTs o id=19 [o=21,18,5,4 t=0 38.6447449,38.6689377 w=1 o=0] [o=21,17 t=1 38.6398277,38.6738319 w=1 o=0]
+SkOpContour::calcCoincidentWinding count=5
+SkOpSegment::debugShowTs p id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=1] [o=26,1 t=1 33,25 w=1 o=0]
+SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=0 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand done
+SkOpSegment::debugShowTs p id=1 [o=25,0 t=0 33,25 w=1 o=1] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
+SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=0 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand done
+SkOpSegment::debugShowTs p id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=1] [o=20,3 t=1 41,33 w=1 o=0]
+SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=0 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand done
+SkOpSegment::debugShowTs p id=3 [o=27,2 t=0 41,33 w=1 o=1] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
+SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=0 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand done
+SkOpSegment::debugShowTs p id=14 [o=24,13 t=0 27.3354416,27.3508568 w=1 o=-1] [o=24,15 t=1 27.331131,27.3551788 w=1 o=0]
+SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=0 o=0] [o=14 t=0.997 27.331131,27.3551788 w=0 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=0 o=0] [o=14 t=0.998 27.3354416,27.3508568 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
+SkOpContour::calcCoincidentWinding count=1
+SkOpSegment::debugShowTs p id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=19 t=0.00258 38.6447449,38.6689377 w=1 o=1] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=1] [o=19 t=0.00363 38.6398277,38.6738319 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
+SkOpSegment::debugShowTs o id=19 [o=21,18,5,4 t=0 38.6447449,38.6689377 w=0 o=0] [o=21,17 t=1 38.6398277,38.6738319 w=1 o=0] done
+SkOpSegment::checkEnds id=4 missing t=1 other=21 otherT=0.00258220891 pt=(38.6447449,38.6689377)
+SkOpSegment::addTPair addTPair this=4 1 other=21 0.00258220891
+SkOpSegment::addTPair id=4 lower=3 upper=6 other=21 oLower=3 oUpper=4
+SkOpSegment::checkEnds id=5 missing t=0 other=21 otherT=0.00258220891 pt=(38.6447449,38.6689377)
+SkOpSegment::addTPair addTPair this=5 0 other=21 0.00258220891
+SkOpSegment::addTPair id=5 lower=0 upper=3 other=21 oLower=3 oUpper=5
+SkOpSegment::checkEnds id=13 missing t=1 other=24 otherT=0.998354892 pt=(27.3354416,27.3508568)
+SkOpSegment::addTPair addTPair this=13 1 other=24 0.998354892
+SkOpSegment::addTPair id=13 lower=1 upper=2 other=24 oLower=3 oUpper=4
+SkOpSegment::checkEnds id=15 missing t=0 other=24 otherT=0.99743327 pt=(27.331131,27.3551788)
+SkOpSegment::addTPair addTPair this=15 0 other=24 0.99743327
+SkOpSegment::addTPair id=15 lower=0 upper=1 other=24 oLower=1 oUpper=2
+SkOpSegment::checkEnds id=21 missing t=0.00362998223 other=17 otherT=0 pt=(38.6398277,38.6738319)
+SkOpSegment::addTPair addTPair this=21 0.00362998223 other=17 0
+SkOpSegment::addTPair id=21 lower=7 upper=8 other=17 oLower=0 oUpper=1
+SkOpSegment::addTPair addTPair this=0 0 other=25 0
+SkOpSegment::addTPair id=0 lower=0 upper=2 other=25 oLower=0 oUpper=2
+SkOpSegment::addTPair addTPair duplicate this=0 0 other=25 0
+SkOpSegment::addTPair addTPair this=3 1 other=20 1
+SkOpSegment::addTPair id=3 lower=2 upper=4 other=20 oLower=2 oUpper=4
+SkOpSegment::addTPair addTPair duplicate this=3 1 other=20 1
+SkOpSegment::addTPair addTPair this=21 0.00258220891 other=18 1
+SkOpSegment::addTPair id=21 lower=3 upper=6 other=18 oLower=1 oUpper=4
+SkOpSegment::addTPair addTPair duplicate this=18 1 other=21 0.00258220891
+SkOpContour::joinCoincidence count=5
+SkOpContour::joinCoincidence count=1
+SkOpSegment::sortAngles [0] tStart=0 [1]
+SkOpAngle::after [0/1] 2/1 tStart=0 tEnd=1 < [16/1] 27/27 tStart=1 tEnd=0 < [24/2] 17/21 tStart=1 tEnd=0.998354892  F 4
+SkOpSegment::sortAngles [0] tStart=1 [4]
+SkOpSegment::sortAngles [1] tStart=1 [3]
+SkOpSegment::sortAngles [2] tStart=1 [3]
+SkOpSegment::sortAngles [3] tStart=1 [3]
+SkOpAngle::after [3/2] 1/5 tStart=1 tEnd=0 < [4/1] 18/17 tStart=0 tEnd=1 < [21/1] 21/17 tStart=0 tEnd=0.00258220891  T 11
+SkOpSegment::sortAngles [4] tStart=1 [3]
+SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254 < [21/2] 1/1 tStart=0.00258220891 tEnd=0  T 5
+SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [18/1] 11/11 tStart=1 tEnd=0 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254  T 4
+SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [5/1] 21/21 tStart=0 tEnd=0.0149880862 < [18/1] 11/11 tStart=1 tEnd=0  F 4
+SkOpAngle::after [18/1] 11/11 tStart=1 tEnd=0 < [5/1] 21/21 tStart=0 tEnd=0.0149880862 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254  T 7
+SkOpSegment::sortAngles [5] tStart=0.0149880862 [4]
+SkOpAngle::after [5/2] 1/1 tStart=0.0149880862 tEnd=0 < [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891 < [5/3] 21/21 tStart=0.0149880862 tEnd=1  T 12
+SkOpAngle::after [5/2] 1/1 tStart=0.0149880862 tEnd=0 < [21/5] 17/17 tStart=0.0026162254 tEnd=0.00362998223 < [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891  F 5
+SkOpAngle::after [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891 < [21/5] 17/17 tStart=0.0026162254 tEnd=0.00362998223 < [5/3] 21/21 tStart=0.0149880862 tEnd=1  T 4
+SkOpSegment::sortAngles [13] tStart=1 [1]
+SkOpAngle::after [13/1] 17/17 tStart=1 tEnd=0 < [14/1] 21/21 tStart=0 tEnd=1 < [24/1] 5/5 tStart=0.998354892 tEnd=1  T 4
+SkOpSegment::sortAngles [14] tStart=1 [3]
+SkOpSegment::sortAngles [15] tStart=0.0266527086 [2]
+SkOpSegment::sortAngles [21] tStart=0.00362998223 [8]
+SkOpAngle::after [21/6] 1/1 tStart=0.00362998223 tEnd=0.0026162254 < [17/1] 5/5 tStart=0 tEnd=1 < [21/7] 17/17 tStart=0.00362998223 tEnd=1  T 4
+SkOpSegment::debugShowActiveSpans id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 (27.3431454,27.3431454) tEnd=1 other=25 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 (33,25) tEnd=1 other=25 otherT=1 otherIndex=4 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 (38.6568527,27.3431454) tEnd=1 other=26 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 (41,33) tEnd=1 other=27 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 (38.6568527,38.6568527) tEnd=1 other=21 otherT=0 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 (38.6447449,38.6689377) tEnd=0.0149880862 other=21 otherT=0.00258220891 otherIndex=4 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=? windValue=1 oppValue=-1
+SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 (27.331131,27.3551788) tEnd=0.0266527086 other=24 otherT=0.99743327 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 (27.3314419,27.3548698) tEnd=1 other=24 otherT=0.997499486 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 (27.3431988,27.3431988) tEnd=1 other=15 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 (38.6568527,38.6568527) tEnd=0.00258220891 other=20 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 (38.6447449,38.6689377) tEnd=0.0026162254 other=18 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 (27.3354416,27.3508568) tEnd=1 other=13 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
 SkOpSegment::findTop
-SkOpAngle::dumpOne [57/131] next=54/132 sect=16/17  s=1 [114] e=0 [113] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [54/132] next=57/131 sect=30/25  s=0 [107] e=1 [108] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=41 opp=0 tHit=0.172695599 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=57 index=113 endIndex=114 tHit=0.9 hitDx=-1.51202893 try=0 vert=0
-SkOpSegment::windingAtT id=41 opp=1 tHit=0.172695599 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=57 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 [113] (962.02002,1490.72095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 [107] (963.143005,1489.59802) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 [109] (964.265015,1490.72095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 [111] (963.143005,1491.84399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 [113] (962.02002,1490.72095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=57 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=57 (962.02002,1490.72095 962.02002,1490.09998 962.521973,1489.59802 963.143005,1489.59802) t=0 [113] (962.02002,1490.72095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=57 from=(962.02002,1490.72095) to=(963.143005,1489.59802)
-path.moveTo(962.02002,1490.72095);
-path.cubicTo(962.02002,1490.09998, 962.521973,1489.59802, 963.143005,1489.59802);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=54 (963.143005,1489.59802 963.763,1489.59802 964.265015,1490.09998 964.265015,1490.72095) t=0 [107] (963.143005,1489.59802) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=54 from=(963.143005,1489.59802) to=(964.265015,1490.72095)
-path.cubicTo(963.763,1489.59802, 964.265015,1490.09998, 964.265015,1490.72095);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=55 (964.265015,1490.72095 964.265015,1491.34204 963.763,1491.84399 963.143005,1491.84399) t=0 [109] (964.265015,1490.72095) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=55 from=(964.265015,1490.72095) to=(963.143005,1491.84399)
-path.cubicTo(964.265015,1491.34204, 963.763,1491.84399, 963.143005,1491.84399);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=56 (963.143005,1491.84399 962.521973,1491.84399 962.02002,1491.34204 962.02002,1490.72095) t=0 [111] (963.143005,1491.84399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=56 from=(963.143005,1491.84399) to=(962.02002,1490.72095)
-path.cubicTo(962.521973,1491.84399, 962.02002,1491.34204, 962.02002,1490.72095);
-path.close();
-SkOpSegment::debugShowActiveSpans id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 (957.127014,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 (951.445557,1491.22766) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 (951.414001,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 (957.119873,1490.39355) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [63/135] next=64/136 sect=13/17  s=1 [126] e=0 [125] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [64/136] next=63/135 sect=29/29  s=0 [127] e=1 [128] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=52 opp=0 tHit=0.599456643 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=63 index=125 endIndex=126 tHit=0.9 hitDx=-3.12248874 try=0 vert=0
-SkOpSegment::windingAtT id=52 opp=1 tHit=0.599456643 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=63 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 [125] (951.414001,1491.21399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 [127] (957.119873,1490.39355) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 [121] (957.127014,1490.40002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 [123] (951.445557,1491.22766) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 [125] (951.414001,1491.21399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=63 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=63 (951.414001,1491.21399 954.694214,1488.33154 956.976746,1490.26636 957.119873,1490.39355) t=0 [125] (951.414001,1491.21399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=63 from=(951.414001,1491.21399) to=(957.119873,1490.39355)
-path.moveTo(951.414001,1491.21399);
-path.cubicTo(954.694214,1488.33154, 956.976746,1490.26636, 957.119873,1490.39355);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=64 (957.119873,1490.39355 957.124634,1490.39783 957.127014,1490.40002 957.127014,1490.40002) t=0 [127] (957.119873,1490.39355) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=64 from=(957.119873,1490.39355) to=(957.127014,1490.40002)
-path.cubicTo(957.124634,1490.39783, 957.127014,1490.40002, 957.127014,1490.40002);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=61 (957.127014,1490.40002 955.541504,1492.89014 951.825745,1491.38965 951.445557,1491.22766) t=0 [121] (957.127014,1490.40002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=61 from=(957.127014,1490.40002) to=(951.445557,1491.22766)
-path.cubicTo(955.541504,1492.89014, 951.825745,1491.38965, 951.445557,1491.22766);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=62 (951.445557,1491.22766 951.424805,1491.21887 951.414001,1491.21399 951.414001,1491.21399) t=0 [123] (951.445557,1491.22766) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=62 from=(951.445557,1491.22766) to=(951.414001,1491.21399)
-path.cubicTo(951.424805,1491.21887, 951.414001,1491.21399, 951.414001,1491.21399);
-path.close();
-SkOpSegment::debugShowActiveSpans id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 (969.156982,1490.40002) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 (974.869995,1491.21399) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 (974.834473,1491.22937) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
+SkOpAngle::dumpOne [0/2] next=1/1 sect=16/17  s=1 [4] e=0 [2] sgn=1 windVal=1 windSum=? oppVal=1 oppSum=?
+SkOpAngle::dumpOne [1/1] next=0/2 sect=30/29  s=0 [0] e=1 [2] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? stop
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markWinding id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [0] (33,25) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [1] (33,25) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markWinding id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [0] (38.6568527,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [1] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markWinding id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [0] (41,33) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::activeOp id=0 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+bridgeOp current id=0 from=(27.3431454,27.3431454) to=(33,25)
+path.moveTo(27.3431454,27.3431454);
+path.quadTo(29.6862907,25, 33,25);
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [0] (33,25) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [1] (33,25) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+bridgeOp current id=1 from=(33,25) to=(38.6568527,27.3431454)
+path.quadTo(36.3137093,25, 38.6568527,27.3431454);
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [0] (38.6568527,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [1] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+bridgeOp current id=2 from=(38.6568527,27.3431454) to=(41,33)
+path.quadTo(41,29.6862907, 41,33);
+SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [2] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=4 windSum=? small=0
+SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [2] (38.6568527,38.6568527) tEnd=0.00258220891 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=21 windSum=? small=0
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [3/2] next=4/1 sect=1/5  s=1 [4] e=0 [1] sgn=1 windVal=1 windSum=-1 oppVal=1 oppSum=-1
+SkOpAngle::dumpOne [4/1] next=21/1 sect=18/17  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [21/1] next=3/2 sect=21/17  s=0 [0] e=0.00258220891 [3] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpSegment::activeOp id=4 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [2] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append id=4 windSum=-2147483647 small=0
+SkOpSegment::activeOp id=21 t=0 tEnd=0.00258220891 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::findNextOp chase.append id=21 windSum=-2147483647 small=0
+SkOpSegment::markDoneBinary id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [0] (41,33) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
+SkOpSegment::findNextOp from:[3] to:[21] start=0 end=3
+bridgeOp current id=3 from=(41,33) to=(38.6568527,38.6568527)
+path.quadTo(41,36.3137093, 38.6568527,38.6568527);
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [21/2] next=4/2 sect=1/1  s=0.00258220891 [3] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [4/2] next=18/1 sect=1/1  s=1 [6] e=0 [2] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done
+SkOpAngle::dumpOne [18/1] next=5/1 sect=11/11  s=1 [4] e=0 [0] sgn=1 windVal=1 windSum=? done
+SkOpAngle::dumpOne [5/1] next=21/3 sect=21/21  s=0 [0] e=0.0149880862 [4] sgn=-1 windVal=1 windSum=? unorderable
+SkOpAngle::dumpOne [21/3] next=21/2 sect=21/21  s=0.00258220891 [3] e=0.0026162254 [7] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? unorderable operand
+SkOpSegment::activeOp id=4 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::activeOp id=18 t=1 tEnd=0 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::activeOp id=5 t=0 tEnd=0.0149880862 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [0] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [1] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [2] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [3] (38.6447449,38.6689377) tEnd=0.0149880862 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::activeOp id=21 t=0.00258220891 tEnd=0.0026162254 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [2] (38.6568527,38.6568527) tEnd=0.00258220891 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[21] to:[21] start=3 end=7
+bridgeOp current id=21 from=(38.6568527,38.6568527) to=(38.6447449,38.6689377)
+path.quadTo(38.6510239,38.6626816, 38.6447449,38.6689377);
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [3] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [4] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [5] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [6] (38.6447449,38.6689377) tEnd=0.0026162254 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=? windValue=1 oppValue=-1
+SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 (27.331131,27.3551788) tEnd=0.0266527086 other=24 otherT=0.99743327 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 (27.3314419,27.3548698) tEnd=1 other=24 otherT=0.997499486 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 (27.3431988,27.3431988) tEnd=1 other=15 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 (27.3354416,27.3508568) tEnd=1 other=13 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
 SkOpSegment::findTop
-SkOpAngle::dumpOne [20/139] next=21/140 sect=13/13  s=1 [40] e=0 [39] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [21/140] next=20/139 sect=17/17  s=0 [41] e=1 [42] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=103 opp=0 tHit=0.641791069 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=20 index=39 endIndex=40 tHit=0.9 hitDx=-4.33002901 try=0 vert=0
-SkOpSegment::windingAtT id=103 opp=1 tHit=0.641791069 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=20 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 [39] (969.156982,1490.40002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 [41] (974.869995,1491.21399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 [43] (974.834473,1491.22937) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 [39] (969.156982,1490.40002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=20 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=20 (969.156982,1490.40002 969.156982,1490.40002 971.478027,1488.23596 974.869995,1491.21399) t=0 [39] (969.156982,1490.40002) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=20 from=(969.156982,1490.40002) to=(974.869995,1491.21399)
-path.moveTo(969.156982,1490.40002);
-path.cubicTo(969.156982,1490.40002, 971.478027,1488.23596, 974.869995,1491.21399);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=21 (974.869995,1491.21399 974.869995,1491.21399 974.857788,1491.21948 974.834473,1491.22937) t=0 [41] (974.869995,1491.21399) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=21 from=(974.869995,1491.21399) to=(974.834473,1491.22937)
-path.cubicTo(974.869995,1491.21399, 974.857788,1491.21948, 974.834473,1491.22937);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=22 (974.834473,1491.22937 974.433289,1491.40051 970.736267,1492.88184 969.156982,1490.40002) t=0 [43] (974.834473,1491.22937) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=22 from=(974.834473,1491.22937) to=(969.156982,1490.40002)
-path.cubicTo(974.433289,1491.40051, 970.736267,1492.88184, 969.156982,1490.40002);
-path.close();
-SkOpSegment::debugShowActiveSpans id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 (961.283997,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 (955.61499,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 (961.236389,1491.52283) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [59/143] next=60/144 sect=13/17  s=1 [118] e=0 [117] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [60/144] next=59/143 sect=29/29  s=0 [119] e=1 [120] sgn=-1 windVal=1 windSum=? operand stop
-SkOpSegment::windingAtT id=42 opp=0 tHit=0.474617252 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=59 index=117 endIndex=118 tHit=0.9 hitDx=-4.33552933 try=0 vert=0
-SkOpSegment::windingAtT id=42 opp=1 tHit=0.474617252 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=59 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 [117] (955.61499,1492.81604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 [119] (961.236389,1491.52283) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 [115] (961.283997,1491.56299) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 [117] (955.61499,1492.81604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=59 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=59 (955.61499,1492.81604 958.695923,1489.72131 960.89093,1491.24622 961.236389,1491.52283) t=0 [117] (955.61499,1492.81604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=59 from=(955.61499,1492.81604) to=(961.236389,1491.52283)
-path.moveTo(955.61499,1492.81604);
-path.cubicTo(958.695923,1489.72131, 960.89093,1491.24622, 961.236389,1491.52283);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=60 (961.236389,1491.52283 961.267883,1491.5481 961.283997,1491.56299 961.283997,1491.56299) t=0 [119] (961.236389,1491.52283) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=60 from=(961.236389,1491.52283) to=(961.283997,1491.56299)
-path.cubicTo(961.267883,1491.5481, 961.283997,1491.56299, 961.283997,1491.56299);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=58 (961.283997,1491.56299 958.953979,1494.49695 955.61499,1492.81604 955.61499,1492.81604) t=0 [115] (961.283997,1491.56299) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=58 from=(961.283997,1491.56299) to=(955.61499,1492.81604)
-path.cubicTo(958.953979,1494.49695, 955.61499,1492.81604, 955.61499,1492.81604);
-path.close();
-SkOpSegment::debugShowActiveSpans id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 (970.666992,1492.81604) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 (964.999023,1491.56299) tEnd=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [29/147] next=28/148 sect=9/13  s=1 [58] e=0 [57] sgn=1 windVal=1 windSum=? operand
-SkOpAngle::dumpOne [28/148] next=29/147 sect=17/13  s=0 [55] e=1 [56] sgn=-1 windVal=1 windSum=? operand
-SkOpSegment::windingAtT id=22 opp=0 tHit=0.899621278 t=0 oldWinding=-1 windValue=1 dx=- winding=0
-FindSortableTop current=29 index=57 endIndex=58 tHit=0.9 hitDx=-5.8496685 try=0 vert=0
-SkOpSegment::windingAtT id=22 opp=1 tHit=0.899621278 t=0 oldWinding=-1 windValue=0 dx=- winding=-1
-SkOpSegment::initWinding id=29 oldWinding=0 hitDx=- dx=+ windVal=1 winding=-1 oppWind=-1
-SkOpSegment::markWinding id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 [57] (964.999023,1491.56299) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 [55] (970.666992,1492.81604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 [57] (964.999023,1491.56299) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::activeOp id=29 t=0 tEnd=1 op=sect miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=29 (964.999023,1491.56299 964.999023,1491.56299 967.304016,1489.43896 970.666992,1492.81604) t=0 [57] (964.999023,1491.56299) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=29 from=(964.999023,1491.56299) to=(970.666992,1492.81604)
-path.moveTo(964.999023,1491.56299);
-path.cubicTo(964.999023,1491.56299, 967.304016,1489.43896, 970.666992,1492.81604);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=28 (970.666992,1492.81604 970.666992,1492.81604 967.327026,1494.49695 964.999023,1491.56299) t=0 [55] (970.666992,1492.81604) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=28 from=(970.666992,1492.81604) to=(964.999023,1491.56299)
-path.cubicTo(970.666992,1492.81604, 967.327026,1494.49695, 964.999023,1491.56299);
-path.close();
+SkOpAngle::dumpOne [0/1] next=24/2 sect=2/1  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1 done
+SkOpAngle::dumpOne [24/2] next=16/1 sect=17/21  s=1 [8] e=0.998354892 [5] sgn=1 windVal=1 windSum=? operand stop
+SkOpAngle::dumpOne [16/1] next=0/1 sect=27/27  s=1 [3] e=0 [0] sgn=1 windVal=1 windSum=? stop
+SkOpSegment::markWinding id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 [0] (27.3431988,27.3431988) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 [2] (27.3314419,27.3548698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [0] (27.331131,27.3551788) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [1] (27.331131,27.3551788) tEnd=0.0266527086 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markAngle last id=15 windSum=-1 small=0
+SkOpSegment::markWinding id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [4] (27.3354416,27.3508568) tEnd=0.998354892 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [5] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=24 windSum=-1 small=0
+SkOpSegment::activeOp id=24 t=0.998354892 tEnd=1 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [24/2] next=16/1 sect=17/21  s=1 [8] e=0.998354892 [5] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand stop
+SkOpAngle::dumpOne [16/1] next=0/1 sect=27/27  s=1 [3] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 stop
+SkOpAngle::dumpOne [0/1] next=24/2 sect=2/1  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1 done
+SkOpSegment::activeOp id=16 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDoneBinary id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 [0] (27.3431988,27.3431988) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 [2] (27.3314419,27.3548698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [0] (27.331131,27.3551788) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [1] (27.331131,27.3551788) tEnd=0.0266527086 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::findNextOp chase.append id=15 windSum=-1 small=0
+SkOpSegment::activeOp id=0 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::markDoneBinary id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [4] (27.3354416,27.3508568) tEnd=0.998354892 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [5] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[24] to:[0] start=0 end=3
+bridgeOp current id=24 from=(27.3354416,27.3508568) to=(27.3431454,27.3431454)
+path.moveTo(27.3354416,27.3508568);
+path.quadTo(27.3392906,27.3470001, 27.3431454,27.3431454);
+SkOpSegment::markWinding id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [0] (27.3354416,27.3508568) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=-1
+SkOpSegment::markWinding id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [1] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=-1
+SkOpSegment::markAngle last id=14 windSum=-1 small=0
+SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=-1 windValue=1 oppValue=-1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
+SkOpSegment::activeOp id=14 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=0 result=0
+SkOpSegment::markDoneBinary id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [0] (27.3354416,27.3508568) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=-1
+SkOpSegment::markDoneBinary id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [1] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=-1
+bridgeOp chase.append id=14 windSum=-1 small=0
+SkOpSegment::markWinding id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 [0] (27.3270645,27.3592148) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 [0] (27.3229256,27.3633518) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 [0] (27.319025,27.3672428) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 [0] (24.9708939,33.0220299) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 [0] (27.3090477,38.6809464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 [0] (32.9638329,41.0290833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 [0] (38.6227531,38.6909218) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 [0] (38.6341171,38.6795731) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 [4] (38.6445847,38.6690979) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=5 windSum=-1 small=0
+SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
+SkOpSegment::activeOp id=13 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 [0] (27.3270645,27.3592148) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=13 from=(27.3354416,27.3508568) to=(27.3270645,27.3592148)
+path.moveTo(27.3354416,27.3508568);
+path.quadTo(27.3312511,27.355032, 27.3270645,27.3592148);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 [0] (27.3229256,27.3633518) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=12 from=(27.3270645,27.3592148) to=(27.3229256,27.3633518)
+path.quadTo(27.324995,27.3612823, 27.3229256,27.3633518);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 [0] (27.319025,27.3672428) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=11 from=(27.3229256,27.3633518) to=(27.319025,27.3672428)
+path.quadTo(27.3209743,27.3652973, 27.319025,27.3672428);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 [0] (24.9708939,33.0220299) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=10 from=(27.319025,27.3672428) to=(24.9708939,33.0220299)
+path.quadTo(24.973814,29.7083225, 24.9708939,33.0220299);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 [0] (27.3090477,38.6809464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=9 from=(24.9708939,33.0220299) to=(27.3090477,38.6809464)
+path.quadTo(24.9679718,36.3357391, 27.3090477,38.6809464);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 [0] (32.9638329,41.0290833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=8 from=(27.3090477,38.6809464) to=(32.9638329,41.0290833)
+path.quadTo(29.6501274,41.0261612, 32.9638329,41.0290833);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 [0] (38.6227531,38.6909218) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=7 from=(32.9638329,41.0290833) to=(38.6227531,38.6909218)
+path.quadTo(36.2775421,41.0320053, 38.6227531,38.6909218);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 [0] (38.6341171,38.6795731) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=6 from=(38.6227531,38.6909218) to=(38.6341171,38.6795731)
+path.quadTo(38.6284409,38.6852493, 38.6341171,38.6795731);
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [5/3] next=5/2 sect=21/21  s=0.0149880862 [4] e=1 [5] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [5/2] next=21/4 sect=1/1  s=0.0149880862 [4] e=0 [0] sgn=1 windVal=1 windSum=? done unorderable
+SkOpAngle::dumpOne [21/4] next=21/5 sect=1/1  s=0.0026162254 [7] e=0.00258220891 [3] sgn=1 windVal=1 windSum=? oppVal=1 oppSum=? done unorderable operand
+SkOpAngle::dumpOne [21/5] next=5/3 sect=17/17  s=0.0026162254 [7] e=0.00362998223 [8] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? operand
+SkOpSegment::activeOp id=5 t=0.0149880862 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
+SkOpSegment::activeOp id=21 t=0.0026162254 tEnd=0.00258220891 op=union miFrom=1 miTo=0 suFrom=0 suTo=1 result=0
+SkOpSegment::activeOp id=21 t=0.0026162254 tEnd=0.00362998223 op=union miFrom=0 miTo=1 suFrom=1 suTo=0 result=0
+SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 [7] (38.6445847,38.6690979) tEnd=0.00362998223 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
+SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 [4] (38.6445847,38.6690979) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[5] to:[5] start=4 end=0
+bridgeOp current id=5 from=(38.6341171,38.6795731) to=(38.6445847,38.6690979)
+path.quadTo(38.6393547,38.6743355, 38.6445847,38.6690979);
+SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
+d:\cygwin\puregit\src\pathops\skopsegment.cpp:472: failed assertion "span.fT > 0"
 </div>
-
-
 </div>
 
 <script type="text/javascript">
 
 var testDivs = [
-    skpwww_educationalcraft_com_4,
+    fuzz763_1026368,
 ];
 
 var decimal_places = 3; // make this 3 to show more precision
@@ -3730,6 +498,7 @@ var draw_add = false;
 var draw_angle = 0;
 var draw_deriviatives = 0;
 var draw_hints = false;
+var draw_hodo = 0;
 var draw_id = false;
 var draw_intersection = 0;
 var draw_intersectT = false;
@@ -3763,8 +532,6 @@ var stepMax = 0;
 var lastIndex = 0;
 var hasPath = false;
 var hasComputedPath = false;
-var angleBetween = false;
-var afterIndex = 0;
 
 var firstActiveSpan = -1;
 var logStart = -1;
@@ -3886,27 +653,25 @@ var COMPUTED_SET_1 =          MARK_ANGLE_LAST + 1;
 var COMPUTED_SET_2 =          COMPUTED_SET_1 + 1;
 
 var ANGLE_AFTER =             COMPUTED_SET_2;
-var ANGLE_AFTERPART =         ANGLE_AFTER + 1;
+var ANGLE_AFTER2 =            ANGLE_AFTER + 1;
 
-var ACTIVE_OP =               ANGLE_AFTERPART + 1;
+var ACTIVE_OP =               ANGLE_AFTER2 + 1;
 
 var FRAG_TYPE_LAST =          ACTIVE_OP;
 
 var REC_TYPE_UNKNOWN = -1;
 var REC_TYPE_PATH = 0;
-var REC_TYPE_PATH2 = 1;
-var REC_TYPE_SECT = 2;
-var REC_TYPE_ACTIVE = 3;
-var REC_TYPE_ADD = 4;
-var REC_TYPE_SORT = 5;
-var REC_TYPE_OP = 6;
-var REC_TYPE_MARK = 7;
-var REC_TYPE_COMPUTED = 8;
-var REC_TYPE_COIN = 9;
-var REC_TYPE_ANGLE = 10;
-var REC_TYPE_ACTIVE_OP = 11;
-var REC_TYPE_AFTERPART = 12;
-var REC_TYPE_LAST = REC_TYPE_AFTERPART;
+var REC_TYPE_SECT = 1;
+var REC_TYPE_ACTIVE = 2;
+var REC_TYPE_ADD = 3;
+var REC_TYPE_SORT = 4;
+var REC_TYPE_OP = 5;
+var REC_TYPE_MARK = 6;
+var REC_TYPE_COMPUTED = 7;
+var REC_TYPE_COIN = 8;
+var REC_TYPE_ANGLE = 9;
+var REC_TYPE_ACTIVE_OP = 10;
+var REC_TYPE_LAST = REC_TYPE_ACTIVE_OP;
 
 function strs_to_nums(strs) {
     var result = [];
@@ -3940,7 +705,6 @@ function construct_regexp2(pattern) {
     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(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
-    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
     escape = escape.replace(/PT_VAL/g, "\\(P_VAL\\)");
     escape = escape.replace(/P_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, ?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
@@ -3954,13 +718,12 @@ function construct_regexp2(pattern) {
 function construct_regexp2c(pattern) {
     var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
     escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
-    escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
-    escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
-    escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
+    escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
+    escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
+    escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}\\}");
     escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
-    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
     escape = escape.replace(/PT_VAL/g, "\\{\\{P_VAL\\}\\}");
-    escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, *(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
+    escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?,(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
     escape = escape.replace(/OPER/g, "[a-z]+");
     escape = escape.replace(/PATH/g, "pathB?");
@@ -4004,10 +767,8 @@ function parse_all(test) {
         if (line.lastIndexOf(angleStart, 0) === 0) {
             line = line.substr(angleStart.length);
         }
-        var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE
-                : line.lastIndexOf("((SkOpSegment*)", 0) === 0 ? REC_TYPE_PATH2
+        var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE 
                 : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN 
-                : line.lastIndexOf("afterPart", 0) === 0 ? REC_TYPE_AFTERPART
                 : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
                 : line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
                 : line.lastIndexOf("computed", 0) === 0 ? REC_TYPE_COMPUTED
@@ -4018,7 +779,7 @@ function parse_all(test) {
                 : line.lastIndexOf("after", 0) === 0 ? REC_TYPE_ANGLE
                 : line.lastIndexOf("mark", 0) === 0 ? REC_TYPE_MARK
                 : line.lastIndexOf("  {{", 0) === 0 ? REC_TYPE_COMPUTED
-                : line.lastIndexOf("seg=", 0) === 0 ? REC_TYPE_PATH
+                : line.lastIndexOf("{{", 0) === 0 ? REC_TYPE_PATH
                 : line.lastIndexOf("op", 0) === 0 ? REC_TYPE_OP
                 : line.lastIndexOf("$", 0) === 0 ? REC_TYPE_PATH
                 : REC_TYPE_UNKNOWN;
@@ -4037,11 +798,11 @@ function parse_all(test) {
         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 windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
                 ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
-" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
                 ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
                 );
                 break;
             case REC_TYPE_ACTIVE_OP:
@@ -4078,13 +839,13 @@ function parse_all(test) {
                     found = match_regexp(line, lineNo, record, ADD_CLOSE, "PATH.close();");
                 }
                 break;
-            case REC_TYPE_AFTERPART:
-                found = match_regexp(line, lineNo, record, PATH_LINE, "afterPart LINE_VAL")
-                    || match_regexp(line, lineNo, record, PATH_QUAD, "afterPart QUAD_VAL")
-                    || match_regexp(line, lineNo, record, PATH_CUBIC, "afterPart CUBIC_VAL")
-                break;
             case REC_TYPE_ANGLE:
                 found = match_regexp(line, lineNo, record, ANGLE_AFTER, "after " +
+"id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL  T_F IDX");
+                if (found) {
+                    break;
+                }
+                found = match_regexp(line, lineNo, record, ANGLE_AFTER2, "after " +
 "[IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL  T_F IDX");
                 break;
             case REC_TYPE_COIN:
@@ -4099,15 +860,9 @@ function parse_all(test) {
                 );
                 break;
             case REC_TYPE_PATH:
-                found = match_regexp(line, lineNo, record, PATH_LINE, "seg=IDX LINE_VAL"
-                ) || match_regexp(line, lineNo, record, PATH_QUAD, "seg=IDX QUAD_VAL"
-                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "seg=IDX CUBIC_VAL"
-                );
-                break;
-            case REC_TYPE_PATH2:
-                found = match_regexp(line, lineNo, record, PATH_LINE, "((SkOpSegment*) PTR_VAL) [IDX] {LINE_VAL}"
-                ) || match_regexp(line, lineNo, record, PATH_QUAD, "((SkOpSegment*) PTR_VAL) [IDX] {QUAD_VAL}"
-                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "((SkOpSegment*) PTR_VAL) [IDX] {CUBIC_VAL}"
+                found = match_regexp(line, lineNo, record, PATH_LINE, "LINE_VAL"
+                ) || match_regexp(line, lineNo, record, PATH_QUAD, "QUAD_VAL"
+                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "CUBIC_VAL"
                 );
                 break;
             case REC_TYPE_SECT:
@@ -4191,27 +946,43 @@ function parse_all(test) {
                 break;
             case REC_TYPE_MARK:
                 found = match_regexp(line, lineNo, record, MARK_LINE, "markWinding" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_QUAD, "markWinding" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_CUBIC, "markWinding" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDone" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDone" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDone" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDoneBinary" +
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDoneBinary" +
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDoneBinary" +
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_LINE, "markUnsortable" +
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_QUAD, "markUnsortable" +
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_CUBIC, "markUnsortable" +
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_LINE, "markWinding" +
 " id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_QUAD, "markWinding" +
 " id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CUBIC, "markWinding" +
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_LINE, "markDone" +
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_QUAD, "markDone" +
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_CUBIC, "markDone" +
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_LINE, "markDoneUnary" +
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_QUAD, "markDoneUnary" +
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_CUBIC, "markDoneUnary" +
 " id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
-" last seg=IDX span=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
-" last segment=IDX span=IDX windSum=OPT");
+" last id=IDX windSum=OPT small=IDX");
                 break;
             case REC_TYPE_OP:
                 if (line.lastIndexOf("oppSign oppSign=", 0) === 0
@@ -4219,9 +990,8 @@ function parse_all(test) {
                     found = true;
                     break;
                 }
-                found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op diff"
+                found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op difference"
                 ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op intersect"
-                ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op sect"
                 ) || match_regexp(line, lineNo, record, OP_UNION, "op union"
                 ) || match_regexp(line, lineNo, record, OP_XOR, "op xor"
                 );
@@ -4292,16 +1062,15 @@ function init(test) {
                     }
                     hasComputedPath = true;
                 case REC_TYPE_PATH:
-                    first = 1;
                     switch (fragType) {
                         case PATH_LINE:
-                            last = 5;
+                            last = 4;
                             break;
                         case PATH_QUAD:
-                            last = 7;
+                            last = 6;
                             break;
                         case PATH_CUBIC:
-                            last = 9;
+                            last = 8;
                             break;
                         default:
                             console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH" 
@@ -4312,27 +1081,6 @@ function init(test) {
                         hasPath = true;
                     }
                     break;
-                case REC_TYPE_PATH2:
-                    first = 1;
-                    switch (fragType) {
-                        case PATH_LINE:
-                            last = 5;
-                            break;
-                        case PATH_QUAD:
-                            last = 7;
-                            break;
-                        case PATH_CUBIC:
-                            last = 9;
-                            break;
-                        default:
-                            console.log("unknown " + (recType == REC_TYPE_PATH2 ? "REC_TYPE_PATH2" 
-                                    : "REC_TYPE_COMPUTED") + " frag type:" + fragType);
-                            throw "stop execution";
-                    }
-                    if (recType == REC_TYPE_PATH2) {
-                        hasPath = true;
-                    }
-                    break;
                 case REC_TYPE_ACTIVE:
                     if (firstActiveSpan < 0) {
                         firstActiveSpan = tIndex;
@@ -4374,22 +1122,6 @@ function init(test) {
                             throw "stop execution";
                     }
                     break;
-                case REC_TYPE_AFTERPART:
-                    switch (fragType) {
-                        case PATH_LINE:
-                            last = 4;
-                            break;
-                        case PATH_QUAD:
-                            last = 6;
-                            break;
-                        case PATH_CUBIC:
-                            last = 8;
-                            break;
-                        default:
-                            console.log("unknown REC_TYPE_ACTIVEPART frag type: " + fragType);
-                            throw "stop execution";
-                    }
-                    break;
                 case REC_TYPE_SECT:
                     switch (fragType) {
                         case INTERSECT_LINE:
@@ -4510,7 +1242,13 @@ function init(test) {
                     if (!draw_angle) {
                         break;
                     }
-                    {
+                    if (fragType == ANGLE_AFTER) {
+                        var curve = curvePartialByID(test, frags[0], frags[3], frags[4]);
+                        curve_extremes(curve, angleBounds);
+                        curve = curvePartialByID(test, frags[5], frags[8], frags[9]);
+                        curve_extremes(curve, angleBounds);
+                        curve = curvePartialByID(test, frags[10], frags[13], frags[14]);
+                    } else if (fragType == ANGLE_AFTER2) {
                         var curve = curvePartialByID(test, frags[0], frags[4], frags[5]);
                         curve_extremes(curve, angleBounds);
                         curve = curvePartialByID(test, frags[6], frags[10], frags[11]);
@@ -4544,13 +1282,13 @@ function init(test) {
 }
 
 function curveByID(test, id) {
-    var tIndex = -3;
-    while ((tIndex += 3) < test.length) {
+    var tIndex = firstActiveSpan;
+    if (tIndex < 0) {
+        return [];
+    }
+    while (tIndex < test.length) {
         var recType = test[tIndex];
-        if (recType == REC_TYPE_OP) {
-            continue;
-        }
-        if (recType != REC_TYPE_PATH) {
+        if (recType != REC_TYPE_ACTIVE) {
             return [];
         }
         var records = test[tIndex + 2];
@@ -4559,29 +1297,30 @@ function curveByID(test, id) {
             var frags = records[recordIndex + 1];
             if (frags[0] == id) {
                 switch (fragType) {
-                    case PATH_LINE:
+                    case ACTIVE_LINE_SPAN:
                         return [frags[1], frags[2], frags[3], frags[4]];
-                    case PATH_QUAD:
+                    case ACTIVE_QUAD_SPAN:
                         return [frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6]];
-                    case PATH_CUBIC:
+                    case ACTIVE_CUBIC_SPAN:
                         return [frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], frags[7], frags[8]];
                 }
             }
         }
+        tIndex += 3;
     }
     return [];
 }
 
 function curvePartialByID(test, id, t0, t1) {
-    var tIndex = -3;
-    while ((tIndex += 3) < test.length) {
+    var tIndex = firstActiveSpan;
+    if (tIndex < 0) {
+        return [];
+    }
+    while (tIndex < test.length) {
         var recType = test[tIndex];
-        if (recType == REC_TYPE_OP) {
-            continue;
-        }
-        if (recType != REC_TYPE_PATH) {
+        if (recType != REC_TYPE_ACTIVE) {
             return [];
         }
         var records = test[tIndex + 2];
@@ -4590,51 +1329,60 @@ function curvePartialByID(test, id, t0, t1) {
             var frags = records[recordIndex + 1];
             if (frags[0] == id) {
                 switch (fragType) {
-                    case PATH_LINE:
+                    case ACTIVE_LINE_SPAN:
                         return linePartial(frags[1], frags[2], frags[3], frags[4], t0, t1);
-                    case PATH_QUAD:
+                    case ACTIVE_QUAD_SPAN:
                         return quadPartial(frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], t0, t1);
-                    case PATH_CUBIC:
+                    case ACTIVE_CUBIC_SPAN:
                         return cubicPartial(frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], frags[7], frags[8], t0, t1);
                 }
             }
         }
+        tIndex += 3;
     }
     return [];
 }
 
 function idByCurve(test, frag, type) {
-    var tIndex = 0;
+    var tIndex = firstActiveSpan;
+    if (tIndex < 0) {
+        return -1;
+    }
     while (tIndex < test.length) {
         var recType = test[tIndex];
-        if (recType != REC_TYPE_PATH) {
-            ++tIndex;
-            continue;
+        if (recType != REC_TYPE_ACTIVE) {
+            return -1;
         }
         var records = test[tIndex + 2];
         for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
             var fragType = records[recordIndex];
             var frags = records[recordIndex + 1];
-            if (frag.length != frags.length - 1) {
-                continue;
-            }
             switch (fragType) {
-                case PATH_LINE:
+                case ACTIVE_LINE_SPAN:
+                    if (type != PATH_LINE) {
+                        continue;
+                    }
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]) {
                         continue;
                     }
                     return frags[0];
-                case PATH_QUAD:
+                case ACTIVE_QUAD_SPAN:
+                    if (type != PATH_QUAD) {
+                        continue;
+                    }
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]
                             || frag[4] != frags[5] || frag[5] != frags[6]) {
                         continue;
                     }
                     return frags[0];
-                case PATH_CUBIC:
+                case ACTIVE_CUBIC_SPAN:
+                    if (type != PATH_CUBIC) {
+                        continue;
+                    }
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]
                             || frag[4] != frags[5] || frag[5] != frags[6]
@@ -5487,6 +2235,46 @@ function drawCurveSpecials(test, curve, type) {
     if (type != PATH_CUBIC) {
         return;
     }
+    if (draw_hodo == 1 || draw_hodo == 2) {
+        var hodo = hodograph(curve);
+        var hMinX = Math.min(0, hodo[0], hodo[2], hodo[4]);
+        var hMinY = Math.min(0, hodo[1], hodo[3], hodo[5]);
+        var hMaxX = Math.max(0, hodo[0], hodo[2], hodo[4]);
+        var hMaxY = Math.max(0, hodo[1], hodo[3], hodo[5]);
+        var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
+        var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
+        var hUnit = Math.min(hScaleX, hScaleY);
+        hUnit /= 2;
+        var hx = xoffset - hMinX * hUnit;
+        var hy = yoffset - hMinY * hUnit;
+        ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
+        ctx.quadraticCurveTo(
+            hx + hodo[2] * hUnit, hy + hodo[3] * hUnit,
+            hx + hodo[4] * hUnit, hy + hodo[5] * hUnit);
+        ctx.strokeStyle = "red";
+        ctx.stroke();
+        if (draw_hodo == 1) {
+            drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
+        }
+    }
+    if (draw_hodo == 3) {
+        var hodo = hodograph2(curve);
+        var hMinX = Math.min(0, hodo[0], hodo[2]);
+        var hMinY = Math.min(0, hodo[1], hodo[3]);
+        var hMaxX = Math.max(0, hodo[0], hodo[2]);
+        var hMaxY = Math.max(0, hodo[1], hodo[3]);
+        var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
+        var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
+        var hUnit = Math.min(hScaleX, hScaleY);
+        hUnit /= 2;
+        var hx = xoffset - hMinX * hUnit;
+        var hy = yoffset - hMinY * hUnit;
+        ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
+        ctx.lineTo(hx + hodo[2] * hUnit, hy + hodo[3] * hUnit);
+        ctx.strokeStyle = "red";
+        ctx.stroke();
+        drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
+    }
     if (draw_sequence) {
         var ymin = Math.min(curve[1], curve[3], curve[5], curve[7]);
         for (var i = 0; i < 8; i+= 2) {
@@ -5599,9 +2387,6 @@ function draw(test, lines, title) {
         if (recType == REC_TYPE_PATH && hasOp) {
             secondPath = tIndex;
         }
-        if (recType == REC_TYPE_PATH2 && hasOp) {
-            secondPath = tIndex;
-        }
         if (recType == REC_TYPE_ACTIVE) {
             ++activeMax;
             if (!draw_active || !inStepRange) {
@@ -5620,17 +2405,9 @@ function draw(test, lines, title) {
             ++opCount;
             bumpStep = true;
         }
-        if (recType == REC_TYPE_AFTERPART) {
-            if (draw_angle != 3 || !inStepRange) {
-                continue;
-            }
-            lastAngle = tIndex;
-            ++angleCount;
-            bumpStep = true;
-        }
         if (recType == REC_TYPE_ANGLE) {
             ++angleMax;
-            if (draw_angle == 0 || draw_angle == 3 || !inStepRange) {
+            if (!draw_angle || !inStepRange) {
                 continue;
             }
             lastAngle = tIndex;
@@ -5719,8 +2496,8 @@ function draw(test, lines, title) {
     }
     stepMax = (draw_add ? addMax : 0)
             + (draw_active ? activeMax : 0)
-            + (draw_angle ? angleMax : 0)
             + (draw_op ? opMax : 0)
+            + (draw_angle ? angleMax : 0)
             + (draw_sort ? sortMax : 0)
             + (draw_mark ? markMax : 0)
             + (draw_intersection == 2 ? sectMax : draw_intersection == 3 ? sectMax2 : 0);
@@ -5789,7 +2566,6 @@ function draw(test, lines, title) {
                     drawCurveSpecials(test, frags, fragType);
                     break;
                 case REC_TYPE_PATH:
-                case REC_TYPE_PATH2:
                     if (!draw_path) {
                         continue;
                     }
@@ -5800,30 +2576,26 @@ function draw(test, lines, title) {
                     ctx.lineWidth = 1;
                     ctx.strokeStyle = firstPath ? "black" : "red";
                     ctx.fillStyle = "blue";
-                    var frags2 = []; 
                     switch (fragType) {
                         case PATH_LINE:
-                            for (var i = 0; i < 4; ++ i) { frags2[i] = frags[i + 1]; }
-                            drawLine(frags2[0], frags2[1], frags2[2], frags2[3]);
+                            drawLine(frags[0], frags[1], frags[2], frags[3]);
                             break;
                         case PATH_QUAD:
-                            for (var i = 0; i < 6; ++ i) { frags2[i] = frags[i + 1]; }
-                            drawQuad(frags2[0], frags2[1], frags2[2], frags2[3],
-                                    frags2[4], frags2[5]);
+                            drawQuad(frags[0], frags[1], frags[2], frags[3],
+                                    frags[4], frags[5]);
                             break;
                         case PATH_CUBIC:
-                            for (var i = 0; i < 8; ++ i) { frags2[i] = frags[i + 1]; }
-                            drawCubic(frags2[0], frags2[1], frags2[2], frags2[3],
-                                    frags2[4], frags2[5], frags2[6], frags2[7]);
+                            drawCubic(frags[0], frags[1], frags[2], frags[3],
+                                    frags[4], frags[5], frags[6], frags[7]);
                             break;
                         default:
-                            console.log("unknown REC_TYPE_PATH2 frag type: " + fragType);
+                            console.log("unknown REC_TYPE_PATH frag type: " + fragType);
                             throw "stop execution";
                     }
                     if (collect_bounds) {
                         break;
                     }
-                    drawCurveSpecials(test, frags2, fragType);
+                    drawCurveSpecials(test, frags, fragType);
                     break;
                 case REC_TYPE_OP:
                     switch (fragType) {
@@ -5940,51 +2712,35 @@ function draw(test, lines, title) {
                     }
                     break;
                 case REC_TYPE_ANGLE:
-                    angleBetween = frags[18] == "T";
-                    afterIndex = 0;
-                    if (draw_angle == 0 || draw_angle == 3 || (step_limit > 0 && tIndex < lastAngle)) {
+                    if (!draw_angle || (step_limit > 0 && tIndex < lastAngle)) {
+                        continue;
+                    }
+                    if (fragType != ANGLE_AFTER && fragType != ANGLE_AFTER2) {
                         continue;
                     }
                     focus_enabled = true;
                     ctx.lineWidth = 3;
                     ctx.strokeStyle = "rgba(127,45,127, 0.3)";
-                    var leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
-                    var midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
-                    var rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
+                    var leftCurve, midCurve, rightCurve;
+                    if (fragType == ANGLE_AFTER) {
+                        leftCurve = curvePartialByID(test, frags[0], frags[3], frags[4]);
+                        midCurve = curvePartialByID(test, frags[5], frags[8], frags[9]);
+                        rightCurve = curvePartialByID(test, frags[10], frags[13], frags[14]);
+                    } else {
+                        leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
+                        midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
+                        rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
+                    }
                     drawCurve(leftCurve);
                     drawCurve(rightCurve);
-                    ctx.strokeStyle = angleBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
+                    var inBetween = frags[fragType == ANGLE_AFTER ? 15 : 18] == "T";
+                    ctx.strokeStyle = inBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
                     drawCurve(midCurve);
                     if (draw_angle > 1) {
                         drawOrder(leftCurve, 'L');
                         drawOrder(rightCurve, 'R');
                     }
                     break;
-                case REC_TYPE_AFTERPART:
-                    if (draw_angle != 3 || (step_limit > 0 && tIndex < lastAngle)) {
-                        continue;
-                    }
-                    ctx.strokeStyle = afterIndex == 0 ? "rgba(255,0,0, 1.0)"
-                            : (afterIndex == 1) == angleBetween ? "rgba(0,128,0, 1.0)"
-                            :  "rgba(0,0,255, 1.0)";
-                    switch (fragType) {
-                        case PATH_LINE:
-                            drawLine(frags[0], frags[1], frags[2], frags[3]);
-                            break;
-                        case PATH_QUAD:
-                            drawQuad(frags[0], frags[1], frags[2], frags[3],
-                                     frags[4], frags[5]);
-                            break;
-                        case PATH_CUBIC:
-                            drawCubic(frags[0], frags[1], frags[2], frags[3],
-                                      frags[4], frags[5], frags[6], frags[7]);
-                            break;
-                        default:
-                            console.log("unknown REC_TYPE_AFTERPART frag type: " + fragType);
-                            throw "stop execution";
-                    }
-                    ++afterIndex;
-                    break;
                 case REC_TYPE_SECT:
                     if (!draw_intersection) {
                         continue;
@@ -6190,8 +2946,8 @@ function draw(test, lines, title) {
                     if (collect_bounds) {
                         break;
                     }
-                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
-                        for (var idx = 0; idx < f.length; idx += 4) {
+                    for (var idx = 0; idx < f.length; idx += 4) {
+                        if (draw_intersection != 3 || idx == lastSect - tIndex) {
                             drawPoint(frags[f[idx]], frags[f[idx + 1]], true);
                         }
                     }
@@ -6199,8 +2955,8 @@ function draw(test, lines, title) {
                         break;
                     }
                     ctx.fillStyle = "red";
-                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
-                        for (var idx = 0; idx < f.length; idx += 4) {
+                    for (var idx = 0; idx < f.length; idx += 4) {
+                        if (draw_intersection != 3 || idx == lastSect - tIndex) {
                             drawTAtPointUp(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 2]]);
                             drawTAtPointDown(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 3]]);
                         }
@@ -6381,7 +3137,7 @@ function draw(test, lines, title) {
     }
     if (draw_legend) {
         var pos = 0;
-        var drawSomething = draw_add | draw_active | draw_angle | draw_sort | draw_mark;
+        var drawSomething = draw_add | draw_active | draw_sort | draw_mark;
    //     drawBox(pos++, "yellow", "black", opLetter, true, '');
         drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_intersection > 1 ? sectCount : sectMax2, draw_intersection, intersectionKey);
         drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_add ? addCount : addMax, draw_add, addKey);
@@ -6416,6 +3172,7 @@ function draw(test, lines, title) {
         ctx.fillText("curve t : " +  curveTKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("deriviatives : " +  deriviativesKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("intersect t : " +  intersectTKey, screenWidth - 10, pos * 50 + y++ * 10);
+        ctx.fillText("hodo : " +  hodoKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("log : " +  logKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("log curve : " +  logCurvesKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("mid point : " +  midpointKey, screenWidth - 10, pos * 50 + y++ * 10);
@@ -6552,6 +3309,15 @@ function dumpLogToConsole() {
             var fragType = records[recordIndex];
             var frags = records[recordIndex + 1];
             if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER) {
+                dumpCurvePartial(test, frags[0], frags[3], frags[4]);
+                dumpCurvePartial(test, frags[5], frags[8], frags[9]);
+                dumpCurvePartial(test, frags[10], frags[13], frags[14]);
+                console.log("\nstatic IntersectData intersectDataSet[] = {");
+                dumpAngleTest(test, frags[0], frags[3], frags[4]);
+                dumpAngleTest(test, frags[5], frags[8], frags[9]);
+                dumpAngleTest(test, frags[10], frags[13], frags[14]);
+                console.log("};");
+            } else if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER2) {
                 dumpCurvePartial(test, frags[0], frags[4], frags[5]);
                 dumpCurvePartial(test, frags[6], frags[10], frags[11]);
                 dumpCurvePartial(test, frags[12], frags[16], frags[17]);
@@ -6573,6 +3339,7 @@ var addKey = 'd';
 var deriviativesKey = 'f';
 var angleKey = 'g';
 var angleBackKey = 'G';
+var hodoKey = 'h';
 var intersectionKey = 'i';
 var intersectionBackKey = 'I';
 var sequenceKey = 'j';
@@ -6624,7 +3391,7 @@ function doKeyPress(evt) {
         redraw(); 
         break;
     case angleKey:
-        draw_angle = (draw_angle + 1) % 4;
+        draw_angle = (draw_angle + 1) % 3;
         redraw();
         break;
     case angleBackKey:
@@ -6667,6 +3434,10 @@ function doKeyPress(evt) {
         setScale(xmin, xmax, ymin, ymax);
         redraw();
         break;
+    case hodoKey:
+        draw_hodo = (draw_hodo + 1) % 4;
+        redraw();
+        break;
     case idKey:
         draw_id ^= true;
         redraw();