split tight quads and conics

Tight quads and conics may nearly fold over on themselves, confusing
coincidence against other curves. Split them at their max curvature
early on to avoid complicating later logic.

TBR=reed@google.com
BUG=skia:5131
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2357353002

Review-Url: https://codereview.chromium.org/2357353002
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 2abf67a..b3a82cd 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -542,7 +542,8 @@
                     SkTSwap(coinPtT[0], coinPtT[1]);
                     SkTSwap(testTAt, nextTAt);
                 }
-                SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
+                SkASSERT(coincidence->globalState()->debugSkipAssert()
+                        || coinPtT[0]->span()->t() < testTAt->span()->t());
                 if (coinPtT[0]->span()->deleted()) {
                     coinIndex = -1;
                     continue;
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index 1bbb82a..d2874c3 100755
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -334,6 +334,9 @@
             }
             SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
             SkOpPtT* oppStart = writableSeg->addT(t);
+            if (oppStart == testPtT) {
+                continue;
+            }
             SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
             oppStart->span()->addOpp(writableBase);
             if (oppStart->deleted()) {
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 3b2318c..981bd29 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -10,7 +10,7 @@
 #include "SkReduceOrder.h"
 #include "SkTSort.h"
 
-SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
+SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkScalar weight) {
     SkChunkAlloc* allocator = this->globalState()->allocator();
     switch (verb) {
         case SkPath::kLine_Verb: {
@@ -24,7 +24,9 @@
             return appendSegment().addQuad(ptStorage, this);
         } break;
         case SkPath::kConic_Verb: {
-            SkASSERT(0);  // the original curve is a cubic, which will never reduce to a conic
+            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
+            memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
+            return appendSegment().addConic(ptStorage, weight, this);
         } break;
         case SkPath::kCubic_Verb: {
             SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index acc6744..4390fe4 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -41,7 +41,7 @@
         appendSegment().addCubic(pts, this);
     }
 
-    SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4]);
+    SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkScalar weight = 1);
 
     SkOpSegment* addLine(SkPoint pts[2]) {
         SkASSERT(pts[0] != pts[1]);
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 36fc9ed..d67ed44 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -17,6 +17,26 @@
     fSecondHalf = preFetch();
 }
 
+// very tiny points cause numerical instability : don't allow them
+static void force_small_to_zero(SkPoint* pt) {
+    if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) {
+        pt->fX = 0;
+    }
+    if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) {
+        pt->fY = 0;
+    }
+}
+
+static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) {
+    if (SkPath::kMove_Verb == verb) {
+        return false;
+    }
+    for (int index = 0; index < SkPathOpsVerbToPoints(verb); ++index) {
+        force_small_to_zero(&curve[index]);
+    }
+    return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]);
+}
+
 void SkOpEdgeBuilder::addOperand(const SkPath& path) {
     SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
     fPathVerbs.pop();
@@ -48,16 +68,6 @@
     *fPathVerbs.append() = SkPath::kClose_Verb;
 }
 
-// very tiny points cause numerical instability : don't allow them
-static void force_small_to_zero(SkPoint* pt) {
-    if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) {
-        pt->fX = 0;
-    }
-    if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) {
-        pt->fY = 0;
-    }
-}
-
 int SkOpEdgeBuilder::preFetch() {
     if (!fPath->isFinite()) {
         fUnparseable = true;
@@ -107,8 +117,10 @@
                 force_small_to_zero(&pts[2]);
                 curve[1] = pts[1];
                 curve[2] = pts[2];
-                verb = SkReduceOrder::Conic(curve, iter.conicWeight(), pts);
-                if (verb == SkPath::kMove_Verb) {
+                verb = SkReduceOrder::Quad(curve, pts);
+                if (SkPath::kQuad_Verb == verb && 1 != iter.conicWeight()) {
+                  verb = SkPath::kConic_Verb;
+                } else if (verb == SkPath::kMove_Verb) {
                     continue;  // skip degenerate points
                 }
                 break;
@@ -183,49 +195,83 @@
                 fCurrentContour->addLine(pointsPtr);
                 break;
             case SkPath::kQuad_Verb:
+                {
+                    SkVector v1 = pointsPtr[1] - pointsPtr[0];
+                    SkVector v2 = pointsPtr[2] - pointsPtr[1];
+                    if (v1.dot(v2) < 0) {
+                        SkPoint pair[5];
+                        if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) {
+                            goto addOneQuad;
+                        }
+                        if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) {
+                            return false;
+                        }
+                        SkPoint cStorage[2][2];
+                        SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]);
+                        SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]);
+                        SkPoint* curve1 = v1 == SkPath::kQuad_Verb ? &pair[0] : cStorage[0];
+                        SkPoint* curve2 = v2 == SkPath::kQuad_Verb ? &pair[2] : cStorage[1];
+                        if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
+                            fCurrentContour->addCurve(v1, curve1);
+                            fCurrentContour->addCurve(v2, curve2);
+                            break;
+                        }
+                    }
+                }
+            addOneQuad:
                 fCurrentContour->addQuad(pointsPtr);
                 break;
-            case SkPath::kConic_Verb:
-                fCurrentContour->addConic(pointsPtr, *weightPtr++);
-                break;
-            case SkPath::kCubic_Verb: {
-                // Split complex cubics (such as self-intersecting curves or
-                // ones with difficult curvature) in two before proceeding.
-                // This can be required for intersection to succeed.
-                SkScalar splitT;
-                if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) {
-                    SkPoint cubicPair[7];
-                    SkChopCubicAt(pointsPtr, cubicPair, splitT);
-                    if (!SkScalarsAreFinite(&cubicPair[0].fX, SK_ARRAY_COUNT(cubicPair) * 2)) {
-                        return false;
+            case SkPath::kConic_Verb: {
+                SkVector v1 = pointsPtr[1] - pointsPtr[0];
+                SkVector v2 = pointsPtr[2] - pointsPtr[1];
+                SkScalar weight = *weightPtr++;
+                if (v1.dot(v2) < 0) {
+                    // FIXME: max curvature for conics hasn't been implemented; use placeholder
+                    SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr);
+                    if (maxCurvature > 0) {
+                        SkConic conic(pointsPtr, weight);
+                        SkConic pair[2];
+                        conic.chopAt(maxCurvature, pair);
+                        SkPoint cStorage[2][3];
+                        SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]);
+                        SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]);
+                        SkPoint* curve1 = v1 == SkPath::kConic_Verb ? pair[0].fPts : cStorage[0];
+                        SkPoint* curve2 = v2 == SkPath::kConic_Verb ? pair[1].fPts : cStorage[1];
+                        if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
+                            fCurrentContour->addCurve(v1, curve1, pair[0].fW);
+                            fCurrentContour->addCurve(v2, curve2, pair[1].fW);
+                            break;
+                        }
                     }
-                    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 (int index = 0; index < SkPathOpsVerbToPoints(v1); ++index) {
-                            force_small_to_zero(&curve1[index]);
-                        }
-                        for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) {
-                            force_small_to_zero(&curve2[index]);
-                        }
-                        if (SkPath::kLine_Verb != v1 ||
-                                !SkDPoint::ApproximatelyEqual(curve1[0], curve1[1])) {
-                            fCurrentContour->addCurve(v1, curve1);
-                        }
-                        if (SkPath::kLine_Verb != v2 ||
-                                !SkDPoint::ApproximatelyEqual(curve2[0], curve2[1])) {
-                            fCurrentContour->addCurve(v2, curve2);
-                        }
-                    } else {
-                        fCurrentContour->addCubic(pointsPtr);
-                    }
-                } else {
-                    fCurrentContour->addCubic(pointsPtr);
                 }
+                fCurrentContour->addConic(pointsPtr, weight);
                 } break;
+            case SkPath::kCubic_Verb:
+                {
+                    // Split complex cubics (such as self-intersecting curves or
+                    // ones with difficult curvature) in two before proceeding.
+                    // This can be required for intersection to succeed.
+                    SkScalar splitT;
+                    if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) {
+                        SkPoint pair[7];
+                        SkChopCubicAt(pointsPtr, pair, splitT);
+                        if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) {
+                            return false;
+                        }
+                        SkPoint cStorage[2][4];
+                        SkPath::Verb v1 = SkReduceOrder::Cubic(&pair[0], cStorage[0]);
+                        SkPath::Verb v2 = SkReduceOrder::Cubic(&pair[3], cStorage[1]);
+                        SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &pair[0] : cStorage[0];
+                        SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &pair[3] : cStorage[1];
+                        if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) {
+                            fCurrentContour->addCurve(v1, curve1);
+                            fCurrentContour->addCurve(v2, curve2);
+                            break;
+                        } 
+                    }
+                }
+                fCurrentContour->addCubic(pointsPtr);
+                break;
             case SkPath::kClose_Verb:
                 SkASSERT(fCurrentContour);
                 if (!close()) {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 1a965c2..9ab240d 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -249,7 +249,7 @@
     SkOpSpanBase* spanBase = &fHead;
     do {
         SkOpPtT* result = spanBase->ptT();
-        if (t == result->fT || this->match(result, this, t, pt)) {
+        if (t == result->fT || (!zero_or_one(t) && this->match(result, this, t, pt))) {
             spanBase->bumpSpanAdds();
             return result;
         }
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 67bcee4..5326add 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -1876,7 +1876,7 @@
     do {
         const SkOpPtT* ptT = next->ptT();
         int index = 0;
-        bool somethingBetween;
+        bool somethingBetween = false;
         do {
             ++index;
             ptT = ptT->next();
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index e390b91..f0e384a 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -19,6 +19,8 @@
 #define FORCE_RELEASE 1  // set force release to 1 for multiple thread -- no debugging
 #endif
 
+#define DEBUG_UNDER_DEVELOPMENT 1
+
 #define ONE_OFF_DEBUG 0
 #define ONE_OFF_DEBUG_MATHEMATICA 0
 
@@ -37,8 +39,6 @@
         if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
         else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
 
-#define DEBUG_UNDER_DEVELOPMENT 1
-
 #if FORCE_RELEASE
 
 #define DEBUG_ACTIVE_OP 0
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 0f4415b..d34c057 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -207,7 +207,7 @@
     SkDynamicMemoryWStream wStream;
     path.dump(&wStream, force, dumpAsHex);
     sk_sp<SkData> data(wStream.detachAsData());
-    fprintf(file, "%.*s\n", (int) data->size(), data->data());
+    fprintf(file, "%.*s\n", (int) data->size(), (char*) data->data());
 }
 
 static int dumpID = 0;
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 6ae6ee5..f84aaaa 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -1531,7 +1531,8 @@
         workT += tStep;
         workPt = fCurve.ptAtT(workT);
         coinW.setPerp(fCurve, workT, workPt, opp->fCurve);
-        if (coinW.perpT() < 0) {
+        double perpT = coinW.perpT();
+        if (coinW.isCoincident() ? !between(oppSpan->fStartT, perpT, oppSpan->fEndT) : perpT < 0) {
             continue;
         }
         SkDVector perpW = workPt - coinW.perpPt();
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 48624ba..7f7ea11 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -4,6 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "SkGeometry.h"
 #include "SkReduceOrder.h"
 
 int SkReduceOrder::reduce(const SkDLine& line) {
@@ -255,9 +256,9 @@
     return SkPathOpsPointsToVerb(order - 1);
 }
 
-SkPath::Verb SkReduceOrder::Conic(const SkPoint a[3], SkScalar weight, SkPoint* reducePts) {
-    SkPath::Verb verb = SkReduceOrder::Quad(a, reducePts);
-    if (verb > SkPath::kLine_Verb && weight == 1) {
+SkPath::Verb SkReduceOrder::Conic(const SkConic& c, SkPoint* reducePts) {
+    SkPath::Verb verb = SkReduceOrder::Quad(c.fPts, reducePts);
+    if (verb > SkPath::kLine_Verb && c.fW == 1) {
         return SkPath::kQuad_Verb;
     }
     return verb == SkPath::kQuad_Verb ? SkPath::kConic_Verb : verb;
diff --git a/src/pathops/SkReduceOrder.h b/src/pathops/SkReduceOrder.h
index e9e4090..7efb71d 100644
--- a/src/pathops/SkReduceOrder.h
+++ b/src/pathops/SkReduceOrder.h
@@ -11,6 +11,8 @@
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
 
+struct SkConic;
+
 union SkReduceOrder {
     enum Quadratics {
         kNo_Quadratics,
@@ -21,7 +23,7 @@
     int reduce(const SkDLine& line);
     int reduce(const SkDQuad& quad);
 
-    static SkPath::Verb Conic(const SkPoint pts[3], SkScalar weight, SkPoint* reducePts);
+    static SkPath::Verb Conic(const SkConic& conic, SkPoint* reducePts);
     static SkPath::Verb Cubic(const SkPoint pts[4], SkPoint* reducePts);
     static SkPath::Verb Quad(const SkPoint pts[3], SkPoint* reducePts);
 
diff --git a/tests/PathOpsConicLineIntersectionTest.cpp b/tests/PathOpsConicLineIntersectionTest.cpp
index 6219506..ecc4a47 100644
--- a/tests/PathOpsConicLineIntersectionTest.cpp
+++ b/tests/PathOpsConicLineIntersectionTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
 #include "SkIntersections.h"
 #include "SkPathOpsConic.h"
 #include "SkPathOpsLine.h"
@@ -103,7 +104,9 @@
         SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(),
             conic.fPts.fPts[2].asSkPoint() };
         SkPoint reduced[3];
-        SkPath::Verb order1 = SkReduceOrder::Conic(pts, conic.fWeight, reduced);
+        SkConic floatConic;
+        floatConic.set(pts, conic.fWeight);
+        SkPath::Verb order1 = SkReduceOrder::Conic(floatConic, reduced);
         if (order1 != SkPath::kConic_Verb) {
             SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1);
             REPORTER_ASSERT(reporter, 0);
diff --git a/tests/PathOpsCubicIntersectionTest.cpp b/tests/PathOpsCubicIntersectionTest.cpp
index dd7a7f7..9bf60b7 100644
--- a/tests/PathOpsCubicIntersectionTest.cpp
+++ b/tests/PathOpsCubicIntersectionTest.cpp
@@ -165,6 +165,9 @@
 const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
 
 static const SkDCubic newTestSet[] = {
+{ { { 130.0427549999999997, 11417.41309999999976 },{ 130.2331240000000037, 11418.3192999999992 },{ 131.0370790000000056, 11419 },{ 132, 11419 } } },
+{ { { 132, 11419 },{ 130.8954319999999996, 11419 },{ 130, 11418.10449999999946 },{ 130, 11417 } } },
+    
 {{{1,3}, {-1.0564518,1.79032254}, {1.45265341,0.229448318}, {1.45381773,0.22913377}}},
 {{{1.45381773,0.22913377}, {1.45425761,0.229014933}, {1.0967741,0.451612949}, {0,1}}},
 
@@ -698,7 +701,7 @@
 }
 
 DEF_TEST(PathOpsCubicIntersectionOneOff, reporter) {
-    newOneOff(reporter, 66, 70);
+    newOneOff(reporter, 0, 1);
 }
 
 DEF_TEST(PathOpsCubicIntersectionTestsOneOff, reporter) {
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index 8ff0079..9bb3a0f 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -5375,6 +5375,26 @@
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
+static void loop17(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(1, 2);
+    path.cubicTo(0, 3, -0.333333343f, 3.33333325f, 0.833333373f, 3.5f);
+    path.close();
+    pathB.moveTo(0, 3);
+    pathB.cubicTo(-0.333333343f, 3.33333325f, 0.833333373f, 3.5f, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void circlesOp4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addCircle(0, 1, 5, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.addCircle(0, 1, 0, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, 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;
@@ -5382,6 +5402,8 @@
 #define TEST(name) { name, #name }
 
 static struct TestDesc tests[] = {
+    TEST(circlesOp4),
+    TEST(loop17),
     TEST(cubicOp158),
     TEST(loops_i1),
     TEST(loops_i2),
diff --git a/tests/PathOpsQuadIntersectionTest.cpp b/tests/PathOpsQuadIntersectionTest.cpp
index f012622..be3d5a8 100644
--- a/tests/PathOpsQuadIntersectionTest.cpp
+++ b/tests/PathOpsQuadIntersectionTest.cpp
@@ -53,6 +53,9 @@
 }
 
 static const SkDQuad testSet[] = {
+{{{-0.001019871095195412636, -0.008523519150912761688}, {-0.005396408028900623322, -0.005396373569965362549}, {-0.02855382487177848816, -0.02855364233255386353}}},
+{{{-0.004567248281091451645, -0.01482933573424816132}, {-0.01142475008964538574, -0.01140109263360500336}, {-0.02852955088019371033, -0.02847047336399555206}}},
+
 {{{1, 1}, {0, 2}, {3, 3}}},
 {{{3, 0}, {0, 1}, {1, 2}}},
 
diff --git a/tests/PathOpsSimplifyFailTest.cpp b/tests/PathOpsSimplifyFailTest.cpp
index 4da43b8..0166a84 100644
--- a/tests/PathOpsSimplifyFailTest.cpp
+++ b/tests/PathOpsSimplifyFailTest.cpp
@@ -154,7 +154,7 @@
 path.moveTo(SkBits2Float(0x64b6b6b6), SkBits2Float(0xe9e9e900));  // 2.69638e+22f, -3.53475e+25f
 path.quadTo(SkBits2Float(0xb6b6b6e9), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e9b6ce), SkBits2Float(0xe9e93ae9));  // -5.44532e-06f, -5.44529e-06f, -3.53179e+25f, -3.52447e+25f
 
-    testSimplify(reporter, path, filename);
+    testSimplifyFuzz(reporter, path, filename);
 }
 
 
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 07e3720..55f0447 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -5256,7 +5256,7 @@
 #if DEBUG_UNDER_DEVELOPMENT  // tiger
     return;
 #endif
-    uint64_t testlines = 0x0000000a01900c00;  // best so far: 0x0000001d14c14bb1;
+    uint64_t testlines = 0x0000000000002008;  // best so far: 0x0000001d14c14bb1;
     tiger8a_x(reporter, filename, testlines);
 }
 
@@ -5331,7 +5331,7 @@
 #if DEBUG_UNDER_DEVELOPMENT  // tiger
     return;
 #endif
-    uint64_t testlines = 0x0000001000350204;  // best so far: 0x0000000104080223
+    uint64_t testlines = 0x000000201304b4a3;  // best so far: 0x000000201304b4a3
     tiger8b_x(reporter, filename, testlines);
 }
 
@@ -5870,11 +5870,10 @@
 }
 
 static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = tiger8b_h_1;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static TestDesc tests[] = {
-    TEST(testQuads73),
     TEST(tiger8a_h_1),
     TEST(tiger8a_h),
     TEST(tiger8a),
@@ -5882,6 +5881,7 @@
     TEST(tiger8b_h),
     TEST(tiger8b),
     TEST(tiger8),
+    TEST(testQuads73),
     TEST(testQuads72),
     TEST(testQuads71),
     TEST(testQuads70),
diff --git a/tests/PathOpsThreeWayTest.cpp b/tests/PathOpsThreeWayTest.cpp
index bf634f9..6a93bfb 100644
--- a/tests/PathOpsThreeWayTest.cpp
+++ b/tests/PathOpsThreeWayTest.cpp
@@ -75,6 +75,6 @@
 }
 
 DEF_TEST(PathOpsThreeWayOneOff, reporter) {
-    int index = 1;
+    int index = 0;
     testSetTest(reporter, index);
 }
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 667e86d..0983b70 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -6,9 +6,30 @@
     <title></title>
 <div style="height:0">
 
-<div id="lineconic">
-{{{{494.348663330078125, 224.583770751953125}, {494.365142822265625, 224.6331939697265625}, {494.37640380859375, 224.6840667724609375}}}, 0.998645842f},
-{{{494.371185302734375, 224.66168212890625}, {494.375213623046875, 224.6787261962890625}}},
+<div id="perp">
+{{{130.0427549999999997, 11417.41309999999976}, {130.2331240000000037, 11418.3192999999992}, {131.0370790000000056, 11419}, {132, 11419}}},
+{{{132, 11419}, {130.8954319999999996, 11419}, {130, 11418.10449999999946}, {130, 11417}}},
+</div>
+
+<div id="quads">
+{{{-0.001019871095195412636, -0.008523519150912761688}, {-0.005396408028900623322, -0.005396373569965362549}, {-0.02855382487177848816, -0.02855364233255386353}}},
+{{{-0.004567248281091451645, -0.01482933573424816132}, {-0.01142475008964538574, -0.01140109263360500336}, {-0.02852955088019371033, -0.02847047336399555206}}},
+        </div>
+
+<div id="both">
+{{fX=0.000000000 fY=0.000000000 }, {fX=494.350159 fY=228.773712 }, {fX=493.191650 fY=226.887451 }}
+{{fX=0.000000000 fY=0.000000000 } {fX=492.680206 fY=228.000900 } {fX=493.193817 fY=226.897568 }
+{{fX=493.193817 fY=226.897568 } {fX=493.195557 fY=226.893829 } {fX=493.191650 fY=226.887451 }    
+    </div>
+
+<div id="startguy">
+{{fX=0.000000000 fY=0.000000000 }, {fX=494.350159 fY=228.773712 }, {fX=493.191650 fY=226.887451 }}
+{{fX=0.000000000 fY=0.000000000 }, {fX=494.350159 fY=228.773712 }, {fX=493.191650 fY=226.887451 }}
+    </div>
+
+<div id="splitted">
+{{fX=0.000000000 fY=0.000000000 } {fX=492.680206 fY=228.000900 } {fX=493.193817 fY=226.897568 }
+{{fX=493.193817 fY=226.897568 } {fX=493.195557 fY=226.893829 } {fX=493.191650 fY=226.887451 }    
     </div>
 
 </div>
@@ -16,7 +37,11 @@
 <script type="text/javascript">
 
 var testDivs = [
-    lineconic,
+    perp,
+    quads,
+    both,
+    startguy,
+    splitted
 ];
 
     var decimal_places = 3;
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index 945a6a9..5cec64f 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -2,88 +2,150 @@
 <head>
 <div height="0" hidden="true">
 
-<div id="tiger8b_h_1">
-seg=1 {{{{494.348663f, 224.583771f}, {494.365143f, 224.633194f}, {494.376404f, 224.684067f}}}, 0.998645842f}
-seg=2 {{{494.376404f, 224.684067f}, {492.527069f, 224.218475f}, {492.952789f, 224.005585f}}}
-seg=3 {{{492.952789f, 224.005585f}, {494.375336f, 224.679337f}, {494.376038f, 224.682449f}}}
-seg=4 {{{494.376038f, 224.682449f}, {494.37619f, 224.68309f}}}
-seg=5 {{{494.37619f, 224.68309f}, {494.634338f, 225.414886f}, {494.895874f, 225.840698f}}}
-seg=6 {{{494.895874f, 225.840698f}, {494.348663f, 224.583771f}}}
-debugShowConicQuadIntersection wtTs[0]=1 {{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842} {{494.376404,224.684067}} wnTs[0]=0 {{{494.376404,224.684067}, {492.527069,224.218475}, {492.952789,224.005585}}}
-id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-id=1 1=(0,1) [6] id=2 6=(0.75,1) [1]
-id=1 3=(0.5,1) [6] id=2 6=(0.75,1) [3]
-id=1 3=(0.5,1) [8] id=2 8=(0.875,1) [3]
-id=1 5=(0.75,1) [8] id=2 8=(0.875,1) [5]
-id=1 7=(0.875,1) [8] id=2 8=(0.875,1) [7]
-id=1 7=(0.875,1) [10] id=2 10=(0.9375,1) [7]
-id=1 (empty) id=2 (empty)
-debugShowConicQuadIntersection no intersect {{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842} {{{492.952789,224.005585}, {494.375336,224.679337}, {494.376038,224.682449}}}
-debugShowConicLineIntersection wtTs[0]=0.988457533 {{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842} {{494.376038,224.682449}} wnTs[0]=0 {{{494.376038,224.682449}, {494.37619,224.68309}}}
-SkOpSegment::addT insert t=0.988457533 segID=1 spanID=13
-debugShowConicQuadIntersection no intersect {{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842} {{{494.37619,224.68309}, {494.634338,225.414886}, {494.895874,225.840698}}}
-debugShowConicLineIntersection wtTs[0]=0 {{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842} {{494.348663,224.583771}} wnTs[0]=1 {{{494.895874,225.840698}, {494.348663,224.583771}}}
-debugShowQuadIntersection wtTs[0]=1 {{{494.376404,224.684067}, {492.527069,224.218475}, {492.952789,224.005585}}} {{492.952789,224.005585}} wnTs[0]=0 {{{492.952789,224.005585}, {494.375336,224.679337}, {494.376038,224.682449}}}
-debugShowQuadLineIntersection no intersect {{{494.376404,224.684067}, {492.527069,224.218475}, {492.952789,224.005585}}} {{{494.376038,224.682449}, {494.37619,224.68309}}}
-id=1 1=(0,0.5) [2] id=2 2=(0,1) [1]
-id=1 1=(0,0.25) [2] id=2 2=(0,1) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{494.376404,224.684067}, {492.527069,224.218475}, {492.952789,224.005585}}} {{{494.37619,224.68309}, {494.634338,225.414886}, {494.895874,225.840698}}}
-debugShowQuadLineIntersection no intersect {{{494.376404,224.684067}, {492.527069,224.218475}, {492.952789,224.005585}}} {{{494.895874,225.840698}, {494.348663,224.583771}}}
-debugShowQuadLineIntersection wtTs[0]=1 {{{492.952789,224.005585}, {494.375336,224.679337}, {494.376038,224.682449}}} {{494.376038,224.682449}} wnTs[0]=0 {{{494.376038,224.682449}, {494.37619,224.68309}}}
-debugShowQuadLineIntersection no intersect {{{492.952789,224.005585}, {494.375336,224.679337}, {494.376038,224.682449}}} {{{494.895874,225.840698}, {494.348663,224.583771}}}
-debugShowQuadLineIntersection wtTs[0]=0 {{{494.37619,224.68309}, {494.634338,225.414886}, {494.895874,225.840698}}} {{494.37619,224.68309}} wnTs[0]=1 {{{494.376038,224.682449}, {494.37619,224.68309}}}
-debugShowLineIntersection no intersect {{{494.376038,224.682449}, {494.37619,224.68309}}} {{{494.895874,225.840698}, {494.348663,224.583771}}}
-debugShowQuadLineIntersection wtTs[0]=1 {{{494.37619,224.68309}, {494.634338,225.414886}, {494.895874,225.840698}}} {{494.895874,225.840698}} wnTs[0]=0 {{{494.895874,225.840698}, {494.348663,224.583771}}}
-SkOpSegment::markDone id=4 (494.376038,224.682449 494.37619,224.68309) t=0 [7] (494.376038,224.682449) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::sortAngles [1] tStart=0.988457533 [13]
-SkOpAngle::after [1/1] 9/9 tStart=0.988457533 tEnd=0 < [3/3] 13/13 tStart=1 tEnd=0 < [1/2] 25/25 tStart=0.988457533 tEnd=1  T 4
-SkOpAngle::afterPart {{{{494.376038,224.682449}, {494.364861,224.63218}, {494.348663,224.583771}}}, 0.998676896} id=1
-SkOpAngle::afterPart {{{494.376038,224.682449}, {492.952789,224.005585}, {492.952789,224.005585}}} id=3
-SkOpAngle::afterPart {{{{494.376038,224.682449}, {494.376312,224.683624}, {494.376404,224.684067}}}, 0.999999821} id=1
+<div id="loop17">
+seg=1 {{{1, 2}, {0, 3}, {-0.333333343f, 3.33333325f}, {0.833333373f, 3.5f}}}
+seg=2 {{{0.833333373f, 3.5f}, {1, 2}}}
+op sect
+seg=3 {{{0, 3}, {-0.333333343f, 3.33333325f}, {0.833333373f, 3.5f}, {1, 2}}}
+seg=4 {{{1, 2}, {0, 3}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}, {1,2}}} {{0,3}} wtTs[1]=1 {{1,2}} wnTs[0]=1 {{{1,2}, {0,3}}} wnTs[1]=0
+id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
+id=1 1=(0,0.5) [4] 3=(0.5,1) [2,4] id=2 2=(0,0) [3] 4=(0.5,1) [3,1]
+id=1 1=(0,0.5) [4] 3=(0.5,0.75) [4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(0.5,1) [3,1]
+id=1 1=(0,0.5) [4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(0.5,0.75) [1]
+id=1 7=(0.25,0.5) [4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(0.5,0.75) [7]
+id=1 7=(0.25,0.375) [4] 9=(0.375,0.5) [4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(0.5,0.75) [9,7]
+id=1 7=(0.25,0.375) [8,4] 5=(1,1) [2] id=2 2=(0,0) [5] 4=(0.5,0.625) [7] 8=(0.625,0.75) [7]
+id=1 7=(0.25,0.375) [10,8] 5=(1,1) [2] id=2 2=(0,0) [5] 10=(0.5625,0.625) [7] 8=(0.625,0.75) [7]
+id=1 11=(0.3125,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.75) [11]
+id=1 11=(0.3125,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.6875) [11]
+id=1 13=(0.34375,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.6875) [13]
+id=1 13=(0.34375,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.65625) [13]
+id=1 15=(0.359375,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.65625) [15]
+id=1 15=(0.359375,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.640625) [15]
+id=1 15=(0.359375,0.367188) [8] 17=(0.367188,0.375) [8] 5=(1,1) [2] id=2 2=(0,0) [5] 8=(0.625,0.640625) [17,15]
+id=1 15=(0.359375,0.367188) [18] 5=(1,1) [2] id=2 2=(0,0) [5] 18=(0.632813,0.640625) [15]
+id=1 19=(0.363281,0.367188) [18] 5=(1,1) [2] id=2 2=(0,0) [5] 18=(0.632813,0.640625) [19]
+setPerp t=0.36328125 cPt=(0.110739922,3.22537946) != oppT=0.627603681 fPerpPt=(0.110965509,3.21864052)
+setPerp t=0.3671875 cPt=(0.115765816,3.22551414) != oppT=0.636601379 fPerpPt=(0.115710094,3.22827643)
+setPerp t=0.6328125 cPt=(0.113641506,3.22423955) != oppT=0.363729545 fPerpPt=(0.111313976,3.22539824)
+setPerp t=0.63470694 cPt=(0.114662928,3.22626175) != oppT=0.367452497 fPerpPt=(0.116108709,3.2255209)
+setPerp t=0.635654159 cPt=(0.115183291,3.22727003) != oppT=0.369275918 fPerpPt=(0.118474752,3.22555923)
+setPerp t=0.636127769 cPt=(0.115445887,3.22777346) != oppT=0.370178237 fPerpPt=(0.119649848,3.22557288)
+setPerp t=0.636364574 cPt=(0.115577789,3.228025) != oppT=0.370627065 fPerpPt=(0.120235406,3.22557836)
+setPerp t=0.636482977 cPt=(0.115643891,3.22815073) != oppT=0.370850898 fPerpPt=(0.120527686,3.22558077)
+setPerp t=0.636542178 cPt=(0.11567698,3.22821358) != oppT=0.370962669 fPerpPt=(0.120673701,3.22558189)
+setPerp t=0.636571779 cPt=(0.115693534,3.22824501) != oppT=0.371018519 fPerpPt=(0.120746678,3.22558243)
+setPerp t=0.636586579 cPt=(0.115701813,3.22826072) != oppT=0.371046435 fPerpPt=(0.120783158,3.22558269)
+setPerp t=0.636593979 cPt=(0.115705954,3.22826857) != oppT=0.37106039 fPerpPt=(0.120801396,3.22558282)
+setPerp t=0.636597679 cPt=(0.115708024,3.2282725) != oppT=0.371067368 fPerpPt=(0.120810515,3.22558289)
+setPerp t=0.636599529 cPt=(0.115709059,3.22827446) != oppT=0.371070856 fPerpPt=(0.120815074,3.22558292)
+setPerp t=0.636600454 cPt=(0.115709577,3.22827545) != oppT=0.3710726 fPerpPt=(0.120817354,3.22558294)
+setPerp t=0.636600917 cPt=(0.115709835,3.22827594) != oppT=0.371073472 fPerpPt=(0.120818494,3.22558294)
+setPerp t=0.636601148 cPt=(0.115709965,3.22827618) != oppT=0.371073908 fPerpPt=(0.120819063,3.22558295)
+setPerp t=0.636601264 cPt=(0.11571003,3.2282763) != oppT=0.371074126 fPerpPt=(0.120819348,3.22558295)
+setPerp t=0.636601322 cPt=(0.115710062,3.22827637) != oppT=0.371074235 fPerpPt=(0.120819491,3.22558295)
+setPerp t=0.63660135 cPt=(0.115710078,3.2282764) != oppT=0.37107429 fPerpPt=(0.120819562,3.22558295)
+setPerp t=0.636601365 cPt=(0.115710086,3.22827641) != oppT=0.371074317 fPerpPt=(0.120819598,3.22558295)
+setPerp t=0.636601372 cPt=(0.11571009,3.22827642) != oppT=0.371074331 fPerpPt=(0.120819616,3.22558295)
+setPerp t=0.636601376 cPt=(0.115710092,3.22827642) != oppT=0.371074338 fPerpPt=(0.120819624,3.22558295)
+setPerp t=0.636601378 cPt=(0.115710093,3.22827643) != oppT=0.371074341 fPerpPt=(0.120819629,3.22558295)
+setPerp t=0.636601378 cPt=(0.115710094,3.22827643) != oppT=0.371074343 fPerpPt=(0.120819631,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819632,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+setPerp t=0.636601379 cPt=(0.115710094,3.22827643) != oppT=0.371074344 fPerpPt=(0.120819633,3.22558295)
+id=1 5=(1,1) [2] id=2 2=(0,0) [5]
+debugShowCubicIntersection wtTs[0]=1 {{{0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}, {1,2}}} {{1,2}} wnTs[0]=0 {{{1,2}, {0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}, {1,2}}} {{1,2}} wnTs[0]=1 {{{0.833333373,3.5}, {1,2}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{1,2}, {0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}}} {{1,2}} wnTs[0]=0 {{{1,2}, {0,3}}}
+debugShowLineIntersection wtTs[0]=0 {{{1,2}, {0,3}}} {{1,2}} wnTs[0]=1 {{{0.833333373,3.5}, {1,2}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{1,2}, {0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}}} {{1,2}} wtTs[1]=1 {{0.833333373,3.5}} wnTs[0]=1 {{{0.833333373,3.5}, {1,2}}} wnTs[1]=0
+-----------------------------------  start
+-----------------------------------  addExpanded
+-----------------------------------  moveMultiples
+-----------------------------------  moveNearby
+-----------------------------------  addEndMovedSpans
+-----------------------------------  addMissing2
+-----------------------------------  moveNearby2
+-----------------------------------  expand2
+-----------------------------------  addExpanded3
+-----------------------------------  mark1
+-----------------------------------  missingCoincidence2
+-----------------------------------  missingCoincidence3
+-----------------------------------  coincidence.reorder
+-----------------------------------  pairs->apply
+-----------------------------------  pairs->findOverlaps
 SkOpSegment::sortAngles [3] tStart=1 [6]
-SkOpSegment::debugShowActiveSpans id=1 (494.348663,224.583771 494.364952,224.632623 494.376129,224.682892 0.998676896f) t=0 tEnd=0.988457533 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=1 (494.376129,224.682892 494.376274,224.68348 494.376404,224.684067 0.999999821f) t=0.988457533 tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=2 (494.376404,224.684067 492.527069,224.218475 492.952789,224.005585) t=0 tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=3 (492.952789,224.005585 494.375336,224.679337 494.376038,224.682449) t=0 tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=5 (494.37619,224.68309 494.634338,225.414886 494.895874,225.840698) t=0 tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=6 (494.895874,225.840698 494.348663,224.583771) t=0 tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kLeft seg=1 t=0.494228767 pt=(494.363678,224.63298)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=3 ccw=1 seg=2 {{{494.376404f, 224.684067f}, {492.527069f, 224.218475f}, {492.952789f, 224.005585f}}} t=0.0557039225 pt=(494.177429,224.63298) slope=(-1.72260523,-0.451515005)
-SkOpSpan::sortableTop [1] valid=1 operand=0 span=5 ccw=0 seg=3 {{{492.952789f, 224.005585f}, {494.375336f, 224.679337f}, {494.376038f, 224.682449f}}} t=0.733006652 pt=(494.274292,224.63298) slope=(0.380324923,0.182168955)
-SkOpSpan::sortableTop [2] valid=1 operand=0 span=1 ccw=0 seg=1 {{{{494.348663f, 224.583771f}, {494.365143f, 224.633194f}, {494.376404f, 224.684067f}}}, 0.998645842f} t=0.494228767 pt=(494.363678,224.63298) slope=(0.0138909232,0.050105697)
-SkOpSegment::markWinding id=2 (494.376404,224.684067 492.527069,224.218475 492.952789,224.005585) t=0 [3] (494.376404,224.684067) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=3 (492.952789,224.005585 494.375336,224.679337 494.376038,224.682449) t=0 [5] (492.952789,224.005585) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=2 (494.376404,224.684067 492.527069,224.218475 492.952789,224.005585) t=0 [3] (494.376404,224.684067) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (494.348663,224.583771 494.365143,224.633194 494.376404,224.684067) t=0.988457533 [13] (494.376129,224.682892) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (494.348663,224.583771 494.365143,224.633194 494.376404,224.684067) t=0 [1] (494.348663,224.583771) tEnd=0.988457533 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (494.348663,224.583771 494.365143,224.633194 494.376404,224.684067) t=0 [1] (494.348663,224.583771) tEnd=0.988457533 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=6 (494.895874,225.840698 494.348663,224.583771) t=0 [11] (494.895874,225.840698) tEnd=1 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=5 (494.37619,224.68309 494.634338,225.414886 494.895874,225.840698) t=0 [9] (494.37619,224.68309) tEnd=1 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=1 (494.348663,224.583771 494.365143,224.633194 494.376404,224.684067) t=0 [1] (494.348663,224.583771) tEnd=0.988457533 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
-bridgeWinding current id=1 from=(494.376129,224.682892) to=(494.348663,224.583771)
-path.moveTo(494.376129,224.682892);
-path.conicTo(494.36496,224.632629, 494.348663,224.583771, 0.998676896);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=6 (494.895874,225.840698 494.348663,224.583771) t=0 [11] (494.895874,225.840698) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
-bridgeWinding current id=6 from=(494.348663,224.583771) to=(494.895874,225.840698)
-SkOpSegment::markDone id=5 (494.37619,224.68309 494.634338,225.414886 494.895874,225.840698) t=0 [9] (494.37619,224.68309) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
-path.lineTo(494.895874,225.840698);
-SkOpSegment::debugShowActiveSpans id=1 (494.376129,224.682892 494.376274,224.68348 494.376404,224.684067 0.999999821f) t=0.988457533 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=2 (494.376404,224.684067 492.527069,224.218475 492.952789,224.005585) t=0 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=3 (492.952789,224.005585 494.375336,224.679337 494.376038,224.682449) t=0 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0
-SkOpSegment::findNextWinding
-SkOpAngle::dumpOne [1/2] next=1/1 sect=25/25  s=0.988457533 [13] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
-SkOpAngle::dumpOne [1/1] next=3/3 sect=9/9  s=0.988457533 [13] e=0 [1] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=0 done
-SkOpAngle::dumpOne [3/3] next=1/2 sect=13/13  s=1 [6] e=0 [5] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0
-SkOpSegment::markDone id=3 (492.952789,224.005585 494.375336,224.679337 494.376038,224.682449) t=0 [5] (492.952789,224.005585) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDone id=2 (494.376404,224.684067 492.527069,224.218475 492.952789,224.005585) t=0 [3] (494.376404,224.684067) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDone id=1 (494.348663,224.583771 494.365143,224.633194 494.376404,224.684067) t=0.988457533 [13] (494.376129,224.682892) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextWinding from:[1] to:[1] start=13259432 end=18937312
-bridgeWinding current id=1 from=(494.376404,224.684067) to=(494.376129,224.682892)
-path.moveTo(494.376404,224.684067);
-path.lineTo(494.376129,224.682892);
+SkOpAngle::after [3/1] 21/20 tStart=1 tEnd=0 < [1/3] 20/21 tStart=0 tEnd=1 < [2/4] 21/21 tStart=1 tEnd=0  F 7
+SkOpAngle::afterPart {{{1,2}, {0.833333373,3.5}, {-0.333333343,3.33333325}, {0,3}}} id=3
+SkOpAngle::afterPart {{{1,2}, {0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}}} id=1
+SkOpAngle::afterPart {{{1,2}, {0.833333373,3.5}}} id=2
+SkOpAngle::after [3/1] 21/20 tStart=1 tEnd=0 < [4/2] 19/19 tStart=0 tEnd=1 < [2/4] 21/21 tStart=1 tEnd=0  F 5
+SkOpAngle::afterPart {{{1,2}, {0.833333373,3.5}, {-0.333333343,3.33333325}, {0,3}}} id=3
+SkOpAngle::afterPart {{{1,2}, {0,3}}} id=4
+SkOpAngle::afterPart {{{1,2}, {0.833333373,3.5}}} id=2
+SkOpAngle::after [2/4] 21/21 tStart=1 tEnd=0 < [4/2] 19/19 tStart=0 tEnd=1 < [1/3] 20/21 tStart=0 tEnd=1  T 5
+SkOpAngle::afterPart {{{1,2}, {0.833333373,3.5}}} id=2
+SkOpAngle::afterPart {{{1,2}, {0,3}}} id=4
+SkOpAngle::afterPart {{{1,2}, {0,3}, {-0.333333343,3.33333325}, {0.833333373,3.5}}} id=1
+SkOpSegment::sortAngles [4] tStart=0 [7]
+SkOpSegment::sortAngles [1] tStart=0 [1]
+SkOpSegment::sortAngles [2] tStart=1 [4]
+SkOpSegment::debugShowActiveSpans id=3 (0,3 -0.333333343,3.33333325 0.833333373,3.5 1,2) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=4 (1,2 0,3) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (1,2 0,3 -0.333333343,3.33333325 0.833333373,3.5) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (0.833333373,3.5 1,2) t=0 tEnd=1 windSum=? windValue=1
+SkOpSpan::sortableTop dir=kTop seg=3 t=0.5 pt=(0.3125,3.1875)
+SkOpSpan::sortableTop [0] valid=1 operand=1 span=7 ccw=0 seg=4 {{{1, 2}, {0, 3}}} t=0.6875 pt=(0.3125,2.6875) slope=(-1,1)
+SkOpSpan::sortableTop [1] valid=1 operand=0 span=1 ccw=0 seg=1 {{{1, 2}, {0, 3}, {-0.333333343f, 3.33333325f}, {0.833333373f, 3.5f}}} t=0.293719533 pt=(0.3125,2.72128606) slope=(-1.60944396,1.95452854)
+SkOpSpan::sortableTop [2] valid=1 operand=1 span=5 ccw=1 seg=3 {{{0, 3}, {-0.333333343f, 3.33333325f}, {0.833333373f, 3.5f}, {1, 2}}} t=0.5 pt=(0.3125,3.1875) slope=(1.62500004,-0.62499994)
+SkOpSegment::markWinding id=4 (1,2 0,3) t=0 [7] (1,2) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=3 (0,3 -0.333333343,3.33333325 0.833333373,3.5 1,2) t=0 [5] (0,3) tEnd=1 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (1,2 0,3) t=0 [7] (1,2) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=1 (1,2 0,3 -0.333333343,3.33333325 0.833333373,3.5) t=0 [1] (1,2) tEnd=1 newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=2 (0.833333373,3.5 1,2) t=0 [3] (0.833333373,3.5) tEnd=1 newWindSum=1 newOppSum=1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=1 (1,2 0,3 -0.333333343,3.33333325 0.833333373,3.5) t=0 [1] (1,2) tEnd=1 newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=3 t=1 tEnd=0 op=sect miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
+SkOpSegment::markDone id=3 (0,3 -0.333333343,3.33333325 0.833333373,3.5 1,2) t=0 [5] (0,3) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markDone id=4 (1,2 0,3) t=0 [7] (1,2) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+bridgeOp chase.append id=4 windSum=1
+SkOpSegment::debugShowActiveSpans id=1 (1,2 0,3 -0.333333343,3.33333325 0.833333373,3.5) t=0 tEnd=1 windSum=1 oppSum=1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (0.833333373,3.5 1,2) t=0 tEnd=1 windSum=1 oppSum=1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=1 t=0 tEnd=1 op=sect miFrom=0 miTo=1 suFrom=1 suTo=1 result=1
+SkOpSegment::findNextOp simple
+SkOpSegment::markDone id=1 (1,2 0,3 -0.333333343,3.33333325 0.833333373,3.5) t=0 [1] (1,2) tEnd=1 newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 oppValue=0
+bridgeOp current id=1 from=(1,2) to=(0.833333373,3.5)
+path.moveTo(1,2);
+path.cubicTo(0,3, -0.333333343,3.33333325, 0.833333373,3.5);
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [2/4] next=4/2 sect=21/21  s=1 [4] e=0 [3] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=1
+SkOpAngle::dumpOne [4/2] next=1/3 sect=19/19  s=0 [7] e=1 [8] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=0 done operand
+SkOpAngle::dumpOne [1/3] next=3/1 sect=20/21  s=0 [1] e=1 [2] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=1 done
+SkOpAngle::dumpOne [3/1] next=2/4 sect=21/20  s=1 [6] e=0 [5] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=0 done operand
+SkOpSegment::activeOp id=4 t=0 tEnd=1 op=sect miFrom=0 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::activeOp id=1 t=0 tEnd=1 op=sect miFrom=0 miTo=1 suFrom=1 suTo=1 result=1
+SkOpSegment::activeOp id=3 t=1 tEnd=0 op=sect miFrom=1 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=2 (0.833333373,3.5 1,2) t=0 [3] (0.833333373,3.5) tEnd=1 newWindSum=1 newOppSum=1 oppSum=1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[2] to:[1] start=20180984 end=20181096
+bridgeOp current id=2 from=(0.833333373,3.5) to=(1,2)
+path.lineTo(1,2);
+path.close();
 </div>
 
 </div>
@@ -91,7 +153,7 @@
 <script type="text/javascript">
 
     var testDivs = [
-        tiger8b_h_1,
+        loop17,
     ];
 
 var decimal_places = 3; // make this 3 to show more precision