pathops version two

R=reed@google.com

marked 'no commit' to attempt to get trybots to run

TBR=reed@google.com

Review URL: https://codereview.chromium.org/1002693002
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index c27434f..b507eb7 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkPathOpsBounds.h"
 
 #if DEBUG_ADD_INTERSECTING_TS
@@ -130,20 +131,6 @@
     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& ) {
@@ -168,13 +155,10 @@
 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) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+        SkChunkAlloc* allocator) {
     if (test != next) {
         if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
             return false;
@@ -186,10 +170,11 @@
     }
     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;
         }
@@ -306,14 +291,22 @@
                             break;
                         }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            pts = ts.quadQuad(wt.pts(), wn.pts());
-                            ts.alignQuadPts(wt.pts(), wn.pts());
+                            SkDQuad quad1;
+                            quad1.set(wt.pts());
+                            SkDQuad quad2;
+                            quad2.set(wn.pts());
+                            pts = ts.intersect(quad1, quad2);
                             debugShowQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
                         case SkIntersectionHelper::kCubic_Segment: {
                             swap = true;
-                            pts = ts.cubicQuad(wn.pts(), wt.pts());
+                            SkDQuad quad1;
+                            quad1.set(wt.pts());
+                            SkDCubic cubic1 = quad1.toCubic();
+                            SkDCubic cubic2;
+                            cubic2.set(wn.pts());
+                            pts = ts.intersect(cubic2, cubic1);
                             debugShowCubicQuadIntersection(pts, wn, wt, ts);
                             break;
                         }
@@ -339,12 +332,21 @@
                             break;
                         }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            pts = ts.cubicQuad(wt.pts(), wn.pts());
+                            SkDCubic cubic1;
+                            cubic1.set(wt.pts());
+                            SkDQuad quad2;
+                            quad2.set(wn.pts());
+                            SkDCubic cubic2 = quad2.toCubic();
+                            pts = ts.intersect(cubic1, cubic2);
                             debugShowCubicQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
                         case SkIntersectionHelper::kCubic_Segment: {
-                            pts = ts.cubicCubic(wt.pts(), wn.pts());
+                            SkDCubic cubic1;
+                            cubic1.set(wt.pts());
+                            SkDCubic cubic2;
+                            cubic2.set(wn.pts());
+                            pts = ts.intersect(cubic1, cubic2);
                             debugShowCubicIntersection(pts, wt, wn, ts);
                             break;
                         }
@@ -355,102 +357,53 @@
                 default:
                     SkASSERT(0);
             }
-            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)
-                        }
-                    }
-                }
-            }
+            int coinIndex = -1;
+            SkOpPtT* coinPtT[2];
             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);
-                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);
+                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;
             }
+            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;
-}
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 4c1947b..23654e5 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -9,10 +9,10 @@
 
 #include "SkIntersectionHelper.h"
 #include "SkIntersections.h"
-#include "SkTArray.h"
 
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
-void AddSelfIntersectTs(SkOpContour* test);
-bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
+class SkOpCoincidence;
+
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+        SkChunkAlloc* allocator);
 
 #endif
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
deleted file mode 100644
index 2fb35e1..0000000
--- a/src/pathops/SkDCubicIntersection.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- * 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();
-}
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index 696c42e..f5fe015 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -93,6 +93,29 @@
         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;
@@ -131,32 +154,11 @@
             double cubicT = rootVals[index];
             double lineT = findLineT(cubicT);
             SkDPoint 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;
-                    }
-                }
+            if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
                 fIntersections->insert(cubicT, lineT, pt);
-        skipInsert:
-                ;
             }
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -186,20 +188,43 @@
         int count = HorizontalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt;
-            pt.fX = fCubic.ptAtT(cubicT).fX;
-            pt.fY = axisIntercept;
+            SkDPoint pt = { fCubic.ptAtT(cubicT).fX,  axisIntercept };
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
                 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);
@@ -226,17 +251,16 @@
         int count = VerticalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt;
-            pt.fX = axisIntercept;
-            pt.fY = fCubic.ptAtT(cubicT).fY;
+            SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
                 fIntersections->insert(cubicT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -342,7 +366,7 @@
         double lT = *lineT = SkPinT(*lineT);
         SkDPoint lPt = fLine.ptAtT(lT);
         SkDPoint cPt = fCubic.ptAtT(cT);
-        if (!lPt.moreRoughlyEqual(cPt)) {
+        if (!lPt.roughlyEqual(cPt)) {
             return false;
         }
         // FIXME: if points are roughly equal but not approximately equal, need to do
diff --git a/src/pathops/SkDCubicToQuads.cpp b/src/pathops/SkDCubicToQuads.cpp
index a28564d..2d034b6 100644
--- a/src/pathops/SkDCubicToQuads.cpp
+++ b/src/pathops/SkDCubicToQuads.cpp
@@ -19,62 +19,10 @@
  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;
@@ -86,101 +34,3 @@
     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);
-}
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index 8fc673f..ed96b9c 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -7,45 +7,6 @@
 #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);
@@ -58,6 +19,9 @@
             removeOne(endMatch);
         }
     }
+    if (fUsed == 2) {
+        fIsCoincident[0] = fIsCoincident[1] = 0x03;
+    }
 }
 
 void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -81,12 +45,6 @@
     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;
@@ -190,7 +148,6 @@
                     }
                     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;
@@ -235,18 +192,6 @@
      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
@@ -323,18 +268,6 @@
     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
@@ -393,14 +326,3 @@
     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
deleted file mode 100644
index f0f66d1..0000000
--- a/src/pathops/SkDQuadImplicit.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 24f1aac..0000000
--- a/src/pathops/SkDQuadImplicit.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
deleted file mode 100644
index fcb9171..0000000
--- a/src/pathops/SkDQuadIntersection.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-// 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;
-        }
-    }
-}
diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp
index ef8edb0..b8a9a64 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -105,6 +105,29 @@
         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
@@ -140,20 +163,17 @@
         if (fAllowNear) {
             addNearEndPoints();
         }
-        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);
-                }
+        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);
             }
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -178,16 +198,41 @@
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
                 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
@@ -209,13 +254,14 @@
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
                 fIntersections->insert(quadT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
diff --git a/src/pathops/SkIntersectionHelper.h b/src/pathops/SkIntersectionHelper.h
index 3569c93..c633fd0 100644
--- a/src/pathops/SkIntersectionHelper.h
+++ b/src/pathops/SkIntersectionHelper.h
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "SkOpContour.h"
+#include "SkOpSegment.h"
 #include "SkPath.h"
 
 #ifdef SK_DEBUG
@@ -21,42 +22,9 @@
         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() {
-        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);
+        fSegment = fSegment->next();
+        return fSegment != NULL;
     }
 
     SkScalar bottom() const {
@@ -64,30 +32,15 @@
     }
 
     const SkPathOpsBounds& bounds() const {
-        return fContour->segments()[fIndex].bounds();
+        return fSegment->bounds();
+    }
+
+    SkOpContour* contour() const {
+        return fSegment->contour();
     }
 
     void init(SkOpContour* contour) {
-        fContour = contour;
-        fIndex = 0;
-        fLast = contour->segments().count();
-    }
-
-    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);
+        fSegment = contour->first();
     }
 
     SkScalar left() const {
@@ -95,41 +48,40 @@
     }
 
     const SkPoint* pts() const {
-        return fContour->segments()[fIndex].pts();
+        return fSegment->pts();
     }
 
     SkScalar right() const {
         return bounds().fRight;
     }
 
+    SkOpSegment* segment() const {
+        return fSegment;
+    }
+
     SegmentType segmentType() const {
-        const SkOpSegment& segment = fContour->segments()[fIndex];
-        SegmentType type = (SegmentType) segment.verb();
+        SegmentType type = (SegmentType) fSegment->verb();
         if (type != kLine_Segment) {
             return type;
         }
-        if (segment.isHorizontal()) {
+        if (fSegment->isHorizontal()) {
             return kHorizontalLine_Segment;
         }
-        if (segment.isVertical()) {
+        if (fSegment->isVertical()) {
             return kVerticalLine_Segment;
         }
         return kLine_Segment;
     }
 
     bool startAfter(const SkIntersectionHelper& after) {
-        fIndex = after.fIndex;
-        return advance();
+        fSegment = after.fSegment->next();
+        return fSegment != NULL;
     }
 
     SkScalar top() const {
         return bounds().fTop;
     }
 
-    SkPath::Verb verb() const {
-        return fContour->segments()[fIndex].verb();
-    }
-
     SkScalar x() const {
         return bounds().fLeft;
     }
@@ -147,10 +99,5 @@
     }
 
 private:
-    // utility callable by the user from the debugger when the implementation code is linked in
-    void dump() const;
-
-    SkOpContour* fContour;
-    int fIndex;
-    int fLast;
+    SkOpSegment* fSegment;
 };
diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp
index e9875cf..007efa7 100644
--- a/src/pathops/SkIntersections.cpp
+++ b/src/pathops/SkIntersections.cpp
@@ -7,26 +7,25 @@
 
 #include "SkIntersections.h"
 
-void SkIntersections::append(const SkIntersections& i) {
-    for (int index = 0; index < i.fUsed; ++index) {
-        insert(i[0][index], i[1][index], i.pt(index));
+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;
+        }
     }
+    return closest;
 }
 
-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
-};
-
+// called only by test code
 int SkIntersections::coincidentUsed() const {
     if (!fIsCoincident[0]) {
         SkASSERT(!fIsCoincident[1]);
@@ -48,12 +47,12 @@
     return count;
 }
 
-int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
-    SkDCubic cubic;
-    cubic.set(pts);
-    fMax = 3;
-    return intersectRay(cubic, line);
-}
+int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
+    NULL,
+    &SkIntersections::verticalLine,
+    &SkIntersections::verticalQuad,
+    &SkIntersections::verticalCubic
+};
 
 void SkIntersections::flip() {
     for (int index = 0; index < fUsed; ++index) {
@@ -105,7 +104,6 @@
     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);
@@ -125,39 +123,53 @@
     SkASSERT(one == 0 || one == 1);
     SkASSERT(two == 0 || two == 1);
     SkASSERT(pt1 != pt2);
-    SkASSERT(fNearlySame[(int) one]);
+    fNearlySame[one ? 1 : 0] = true;
     (void) insert(one, two, pt1);
-    fPt2[one ? fUsed - 1 : 0] = pt2;
+    fPt2[one ? 1 : 0] = pt2;
 }
 
-void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
+int 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;
 }
 
-int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
-    SkDLine l;
-    l.set(pts);
-    fMax = 2;
-    return intersectRay(l, line);
+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;
 }
 
-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;
+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;
+        }
     }
-}
-
-int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
-    SkDQuad quad;
-    quad.set(pts);
-    fMax = 2;
-    return intersectRay(quad, line);
+    return result;
 }
 
 void SkIntersections::quickRemoveOne(int index, int replace) {
@@ -172,7 +184,6 @@
         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);
@@ -182,13 +193,6 @@
     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;
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index a1bde51..15bac19 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -16,7 +16,6 @@
 public:
     SkIntersections()
         : fSwap(0)
-        , fFlatMeasure(false)
 #ifdef SK_DEBUG
         , fDepth(0)
 #endif
@@ -24,7 +23,6 @@
         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
@@ -32,7 +30,7 @@
 
     class TArray {
     public:
-        explicit TArray(const double ts[9]) : fTArray(ts) {}
+        explicit TArray(const double ts[10]) : fTArray(ts) {}
         double operator[](int n) const {
             return fTArray[n];
         }
@@ -40,28 +38,15 @@
     };
     TArray operator[](int n) const { return TArray(fT[n]); }
 
-    void allowFlatMeasure(bool flatAllowed) {
-        fFlatMeasure = flatAllowed;
-    }
-
     void allowNear(bool nearAllowed) {
         fAllowNear = nearAllowed;
     }
 
-    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);
+    void clearCoincidence(int index) {
+        SkASSERT(index >= 0);
+        int bit = 1 << index;
+        fIsCoincident[0] &= ~bit;
+        fIsCoincident[1] &= ~bit;
     }
 
     int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@@ -88,19 +73,6 @@
         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);
@@ -178,19 +150,11 @@
         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) {
@@ -205,8 +169,6 @@
         fSwap ^= true;
     }
 
-    void swapPts();
-
     bool swapped() const {
         return fSwap;
     }
@@ -219,19 +181,27 @@
         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]);
@@ -242,25 +212,20 @@
     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
-    void insertCoincident(double one, double two, const SkDPoint& pt);
+    int 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&);
-    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 merge(const SkIntersections& , int , const SkIntersections& , int );
+    int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
     void quickRemoveOne(int index, int replace);
-    int quadRay(const SkPoint pts[3], const SkDLine& line);
     void removeOne(int index);
-    static bool Test(const SkDLine& , const SkDLine&);
-    int vertical(const SkDLine&, double x);
+    void setCoincident(int index);
     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);
@@ -276,6 +241,8 @@
 #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);
@@ -283,22 +250,20 @@
     void cleanUpParallelLines(bool parallel);
     void computePoints(const SkDLine& line, int used);
 
-    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];
+    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];
     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);
 
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index b3a188c..c13a51a 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -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(SkString* bugOut, int append, bool compare) {
+    static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, 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(&bugOut, append, compare)
+    #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
+            compare)
 #else
     #define COMPARE_RESULT(append, compare) compare
 #endif
@@ -58,51 +58,50 @@
 */
 
 // return true if lh < this < rh
-bool SkOpAngle::after(const SkOpAngle* test) const {
-    const SkOpAngle& lh = *test;
-    const SkOpAngle& rh = *lh.fNext;
-    SkASSERT(&lh != &rh);
+bool SkOpAngle::after(SkOpAngle* test) {
+    SkOpAngle* lh = test;
+    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.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));
+            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() };
 #endif
-    if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) {
+    if (lh->fComputeSector && !lh->computeSector()) {
         return COMPARE_RESULT(1, true);
     }
-    if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) {
+    if (fComputeSector && !this->computeSector()) {
         return COMPARE_RESULT(2, true);
     }
-    if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) {
+    if (rh->fComputeSector && !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.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));
+            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());
 #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
@@ -115,24 +114,24 @@
          */
         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) {
@@ -145,20 +144,20 @@
     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);
     }
@@ -173,77 +172,50 @@
 
 // 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) const {
+int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
     SkASSERT(!fIsCurve);
-    SkASSERT(test.fIsCurve);
-    const SkDPoint& origin = test.fCurvePart[0];
+    SkASSERT(test->fIsCurve);
+    const SkDPoint& origin = test->fCurvePart[0];
     SkVector line;
-    if (fSegment->verb() == SkPath::kLine_Verb) {
-        const SkPoint* linePts = fSegment->pts();
-        int lineStart = fStart < fEnd ? 0 : 1;
+    if (segment()->verb() == SkPath::kLine_Verb) {
+        const SkPoint* linePts = segment()->pts();
+        int lineStart = fStart->t() < fEnd->t() ? 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.fSegment->verb();
+    SkPath::Verb testVerb = test->segment()->verb();
     int iMax = SkPathOpsVerbToPoints(testVerb);
 //    SkASSERT(origin == test.fCurveHalf[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) {
+    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) {
             return -1;
         }
-        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;
-        }
+    }
+    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);
@@ -251,31 +223,94 @@
     return crossesZero;
 }
 
-bool SkOpAngle::checkParallel(const SkOpAngle& rh) const {
+// 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) {
     SkDVector scratch[2];
     const SkDVector* sweep, * tweep;
-    if (!fUnorderedSweep) {
-        sweep = fSweep;
+    if (!this->fUnorderedSweep) {
+        sweep = this->fSweep;
     } else {
-        scratch[0] = fCurvePart[1] - fCurvePart[0];
+        scratch[0] = this->fCurvePart[1] - this->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;
     }
-    SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
-    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[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];
     double m0xm1 = m0.crossCheck(m1);
     if (m0xm1 == 0) {
-        fUnorderable = true;
-        rh.fUnorderable = true;
+        this->fUnorderable = true;
+        rh->fUnorderable = true;
         return true;
     }
     return m0xm1 < 0;
@@ -288,48 +323,51 @@
     if (fComputedSector) {
         return !fUnorderable;
     }
-//    SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small());
     fComputedSector = true;
-    int step = fStart < fEnd ? 1 : -1;
-    int limit = step > 0 ? fSegment->count() : -1;
-    int checkEnd = fEnd;
-    do {
-// advance end
-        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.fOtherIndex == checkEnd) {
-                continue;
-            }
-            if (!approximately_equal(oSpan.fOtherT, span.fT)) {
-                continue;
-            }
-            goto recomputeSector;
-        }
-        checkEnd += step;
-    } while (checkEnd != limit);
-recomputeSector:
-    if (checkEnd == fEnd || checkEnd - step == fEnd) {
+    bool stepUp = fStart->t() < fEnd->t();
+    const SkOpSpanBase* checkEnd = fEnd;
+    if (checkEnd->final() && stepUp) {
         fUnorderable = true;
         return false;
     }
-    int saveEnd = fEnd;
-    fComputedEnd = fEnd = checkEnd - step;
+    do {
+// advance end
+        const SkOpSegment* other = checkEnd->segment();
+        const SkOpSpanBase* oSpan = other->head();
+        do {
+            if (oSpan->segment() != segment()) {
+                continue;
+            }
+            if (oSpan == checkEnd) {
+                continue;
+            }
+            if (!approximately_equal(oSpan->t(), checkEnd->t())) {
+                continue;
+            }
+            goto recomputeSector;
+        } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
+        checkEnd = stepUp ? !checkEnd->final()
+                ? checkEnd->upCast()->next() : NULL
+                : checkEnd->prev();
+    } while (checkEnd);
+recomputeSector:
+    SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head() 
+            : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
+    if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
+        fUnorderable = true;
+        return false;
+    }
+    SkOpSpanBase* saveEnd = fEnd;
+    fComputedEnd = fEnd = computedEnd;
     setSpans();
     setSector();
     fEnd = saveEnd;
     return !fUnorderable;
 }
 
-// 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;
+int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
+    const SkDVector* sweep = this->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]);
@@ -359,8 +397,8 @@
     // 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 = fSegment->dPtAtT(midT()) - fCurvePart[0];
-    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
+    SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
+    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
     double m0xm1 = m0.crossCheck(m1);
     if (s0xt0 > 0 && m0xm1 > 0) {
         return 0;
@@ -394,34 +432,30 @@
     return sqrt(longest) / dist;
 }
 
-bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
-    SkPath::Verb lVerb = fSegment->verb();
-    SkPath::Verb rVerb = rh.fSegment->verb();
+bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
+    SkPath::Verb lVerb = this->segment()->verb();
+    SkPath::Verb rVerb = rh->segment()->verb();
     int lPts = SkPathOpsVerbToPoints(lVerb);
     int rPts = SkPathOpsVerbToPoints(rVerb);
-    SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}},
-            {{fCurvePart[0], fCurvePart[lPts]}}};
+    SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
+            {{this->fCurvePart[0], this->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;
         }
-//      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;
+        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());
         double t = testAscends ? 0 : 1;
         for (int idx2 = 0; idx2 < i.used(); ++idx2) {
             double testT = i[0][idx2];
@@ -435,29 +469,6 @@
             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;
@@ -467,7 +478,7 @@
         if (smallTs[index] < 0) {
             continue;
         }
-        const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
+        const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
         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
@@ -498,7 +509,7 @@
         double minX, minY, maxX, maxY;
         minX = minY = SK_ScalarInfinity;
         maxX = maxY = -SK_ScalarInfinity;
-        const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart;
+        const SkDCubic& curve = index ? rh->fCurvePart : this->fCurvePart;
         int ptCount = index ? rPts : lPts;
         for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
             minX = SkTMin(minX, curve[idx2].fX);
@@ -508,7 +519,7 @@
         }
         double maxWidth = SkTMax(maxX - minX, maxY - minY);
         delta /= maxWidth;
-        if (delta > 1e-4 && (useIntersect ^= true)) {  // FIXME: move this magic number
+        if (delta > 1e-3 && (useIntersect ^= true)) {  // FIXME: move this magic number
             sRayLonger = rayLonger;
             sCept = cept;
             sCeptT = smallTs[index];
@@ -516,9 +527,9 @@
         }
     }
     if (useIntersect) {
-        const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart;
-        const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment;
-        double tStart = segment.t(sIndex ? rh.fStart : fStart);
+        const SkDCubic& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
+        const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
+        double tStart = sIndex ? rh->fStart->t() : fStart->t();
         SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
         double septDir = mid.crossCheck(sCept);
         if (!septDir) {
@@ -530,12 +541,65 @@
     }
 }
 
+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.
-const SkOpAngle* SkOpAngle::findFirst() const {
-    const SkOpAngle* best = this;
+SkOpAngle* SkOpAngle::findFirst() {
+    SkOpAngle* best = this;
     int bestStart = SkTMin(fSectorStart, fSectorEnd);
-    const SkOpAngle* angle = this;
+    SkOpAngle* angle = this;
     while ((angle = angle->fNext) != this) {
         int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
         if (angleEnd < bestStart) {
@@ -548,7 +612,7 @@
         }
     }
     // back up to the first possible angle
-    const SkOpAngle* firstBest = best;
+    SkOpAngle* firstBest = best;
     angle = best;
     int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd);
     while ((angle = angle->previous()) != firstBest) {
@@ -572,7 +636,7 @@
         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;
         }
@@ -639,6 +703,11 @@
     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) {
@@ -662,9 +731,6 @@
     }
     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;
@@ -678,9 +744,6 @@
     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;
@@ -689,48 +752,49 @@
         }
         last = next;
         next = next->fNext;
-        if (last == this && next->fUnorderable) {
-            fUnorderable = true;
+        if (last == this) {
+            if (next->fUnorderable) {
+                fUnorderable = true;
+            } else {
+                globalState()->setAngleCoincidence();
+                this->fNext = angle;
+                angle->fNext = next;
+                angle->fCheckCoincidence = true;
+            }
             return;
         }
-        SkASSERT(last != this);
     } while (true);
 }
 
-bool SkOpAngle::isHorizontal() const {
-    return !fIsCurve && fSweep[0].fY == 0;
-}
-
-SkOpSpan* SkOpAngle::lastMarked() const {
+SkOpSpanBase* SkOpAngle::lastMarked() const {
     if (fLastMarked) {
-        if (fLastMarked->fChased) {
+        if (fLastMarked->chased()) {
             return NULL;
         }
-        fLastMarked->fChased = true;
+        fLastMarked->setChased(true);
     }
     return fLastMarked;
 }
 
-bool SkOpAngle::loopContains(const SkOpAngle& test) const {
+bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
     if (!fNext) {
         return false;
     }
     const SkOpAngle* first = this;
     const SkOpAngle* loop = this;
-    const SkOpSegment* tSegment = test.fSegment;
-    double tStart = tSegment->span(test.fStart).fT;
-    double tEnd = tSegment->span(test.fEnd).fT;
+    const SkOpSegment* tSegment = angle->fStart->segment();
+    double tStart = angle->fStart->t();
+    double tEnd = angle->fEnd->t();
     do {
-        const SkOpSegment* lSegment = loop->fSegment;
-        // FIXME : use precisely_equal ? or compare points exactly ?
+        const SkOpSegment* lSegment = loop->fStart->segment();
         if (lSegment != tSegment) {
             continue;
         }
-        double lStart = lSegment->span(loop->fStart).fT;
+        double lStart = loop->fStart->t();
         if (lStart != tEnd) {
             continue;
         }
-        double lEnd = lSegment->span(loop->fEnd).fT;
+        double lEnd = loop->fEnd->t();
         if (lEnd == tStart) {
             return true;
         }
@@ -782,39 +846,65 @@
         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 (fSegment->t(fStart) + fSegment->t(fEnd)) / 2;
+    return (fStart->t() + fEnd->t()) / 2;
 }
 
-bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const {
-    int startSpan = abs(rh.fSectorStart - fSectorStart);
+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;
+}
+
+bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
+    int startSpan = abs(rh->fSectorStart - fSectorStart);
     return startSpan >= 8;
 }
 
-bool SkOpAngle::orderable(const SkOpAngle& rh) const {
+bool SkOpAngle::orderable(SkOpAngle* rh) {
     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) {
@@ -829,14 +919,14 @@
         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;
         }
     }
@@ -846,27 +936,10 @@
     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 {
@@ -880,26 +953,32 @@
     } while (true);
 }
 
-void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
-    fSegment = segment;
+SkOpSegment* SkOpAngle::segment() const {
+    return fStart->segment();
+}
+
+void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
     fStart = start;
     fComputedEnd = fEnd = end;
+    SkASSERT(start != end);
     fNext = NULL;
-    fComputeSector = fComputedSector = false;
+    fComputeSector = fComputedSector = fCheckCoincidence = false;
     fStop = false;
     setSpans();
     setSector();
+    PATH_OPS_DEBUG_CODE(fID = start->globalState()->nextAngleID());
 }
 
 void SkOpAngle::setCurveHullSweep() {
     fUnorderedSweep = false;
     fSweep[0] = fCurvePart[1] - fCurvePart[0];
-    if (SkPath::kLine_Verb == fSegment->verb()) {
+    const SkOpSegment* segment = fStart->segment();
+    if (SkPath::kLine_Verb == segment->verb()) {
         fSweep[1] = fSweep[0];
         return;
     }
     fSweep[1] = fCurvePart[2] - fCurvePart[0];
-    if (SkPath::kCubic_Verb != fSegment->verb()) {
+    if (SkPath::kCubic_Verb != segment->verb()) {
         if (!fSweep[0].fX && !fSweep[0].fY) {
             fSweep[0] = fSweep[1];
         }
@@ -933,64 +1012,16 @@
     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 = fSegment->isTiny(this);
+    fUnorderable = false;
     fLastMarked = NULL;
-    const SkPoint* pts = fSegment->pts();
+    const SkOpSegment* segment = fStart->segment();
+    const SkPoint* pts = segment->pts();
     SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
             = SK_ScalarNaN);
-    fSegment->subDivide(fStart, fEnd, &fCurvePart);
+    segment->subDivide(fStart, fEnd, &fCurvePart);
     setCurveHullSweep();
-    const SkPath::Verb verb = fSegment->verb();
+    const SkPath::Verb verb = segment->verb();
     if (verb != SkPath::kLine_Verb
             && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
         SkDLine lineHalf;
@@ -1002,9 +1033,9 @@
     switch (verb) {
     case SkPath::kLine_Verb: {
         SkASSERT(fStart != fEnd);
-        const SkPoint& cP1 = pts[fStart < fEnd];
+        const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
         SkDLine lineHalf;
-        lineHalf[0].set(fSegment->span(fStart).fPt);
+        lineHalf[0].set(fStart->pt());
         lineHalf[1].set(cP1);
         fTangentHalf.lineEndPoints(lineHalf);
         fSide = 0;
@@ -1023,8 +1054,8 @@
         double testTs[4];
         // OPTIMIZATION: keep inflections precomputed with cubic segment?
         int testCount = SkDCubic::FindInflections(pts, testTs);
-        double startT = fSegment->t(fStart);
-        double endT = fSegment->t(fEnd);
+        double startT = fStart->t();
+        double endT = fEnd->t();
         double limitT = endT;
         int index;
         for (index = 0; index < testCount; ++index) {
@@ -1064,19 +1095,63 @@
     }
 }
 
-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;
-        }
+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;
     }
-    return true;
+    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::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const {
+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 {
     if (s0xt0 == 0) {
         return false;
     }
@@ -1090,7 +1165,7 @@
     // 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;
@@ -1100,36 +1175,6 @@
     double sDist = sweep[0].length() * m;
     double tDist = tweep[0].length() * m;
     bool useS = fabs(sDist) < fabs(tDist);
-    double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist));
+    double mFactor = fabs(useS ? this->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();
-    }
-}
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 1dc4250..84b3701 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -7,17 +7,18 @@
 #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;
-struct SkOpSpan;
+class SkOpSpanBase;
+class SkOpSpan;
 
-// 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
+struct SkOpAngle {
     enum IncludeType {
         kUnaryWinding,
         kUnaryXor,
@@ -25,29 +26,66 @@
         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;
 
-    int end() const {
+    const SkOpAngle* debugAngle(int id) const;
+    SkOpContour* debugContour(int id);
+
+    int debugID() const {
+        return PATH_OPS_DEBUG_RELEASE(fID, -1);
+    }
+
+#if DEBUG_SORT
+    void debugLoop() const;
+#endif
+
+#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;
     }
 
-    const SkOpAngle* findFirst() const;
-
-    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* );
-    bool isHorizontal() const;
-    SkOpSpan* lastMarked() const;
-    bool loopContains(const SkOpAngle& ) const;
+    SkOpSpanBase* 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 {
@@ -58,120 +96,57 @@
         return fSectorStart;
     }
 
-    void set(const SkOpSegment* segment, int start, int end);
+    SkOpSegment* segment() const;
 
-    void setLastMarked(SkOpSpan* marked) {
+    void set(SkOpSpanBase* start, SkOpSpanBase* end);
+    void setCurveHullSweep();
+
+    void setID(int id) {
+        PATH_OPS_DEBUG_CODE(fID = id);
+    }
+
+    void setLastMarked(SkOpSpanBase* marked) {
         fLastMarked = marked;
     }
 
-    SkOpSegment* segment() const {
-        return const_cast<SkOpSegment*>(fSegment);
-    }
+    void setSector();
+    void setSpans();
+    int sign() const;
 
-    int sign() const {
-        return SkSign32(fStart - fEnd);
-    }
-
-    bool small() const;
-
-    int start() const {
+    SkOpSpanBase* start() const {
         return fStart;
     }
 
+    SkOpSpan* starter();
+    bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+
     bool unorderable() const {
         return fUnorderable;
     }
 
-    // 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
+    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;
-    SkOpSpan* fLastMarked;
+    SkOpSpanBase* fLastMarked;
     SkDVector fSweep[2];
-    int fStart;
-    int fEnd;
-    int fComputedEnd;
+    SkOpSpanBase* fStart;
+    SkOpSpanBase* fEnd;
+    SkOpSpanBase* 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
-    mutable bool fUnorderable;  // this is editable by orderable()
+    bool fStop;  // set if ordered angle is greater than the previous
+    bool fUnorderable;
     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
new file mode 100755
index 0000000..45eee0a
--- /dev/null
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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;
+}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index 287bfd1..b79b88b 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -19,6 +19,8 @@
     SkOpPtT* fOppPtTStart;
     SkOpPtT* fOppPtTEnd;
     bool fFlipped;
+
+    void dump() const;
 };
 
 class SkOpCoincidence {
@@ -28,13 +30,27 @@
     }
 
     void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
-             SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
-    void apply();
+             SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
+    bool addMissing(SkChunkAlloc* allocator);
+    bool 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;
 };
 
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 28c072a..d17b189 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -4,42 +4,35 @@
 * 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"
 
-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;
+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);
     }
-    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) {
+SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
     int segmentCount = fSortedSegments.count();
     SkASSERT(segmentCount > 0);
     for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@@ -47,627 +40,27 @@
         if (testSegment->done()) {
             continue;
         }
-        *start = *end = 0;
-        while (testSegment->nextCandidate(start, end)) {
-            if (!testSegment->isVertical(*start, *end)) {
+        SkOpSpanBase* span = testSegment->head();
+        SkOpSpanBase* testS, * testE;
+        while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
+            if (!testSegment->isVertical(testS, testE)) {
+                *start = testS;
+                *end = testE;
                 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 {
-    int segmentCount = fSegments.count();
-    const SkPoint& pt = fSegments.front().pts()[0];
+    const SkPoint& pt = fHead.pts()[0];
     path->deferredMove(pt);
-    for (int test = 0; test < segmentCount; ++test) {
-        fSegments[test].addCurveTo(0, 1, path, true);
-    }
+    const SkOpSegment* segment = &fHead;
+    do {
+        segment->addCurveTo(segment->head(), segment->tail(), path, true);
+    } while ((segment = segment->next()));
     path->close();
 }
 
@@ -706,57 +99,14 @@
     }
 }
 
-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()) {
+SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
+    SkOpSegment* segment = &fHead;
+    do {
+        if (segment->done()) {
             continue;
         }
-        testSegment->undoneSpan(start, end);
-        return testSegment;
-    }
+        segment->undoneSpan(startPtr, endPtr);
+        return segment;
+    } while ((segment = segment->next()));
     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());
-    }
-}
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index 7a1cc09..be1f59f 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -8,31 +8,16 @@
 #define SkOpContour_DEFINED
 
 #include "SkOpSegment.h"
-#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "SkTSort.h"
 
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-class SkIntersections;
-class SkOpContour;
+class SkChunkAlloc;
 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 {
@@ -41,211 +26,255 @@
                 : fBounds.fTop < rh.fBounds.fTop;
     }
 
-    bool addCoincident(int index, SkOpContour* other, int otherIndex,
-                       const SkIntersections& ts, bool swap);
-    void addCoincidentPoints();
+    void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
+        appendSegment(allocator).addCubic(pts, this);
+    }
 
-    void addCross(const SkOpContour* crosser) {
-#ifdef DEBUG_CROSS
-        for (int index = 0; index < fCrosses.count(); ++index) {
-            SkASSERT(fCrosses[index] != crosser);
+    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);
         }
-#endif
-        fCrosses.push_back(crosser);
+        fTail = result;
+        return *result;
     }
 
-    void addCubic(const SkPoint pts[4]) {
-        fSegments.push_back().addCubic(pts, fOperand, fXor);
-        fContainsCurves = fContainsCubics = true;
-    }
-
-    int addLine(const SkPoint pts[2]) {
-        fSegments.push_back().addLine(pts, fOperand, fXor);
-        return fSegments.count();
-    }
-
-    void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
-        fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
-    }
-
-    bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
-                       const SkIntersections& ts, int ptIndex, bool swap);
-
-    int addQuad(const SkPoint pts[3]) {
-        fSegments.push_back().addQuad(pts, fOperand, fXor);
-        fContainsCurves = true;
-        return fSegments.count();
-    }
-
-    int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
-        setContainsIntercepts();
-        return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
-    }
-
-    int addSelfT(int segIndex, const SkPoint& pt, double newT) {
-        setContainsIntercepts();
-        return fSegments[segIndex].addSelfT(pt, newT);
-    }
-
-    void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
-    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
-            SkTArray<SkCoincidence, true>* coincidences);
-
-    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
-        alignCoincidence(aligned, &fCoincidences);
-        alignCoincidence(aligned, &fPartialCoincidences);
-    }
-
-    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);
-            }
+    SkOpContour* appendContour(SkChunkAlloc* allocator) {
+        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+        
+        SkOpContour* prev = this;
+        SkOpContour* next;
+        while ((next = prev->next())) {
+            prev = next;
         }
+        prev->setNext(contour);
+        return contour;
     }
-
-    void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
-                  bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
-
+    
     const SkPathOpsBounds& bounds() const {
         return fBounds;
     }
 
-    bool calcAngles();
-    bool calcCoincidentWinding();
-    void calcPartialCoincidentWinding();
-
-    void checkDuplicates() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            if (segment.count() > 2) {
-                segment.checkDuplicates();
-            }
-        }
-    }
-
-    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;
-    }
-
-    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();
-            }
-        }
-    }
-
-    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();
-            }
-        }
-    }
-
-    // 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 calcAngles(SkChunkAlloc* allocator) {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->calcAngles(allocator);
+        } while ((segment = segment->next()));
     }
 
     void complete() {
         setBounds();
-        fContainsIntercepts = false;
     }
 
-    bool containsCubics() const {
-        return fContainsCubics;
+    int count() const {
+        return fCount;
     }
 
-    bool crosses(const SkOpContour* crosser) const {
-        for (int index = 0; index < fCrosses.count(); ++index) {
-            if (fCrosses[index] == crosser) {
-                return true;
-            }
-        }
-        return false;
+    int debugID() const {
+        return PATH_OPS_DEBUG_RELEASE(fID, -1);
+    }
+
+    int debugIndent() const {
+        return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
+    }
+
+#if DEBUG_ACTIVE_SPANS
+    void debugShowActiveSpans() {
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->debugShowActiveSpans();
+        } while ((segment = segment->next()));
+    }
+#endif
+
+    const SkOpAngle* debugAngle(int id) const {
+        return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
+    }
+
+    SkOpContour* debugContour(int id) {
+        return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
+    }
+
+    const SkOpPtT* debugPtT(int id) const {
+        return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
+    }
+
+    const SkOpSegment* debugSegment(int id) const {
+        return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
+    }
+
+    const SkOpSpanBase* debugSpan(int id) const {
+        return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
+    }
+
+    SkOpGlobalState* globalState() const {
+        return fState; 
+    }
+
+    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 done() const {
         return fDone;
     }
 
+    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 {
-        const SkOpSegment& segment = fSegments.back();
-        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+        return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
     }
 
-    void fixOtherTIndex() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            fSegments[sIndex].fixOtherTIndex();
-        }
+    SkOpSegment* first() {
+        SkASSERT(fCount > 0);
+        return &fHead;
     }
 
-    bool hasMultiples() const {
-        return fMultiples;
+    const SkOpSegment* first() const {
+        SkASSERT(fCount > 0);
+        return &fHead;
     }
 
-    void joinCoincidence() {
-        joinCoincidence(fCoincidences, false);
-        joinCoincidence(fPartialCoincidences, true);
+    void indentDump() {
+        PATH_OPS_DEBUG_CODE(fIndent += 2);
     }
 
-    SkOpSegment* nonVerticalSegment(int* start, int* end);
+    void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
+        fState = globalState;
+        fOperand = operand;
+        fXor = isXor;
+    }
+
+    bool isXor() const {
+        return fXor;
+    }
+
+    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);
+            }
+        } while ((segment = segment->next()));
+    }
+
+    bool moveNearby() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            if (!segment->moveNearby()) {
+                return false;
+            }
+        } while ((segment = segment->next()));
+        return true;
+    }
+
+    SkOpContour* next() {
+        return fNext;
+    }
+
+    const SkOpContour* next() const {
+        return fNext;
+    }
+
+    SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
 
     bool operand() const {
         return fOperand;
     }
 
+    bool oppXor() const {
+        return fOppXor;
+    }
+
+    void outdentDump() {
+        PATH_OPS_DEBUG_CODE(fIndent -= 2);
+    }
+
+    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);
+    }
+
     void reset() {
-        fSegments.reset();
-        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
-        fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
+        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 resolveNearCoincidence();
-
-    SkTArray<SkOpSegment>& segments() {
-        return fSegments;
+    void setBounds() {
+        SkASSERT(fCount > 0);
+        const SkOpSegment* segment = &fHead;
+        fBounds = segment->bounds();
+        while ((segment = segment->next())) {
+            fBounds.add(segment->bounds());
+        }
     }
 
-    void setContainsIntercepts() {
-        fContainsIntercepts = true;
+    void setGlobalState(SkOpGlobalState* state) {
+        fState = state;
+    }
+
+    void setNext(SkOpContour* contour) {
+        SkASSERT(!fNext == !!contour);
+        fNext = contour;
     }
 
     void setOperand(bool isOp) {
@@ -254,107 +283,68 @@
 
     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;
     }
 
-    void sortAngles();
-    void sortSegments();
+    SkPath::Verb simplifyCubic(SkPoint pts[4]);
 
-    const SkPoint& start() const {
-        return fSegments.front().pts()[0];
+    void sortAngles() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->sortAngles();
+        } while ((segment = segment->next()));
     }
 
-    void toPath(SkPathWriter* path) const;
+    void sortSegments() {
+        SkOpSegment* segment = &fHead;
+        do {
+            *fSortedSegments.append() = segment;
+        } while ((segment = segment->next()));
+        SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+        fFirstSorted = 0;
+    }
+
+    const SkPoint& start() const {
+        return fHead.pts()[0];
+    }
 
     void toPartialBackward(SkPathWriter* path) const {
-        int segmentCount = fSegments.count();
-        for (int test = segmentCount - 1; test >= 0; --test) {
-            fSegments[test].addCurveTo(1, 0, path, true);
-        }
+        const SkOpSegment* segment = fTail;
+        do {
+            segment->addCurveTo(segment->tail(), segment->head(), path, true);
+        } while ((segment = segment->prev()));
     }
 
     void toPartialForward(SkPathWriter* path) const {
-        int segmentCount = fSegments.count();
-        for (int test = 0; test < segmentCount; ++test) {
-            fSegments[test].addCurveTo(0, 1, path, true);
-        }
+        const SkOpSegment* segment = &fHead;
+        do {
+            segment->addCurveTo(segment->head(), segment->tail(), path, true);
+        } while ((segment = segment->next()));
     }
 
+    void toPath(SkPathWriter* path) const;
     void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
-    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;
+    SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
 
 private:
-    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;
-    SkTArray<SkCoincidence, true> fCoincidences;
-    SkTArray<SkCoincidence, true> fPartialCoincidences;
-    SkTArray<const SkOpContour*, true> fCrosses;
+    SkOpGlobalState* fState;
+    SkOpSegment fHead;
+    SkOpSegment* fTail;
+    SkOpContour* fNext;
+    SkTDArray<SkOpSegment*> fSortedSegments;  // set by find top segment
     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
+    int fCount;
+    int fFirstSorted;
+    bool fDone;  // set by find top segment
     bool fOperand;  // true for the second argument to a binary operator
-    bool fXor;
-    bool fOppXor;
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-    int debugID() const { return fID; }
-    int fID;
-#else
-    int debugID() const { return -1; }
-#endif
+    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);
 };
 
 #endif
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 803a5f4..bd21d72 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -9,7 +9,7 @@
 #include "SkReduceOrder.h"
 
 void SkOpEdgeBuilder::init() {
-    fCurrentContour = NULL;
+    fCurrentContour = fContoursHead;
     fOperand = false;
     fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
@@ -19,32 +19,43 @@
 
 void SkOpEdgeBuilder::addOperand(const SkPath& path) {
     SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
-    fPathVerbs.pop_back();
+    fPathVerbs.pop();
     fPath = &path;
     fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
     preFetch();
 }
 
-bool SkOpEdgeBuilder::finish() {
-    if (fUnparseable || !walk()) {
+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)) {
         return false;
     }
     complete();
-    if (fCurrentContour && !fCurrentContour->segments().count()) {
-        fContours.pop_back();
+    if (fCurrentContour && !fCurrentContour->count()) {
+        fContoursHead->remove(fCurrentContour);
     }
     return true;
 }
 
 void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
     if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
-        fPathVerbs.push_back(SkPath::kLine_Verb);
-        fPathPts.push_back_n(1, &curveStart);
+        *fPathVerbs.append() = SkPath::kLine_Verb;
+        *fPathPts.append() = curveStart;
     } else {
         fPathPts[fPathPts.count() - 1] = curveStart;
     }
-    fPathVerbs.push_back(SkPath::kClose_Verb);
+    *fPathVerbs.append() = SkPath::kClose_Verb;
 }
 
 // very tiny points cause numerical instability : don't allow them
@@ -57,7 +68,6 @@
     }
 }
 
-
 int SkOpEdgeBuilder::preFetch() {
     if (!fPath->isFinite()) {
         fUnparseable = true;
@@ -78,18 +88,18 @@
                 if (!fAllowOpenContours && lastCurve) {
                     closeContour(curve[0], curveStart);
                 }
-                fPathVerbs.push_back(verb);
+                *fPathVerbs.append() = verb;
                 force_small_to_zero(&pts[0]);
-                fPathPts.push_back(pts[0]);
+                *fPathPts.append() = 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.back();
+                    uint8_t lastVerb = fPathVerbs.top();
                     if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
-                        fPathPts.back() = pts[1];
+                        fPathPts.top() = pts[1];
                     }
                     continue;  // skip degenerate points
                 }
@@ -109,9 +119,9 @@
                             quadderTol);
                     const int nQuads = quadder.countQuads();
                     for (int i = 0; i < nQuads; ++i) {
-                       fPathVerbs.push_back(SkPath::kQuad_Verb);
+                       *fPathVerbs.append() = SkPath::kQuad_Verb;
                     }
-                    fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
+                    fPathPts.append(nQuads * 2, &quadPts[1]);
                     curve[0] = pts[2];
                     lastCurve = true;
                 }
@@ -135,16 +145,16 @@
             case SkPath::kDone_Verb:
                 continue;
         }
-        fPathVerbs.push_back(verb);
+        *fPathVerbs.append() = verb;
         int ptCount = SkPathOpsVerbToPoints(verb);
-        fPathPts.push_back_n(ptCount, &pts[1]);
+        fPathPts.append(ptCount, &pts[1]);
         curve[0] = pts[ptCount];
         lastCurve = true;
     } while (verb != SkPath::kDone_Verb);
     if (!fAllowOpenContours && lastCurve) {
         closeContour(curve[0], curveStart);
     }
-    fPathVerbs.push_back(SkPath::kDone_Verb);
+    *fPathVerbs.append() = SkPath::kDone_Verb;
     return fPathVerbs.count() - 1;
 }
 
@@ -153,10 +163,10 @@
     return true;
 }
 
-bool SkOpEdgeBuilder::walk() {
+bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
     uint8_t* verbPtr = fPathVerbs.begin();
     uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
-    const SkPoint* pointsPtr = fPathPts.begin() - 1;
+    SkPoint* pointsPtr = fPathPts.begin() - 1;
     SkPath::Verb verb;
     while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
         if (verbPtr == endOfFirstHalf) {
@@ -165,7 +175,7 @@
         verbPtr++;
         switch (verb) {
             case SkPath::kMove_Verb:
-                if (fCurrentContour) {
+                if (fCurrentContour && fCurrentContour->count()) {
                     if (fAllowOpenContours) {
                         complete();
                     } else if (!close()) {
@@ -173,21 +183,44 @@
                     }
                 }
                 if (!fCurrentContour) {
-                    fCurrentContour = fContours.push_back_n(1);
-                    fCurrentContour->setOperand(fOperand);
-                    fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
+                    fCurrentContour = fContoursHead->appendContour(allocator);
                 }
+                fCurrentContour->init(fGlobalState, fOperand,
+                    fXorMask[fOperand] == kEvenOdd_PathOpsMask);
                 pointsPtr += 1;
                 continue;
             case SkPath::kLine_Verb:
-                fCurrentContour->addLine(pointsPtr);
+                fCurrentContour->addLine(pointsPtr, fAllocator);
                 break;
             case SkPath::kQuad_Verb:
-                fCurrentContour->addQuad(pointsPtr);
+                fCurrentContour->addQuad(pointsPtr, fAllocator);
                 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()) {
@@ -198,10 +231,11 @@
                 SkDEBUGFAIL("bad verb");
                 return false;
         }
-        pointsPtr += SkPathOpsVerbToPoints(verb);
         SkASSERT(fCurrentContour);
+        fCurrentContour->debugValidate();
+        pointsPtr += SkPathOpsVerbToPoints(verb);
     }
-   if (fCurrentContour && !fAllowOpenContours && !close()) {
+   if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
        return false;
    }
    return true;
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index fd07445..3ecc915 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -9,20 +9,25 @@
 
 #include "SkOpContour.h"
 #include "SkPathWriter.h"
-#include "SkTArray.h"
 
 class SkOpEdgeBuilder {
 public:
-    SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
-        : fPath(path.nativePath())
-        , fContours(contours)
+    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)
         , fAllowOpenContours(true) {
         init();
     }
 
-    SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
-        : fPath(&path)
-        , fContours(contours)
+    SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
+            SkOpGlobalState* globalState)
+        : fAllocator(allocator)
+        , fGlobalState(globalState)
+        , fPath(&path)
+        , fContoursHead(contours2)
         , fAllowOpenContours(false) {
         init();
     }
@@ -30,13 +35,19 @@
     void addOperand(const SkPath& path);
 
     void complete() {
-        if (fCurrentContour && fCurrentContour->segments().count()) {
+        if (fCurrentContour && fCurrentContour->count()) {
             fCurrentContour->complete();
             fCurrentContour = NULL;
         }
     }
 
-    bool finish();
+    int count() const;
+    bool finish(SkChunkAlloc* );
+
+    const SkOpContour* head() const {
+        return fContoursHead;
+    }
+
     void init();
     bool unparseable() const { return fUnparseable; }
     SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@@ -45,13 +56,15 @@
     void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
     bool close();
     int preFetch();
-    bool walk();
+    bool walk(SkChunkAlloc* );
 
+    SkChunkAlloc* fAllocator;
+    SkOpGlobalState* fGlobalState;
     const SkPath* fPath;
-    SkTArray<SkPoint, true> fPathPts;
-    SkTArray<uint8_t, true> fPathVerbs;
+    SkTDArray<SkPoint> fPathPts;
+    SkTDArray<uint8_t> fPathVerbs;
     SkOpContour* fCurrentContour;
-    SkTArray<SkOpContour>& fContours;
+    SkOpContour* fContoursHead;
     SkPathOpsMask fXorMask[2];
     int fSecondHalf;
     bool fOperand;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 1fb5afa..902f273 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,11 +4,20 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpContour.h"
 #include "SkOpSegment.h"
 #include "SkPathWriter.h"
-#include "SkTSort.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.
+ */
 
 #define F (false)      // discard the edge
 #define T (true)       // keep the edge
@@ -33,147 +42,125 @@
 #undef F
 #undef T
 
-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)) {
+SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr, bool* done, bool* sortable) {
+    if (SkOpAngle* result = activeAngleInner(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;
-        }
+    if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, 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;
 }
 
-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;
+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;
             }
-            if (!upSpan.fDone) {
-                if (upSpan.fWindSum != SK_MinS32) {
-                    return spanToAngle(index, next);
+            if (!upSpan->done()) {
+                if (upSpan->windSum() != SK_MinS32) {
+                    return spanToAngle(start, next);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(upSpan.fDone);
+            SkASSERT(upSpan->done());
         }
     }
-    int prev = nextExactSpan(index, -1);
+    SkOpSpan* downSpan = start->prev();
     // edge leading into junction
-    if (prev >= 0) {
-        const SkOpSpan& downSpan = fTs[prev];
-        if (downSpan.fWindValue || downSpan.fOppValue) {
-            if (*end < 0) {
-                *start = index;
-                *end = prev;
+    if (downSpan) {
+        if (downSpan->windValue() || downSpan->oppValue()) {
+            if (!*endPtr) {
+                *startPtr = start;
+                *endPtr = downSpan;
             }
-            if (!downSpan.fDone) {
-                if (downSpan.fWindSum != SK_MinS32) {
-                    return spanToAngle(index, prev);
+            if (!downSpan->done()) {
+                if (downSpan->windSum() != SK_MinS32) {
+                    return spanToAngle(start, downSpan);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(downSpan.fDone);
+            SkASSERT(downSpan->done());
         }
     }
     return NULL;
 }
 
-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);
+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);
 }
 
-SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
+SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
     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;
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fDone && lastDone) {
-            goto next;
-        }
-        if (approximately_negative(span.fT - lastT)) {
+    SkOpSpanBase* span = &fHead;
+    do {
+        if (lastDone && (span->final() || span->upCast()->done())) {
             goto next;
         }
         {
-            const SkPoint& xy = xyAtT(&span);
+            const SkPoint& xy = span->pt();
             if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
                 topPt = xy;
-                if (firstT) {
-                    *firstT = index;
+                if (firstSpan) {
+                    *firstSpan = span;
                 }
             }
             if (fVerb != SkPath::kLine_Verb && !lastDone) {
-                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
+                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
+                        span->t());
                 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
                         && topPt.fX > curveTop.fX)) {
                     topPt = curveTop;
-                    if (firstT) {
-                        *firstT = index;
+                    if (firstSpan) {
+                        *firstSpan = span;
                     }
                 }
             }
-            lastT = span.fT;
+            lastT = span->t();
         }
 next:
-        lastDone = span.fDone;
-    }
+        if (span->final()) {
+            break;
+        }
+        lastDone = span->upCast()->done();
+    } while ((span = span->upCast()->next()));
     return topPt;
 }
 
-bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
-    int sumMiWinding = updateWinding(endIndex, index);
-    int sumSuWinding = updateOppWinding(endIndex, index);
+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);
 #if DEBUG_LIMIT_WIND_SUM
     SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
     SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
 #endif
-    if (fOperand) {
+    if (this->operand()) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
-    return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
+    return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
 }
 
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
-        int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
+        SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
     int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
-    setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
+    this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
             &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
     bool miFrom;
     bool miTo;
@@ -193,178 +180,31 @@
     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(), span(index).fT, span(endIndex).fT,
+            __FUNCTION__, debugID(), start->t(), end->t(),
             SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
 #endif
     return result;
 }
 
-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 = updateWinding(end, start);
+    return activeWinding(start, end, &sumWinding);
 }
 
-bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
     int maxWinding;
-    setUpWinding(index, endIndex, &maxWinding, sumWinding);
+    setUpWinding(start, end, &maxWinding, sumWinding);
     bool from = maxWinding != 0;
     bool to = *sumWinding  != 0;
     bool result = gUnaryActiveEdge[from][to];
     return result;
 }
 
-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 {
+void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkPathWriter* path, bool active) const {
     SkPoint edge[4];
     const SkPoint* ePtr;
-    int lastT = fTs.count() - 1;
-    if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
+    if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
         ePtr = fPts;
     } else {
     // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -372,7 +212,7 @@
         ePtr = edge;
     }
     if (active) {
-        bool reverse = ePtr == fPts && start != 0;
+        bool reverse = ePtr == fPts && start != &fHead;
         if (reverse) {
             path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
             switch (fVerb) {
@@ -388,7 +228,6 @@
                 default:
                     SkASSERT(0);
             }
-   //         return ePtr[0];
        } else {
             path->deferredMoveLine(ePtr[0]);
             switch (fVerb) {
@@ -406,1538 +245,285 @@
             }
         }
     }
-  //  return ePtr[SkPathOpsVerbToPoints(fVerb)];
 }
 
-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();
+SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
+    SkOpSpanBase* existing = NULL;
+    SkOpSpanBase* test = &fHead;
+    double testT;
     do {
-        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;
-    }
-    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) {
-    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) {
+        if ((testT = test->ptT()->fT) >= t) {
+            if (testT == t) {
+                existing = test;
+            }
             break;
         }
-        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;
+    } while ((test = test->upCast()->next()));
+    SkOpPtT* result;
+    if (existing && existing->contains(opp)) {
+        result = existing->ptT();
+    } else {
+        result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+    }
+    SkASSERT(result);
+    return result;
 }
 
-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) {
+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();
             break;
         }
-        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);
+        oEndSpan = ptT->span();
+        oStartSpan = oEndSpan->prev();
+        if (oStartSpan && oStartSpan->windValue()) {
+            break;
+        }
+    }
+    SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+    oAngle->set(oStartSpan, oEndSpan);
+    oStartSpan->setToAngle(oAngle);
     *otherPtr = other;
-    return &oAngle;
+    return oAngle;
 }
 
-SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
+SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
     SkOpSegment* other;
     SkOpAngle* angle, * otherAngle;
     if (step > 0) {
-        otherAngle = addSingletonAngleUp(&other, &angle);
+        otherAngle = addSingletonAngleUp(&other, &angle, allocator);
     } else {
-        otherAngle = addSingletonAngleDown(&other, &angle);
+        otherAngle = addSingletonAngleDown(&other, &angle, allocator);
     }
     angle->insert(otherAngle);
     return angle;
 }
 
-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);
-}
-
-void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
-    int index = 0;
-    do {
-        fTs[index].fToAngle = angle;
-    } while (++index < endIndex);
-}
-
-    // 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;
+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;
         }
-        if (newT == span.fT) {
-            if (pt == span.fPt) {
-                insertedAt = index;
-                break;
-            }
-            if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
-                insertedAt = index;
-                break;
-            }
-        }
-    }
-    SkOpSpan* span;
-    if (insertedAt >= 0) {
-        span = fTs.insert(insertedAt);
-    } else {
-        insertedAt = tCount;
-        span = fTs.append();
-    }
-    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;
-    }
-    setSpanFlags(pt, newT, span);
-    return insertedAt;
-}
-
-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;
-            }
-        }
-        --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;
-            }
-        }
-        ++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 (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::resetSpanFlags() {
-    fSmall = fTiny = false;
-    fDoneSpans = 0;
-    int start = 0;
-    int last = this->count() - 1;
-    do {
-        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 (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;
-        }
-        if (!startSpan->fWindValue) {
-            --fDoneSpans;  // added back below
-        }
-        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);
-}
-
-// 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 {
-                    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;
-            }
-        }
-        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);
-    }
-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 (!other->done() && oOutsidePts.count()) {
-        other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
-    }
-    setCoincidentRange(startPt, endPt, other);
-    other->setCoincidentRange(startPt, endPt, this);
-}
-
-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* 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) {
+        oStartSpan = oEndSpan->upCastable();
+        if (oStartSpan && oStartSpan->windValue()) {
+            oEndSpan = oStartSpan->next();
             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 {
-        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;
     }
-    angle->insert(oAngle);
+    SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+    oAngle->set(oEndSpan, oStartSpan);
+    oEndSpan->setFromAngle(oAngle);
+    *otherPtr = other;
+    return oAngle;
 }
 
-void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
     debugValidate();
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        SkOpSpan& span = fTs[index];
-        if (!span.fMultiple) {
-            continue;
+    SkPoint pt = this->ptAtT(t);
+    SkOpSpanBase* span = &fHead;
+    do {
+        SkOpPtT* result = span->ptT();
+        if (t == result->fT) {
+            return result;
         }
-        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;
-                    }
+        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;
                 }
-                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:
-                ;
+                duplicatePt |= ptMatch;
+                loop = loop->next();
             }
-            ++index;
+            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;
+}
+
+// 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) {
+            break;
+        }
+        span->align();
+    }
+    if (!span->aligned()) {
+        span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
     }
     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: ;
+bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
+        const SkOpSpanBase* greater) {
+    if (lesser->t() > greater->t()) {
+        SkTSwap<const SkOpSpanBase*>(lesser, greater);
     }
+    return approximately_between(lesser->t(), testT, greater->t());
 }
 
-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;
+void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+    bool activePrior = !fHead.isCanceled();
+    if (activePrior && !fHead.simple()) {
+        addStartSpan(allocator);
     }
-    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;
-            }
+    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);
         }
-        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);
+        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);
         }
-    }
-    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) {
-        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 {
-        addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
-    }
-    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;
+        prior = span;
+        spanBase = next;
     }
-    if (addEnd && activePrior >= 0) {
-        addEndSpan(endIndex);
+    if (activePrior && !fTail.simple()) {
+        addEndSpan(allocator);
     }
-    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(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
-    SkASSERT(includeType != SkOpAngle::kUnaryXor);
-    SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
-    if (NULL == firstAngle || NULL == firstAngle->next()) {
-        return SK_NaN32;
-    }
-    // if all angles have a computed winding,
-    //  or if no adjacent angles are orderable,
-    //  or if adjacent orderable angles have no computed winding,
-    //  there's nothing to do
-    // if two orderable angles are adjacent, and both are next to orderable angles,
-    //  and one has winding computed, transfer to the other
-    SkOpAngle* baseAngle = NULL;
-    bool tryReverse = false;
-    // look for counterclockwise transfers
-    SkOpAngle* angle = firstAngle->previous();
-    SkOpAngle* next = angle->next();
-    firstAngle = next;
+void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+    SkOpSpanBase* base = &fHead;
+    SkOpSpan* span;
     do {
-        SkOpAngle* prior = angle;
-        angle = next;
-        next = angle->next();
-        SkASSERT(prior->next() == angle);
-        SkASSERT(angle->next() == next);
-        if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
-            baseAngle = NULL;
-            continue;
+        SkOpAngle* angle = base->fromAngle();
+        if (angle && angle->fCheckCoincidence) {
+            angle->checkNearCoincidence();
         }
-        int testWinding = angle->segment()->windSum(angle);
-        if (SK_MinS32 != testWinding) {
-            baseAngle = angle;
-            tryReverse = true;
-            continue;
+        if (base->final()) {
+             break;
         }
-        if (baseAngle) {
-            ComputeOneSum(baseAngle, angle, includeType);
-            baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+        span = base->upCast();
+        angle = span->toAngle();
+        if (angle && angle->fCheckCoincidence) {
+            angle->checkNearCoincidence();
         }
-    } while (next != firstAngle);
-    if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
-        firstAngle = baseAngle;
-        tryReverse = true;
+    } while ((base = span->next()));
+}
+
+// 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;
+                    }
+                } else {
+                    if (inflectionT != startT) {
+                        endT = inflectionT;
+                    }
+                }
+            }
+        }
+        SkDCubic part = cubic.subDivide(startT, endT);
+        for (int index = 0; index < 4; ++index) {
+            edge[index] = part[index].asSkPoint();
+        }
+    } else {
+    subDivide(start, end, edge);
     }
-    if (tryReverse) {
-        baseAngle = NULL;
-        SkOpAngle* prior = firstAngle;
-        do {
-            angle = prior;
-            prior = angle->previous();
-            SkASSERT(prior->next() == angle);
-            next = angle->next();
-            if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
-                baseAngle = NULL;
-                continue;
-            }
-            int testWinding = angle->segment()->windSum(angle);
-            if (SK_MinS32 != testWinding) {
-                baseAngle = angle;
-                continue;
-            }
-            if (baseAngle) {
-                ComputeOneSumReverse(baseAngle, angle, includeType);
-                baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
-            }
-        } while (prior != firstAngle);
+    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);
+        }
     }
-    int minIndex = SkMin32(startIndex, endIndex);
-    return windSum(minIndex);
+    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();
+    }
+    return sum <= 0;
 }
 
 void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
@@ -1954,7 +540,7 @@
     }
     SkOpSegment* nextSegment = nextAngle->segment();
     int maxWinding, sumWinding;
-    SkOpSpan* last;
+    SkOpSpanBase* last;
     if (binary) {
         int oppMaxWinding, oppSumWinding;
         nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
@@ -1983,7 +569,7 @@
     }
     SkOpSegment* nextSegment = nextAngle->segment();
     int maxWinding, sumWinding;
-    SkOpSpan* last;
+    SkOpSpanBase* last;
     if (binary) {
         int oppMaxWinding, oppSumWinding;
         nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
@@ -1998,64 +584,98 @@
     nextAngle->setLastMarked(last);
 }
 
-bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
-    int step = index < endIndex ? 1 : -1;
+// at this point, the span is already ordered, or unorderable
+int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
+        SkOpAngle::IncludeType includeType) {
+    SkASSERT(includeType != SkOpAngle::kUnaryXor);
+    SkOpAngle* firstAngle = this->spanToAngle(end, start);
+    if (NULL == firstAngle || NULL == firstAngle->next()) {
+        return SK_NaN32;
+    }
+    // if all angles have a computed winding,
+    //  or if no adjacent angles are orderable,
+    //  or if adjacent orderable angles have no computed winding,
+    //  there's nothing to do
+    // if two orderable angles are adjacent, and both are next to orderable angles,
+    //  and one has winding computed, transfer to the other
+    SkOpAngle* baseAngle = NULL;
+    bool tryReverse = false;
+    // look for counterclockwise transfers
+    SkOpAngle* angle = firstAngle->previous();
+    SkOpAngle* next = angle->next();
+    firstAngle = next;
     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;
+        SkOpAngle* prior = angle;
+        angle = next;
+        next = angle->next();
+        SkASSERT(prior->next() == angle);
+        SkASSERT(angle->next() == next);
+        if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+            baseAngle = NULL;
+            continue;
         }
-        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;
+        int testWinding = angle->starter()->windSum();
+        if (SK_MinS32 != testWinding) {
+            baseAngle = angle;
+            tryReverse = true;
+            continue;
         }
-        if (t == span.fT) {
-            if (other != span.fOther) {
+        if (baseAngle) {
+            ComputeOneSum(baseAngle, angle, includeType);
+            baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+        }
+    } while (next != firstAngle);
+    if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
+        firstAngle = baseAngle;
+        tryReverse = true;
+    }
+    if (tryReverse) {
+        baseAngle = NULL;
+        SkOpAngle* prior = firstAngle;
+        do {
+            angle = prior;
+            prior = angle->previous();
+            SkASSERT(prior->next() == angle);
+            next = angle->next();
+            if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+                baseAngle = NULL;
                 continue;
             }
-            if (other->fVerb != SkPath::kCubic_Verb) {
-                return true;
+            int testWinding = angle->starter()->windSum();
+            if (SK_MinS32 != testWinding) {
+                baseAngle = angle;
+                continue;
             }
-            if (!other->fLoop) {
-                return true;
+            if (baseAngle) {
+                ComputeOneSumReverse(baseAngle, angle, includeType);
+                baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
             }
-            double otherMidT = (otherT + span.fOtherT) / 2;
-            SkPoint otherPt = other->ptAtT(otherMidT);
-            return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
-        }
+        } while (prior != firstAngle);
     }
-    return false;
+    return start->starter(end)->windSum();
 }
 
-int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
-                              bool* hitSomething, double mid, bool opp, bool current) const {
+SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
+        SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
     SkScalar bottom = fBounds.fBottom;
-    int bestTIndex = -1;
+    *vertical = false;
     if (bottom <= *bestY) {
-        return bestTIndex;
+        return NULL;
     }
     SkScalar top = fBounds.fTop;
     if (top >= basePt.fY) {
-        return bestTIndex;
+        return NULL;
     }
     if (fBounds.fLeft > basePt.fX) {
-        return bestTIndex;
+        return NULL;
     }
     if (fBounds.fRight < basePt.fX) {
-        return bestTIndex;
+        return NULL;
     }
     if (fBounds.fLeft == fBounds.fRight) {
         // if vertical, and directly above test point, wait for another one
-        return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
+        *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
+        return NULL;
     }
     // intersect ray starting at basePt with edge
     SkIntersections intersections;
@@ -2065,7 +685,7 @@
     int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
             (fPts, top, bottom, basePt.fX, false);
     if (pts == 0 || (current && pts == 1)) {
-        return bestTIndex;
+        return NULL;
     }
     if (current) {
         SkASSERT(pts > 1);
@@ -2093,933 +713,73 @@
             continue;
         }
         if (pts > 1 && fVerb == SkPath::kLine_Verb) {
-            return SK_MinS32;  // if the intersection is edge on, wait for another one
+            *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)) {
-                return SK_MinS32;  // hit vertical, wait for another one
+                *vertical = true;
+                return NULL;  // hit vertical, wait for another one
             }
         }
         *bestY = testY;
         bestT = foundT;
     }
     if (bestT < 0) {
-        return bestTIndex;
+        return NULL;
     }
     SkASSERT(bestT >= 0);
-    SkASSERT(bestT <= 1);
-    int start;
-    int end = 0;
+    SkASSERT(bestT < 1);
+    SkOpSpanBase* testTSpanBase = &this->fHead;
+    SkOpSpanBase* nextTSpan;
+    double endT = 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)) {
+        nextTSpan = testTSpanBase->upCast()->next();
+        endT = nextTSpan->t();
+        if (endT >= bestT) {
+            break;
+        }
+        testTSpanBase = nextTSpan;
+    } while (testTSpanBase);
+    SkOpSpan* bestTSpan = NULL;
+    SkOpSpan* testTSpan = testTSpanBase->upCast();
+    if (!testTSpan->isCanceled()) {
         *hitT = bestT;
-        bestTIndex = start;
+        bestTSpan = testTSpan;
         *hitSomething = true;
     }
-    return bestTIndex;
+    return bestTSpan;
 }
 
-bool SkOpSegment::decrementSpan(SkOpSpan* span) {
-    SkASSERT(span->fWindValue > 0);
-    if (--(span->fWindValue) == 0) {
-        if (!span->fOppValue && !span->fDone) {
-            span->fDone = true;
-            ++fDoneSpans;
-            return true;
-        }
+void SkOpSegment::detach(const SkOpSpan* span) {
+    if (span->done()) {
+        --this->fDoneCount;
     }
-    return false;
+    --this->fCount;
 }
 
-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) {
+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())) {
             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);
+        double testDistSq = testPt.distanceSquared(i.pt(index));
+        if (closestDistSq > testDistSq) {
+            closestDistSq = testDistSq;
         }
     }
-    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);
-    }
-    if (!checkMultiple) {
-        return;
-    }
-    // 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;
-            }
-        }
-        oTest = &oSpan;
-        while (++oTest < other->fTs.end()) {
-            if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
-                break;
-            }
-
-        }
-    }
-}
-
-// 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;
-        }
-        SkOpSpan* nextSpan = thisSpan + 1;
-        double thisT = thisSpan->fT;
-        double nextT = nextSpan->fT;
-        if (thisT == nextT) {
-            continue;
-        }
-        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;
-            }
-        }
-    }
-    int missingCount = missingSpans.count();
-    if (!missingCount) {
-        return;
-    }
-    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);
-        }
-    }
-    // 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();
-    }
-}
-
-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;
-        }
-        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 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;
+    return closestDistSq;
 }
 
 /*
@@ -3029,71 +789,57 @@
  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<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) 
-    {
+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) {
     // 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
-        int min = SkMin32(startIndex, endIndex);
-        if (fTs[min].fDone) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             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;
     }
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkASSERT(startIndex - endIndex != 0);
-    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+    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));
     // more than one viable candidate -- measure angles to find best
-
-    int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
+    int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    SkOpAngle* angle = spanToAngle(end, startIndex);
+    SkOpAngle* angle = this->spanToAngle(end, start);
     if (angle->unorderable()) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumMiWinding = updateWinding(endIndex, startIndex);
+    int sumMiWinding = updateWinding(end, start);
     if (sumMiWinding == SK_MinS32) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    int sumSuWinding = updateOppWinding(endIndex, startIndex);
+    int sumSuWinding = updateOppWinding(end, start);
     if (operand()) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
@@ -3110,11 +856,6 @@
         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);
             }
@@ -3122,30 +863,24 @@
         if (nextSegment->done()) {
             continue;
         }
-        if (nextSegment->isTiny(nextAngle)) {
-            continue;
-        }
         if (!activeAngle) {
-            (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpan* last = nextAngle->lastMarked();
+        SkOpSpanBase* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                    last->fSmall);
+            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");
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-#if DEBUG_ANGLE
-    if (foundAngle) {
-        foundAngle->debugSameAs(foundAngle);
-    }
-#endif
-
-    markDoneBinary(SkMin32(startIndex, endIndex));
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3159,62 +894,55 @@
     return nextSegment;
 }
 
-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) 
-    {
+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) {
     // 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
-        int min = SkMin32(startIndex, endIndex);
-        if (fTs[min].fDone) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             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;
     }
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkASSERT(startIndex - endIndex != 0);
-    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+    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));
     // more than one viable candidate -- measure angles to find best
-
-    int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
+    int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDoneUnary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    SkOpAngle* angle = spanToAngle(end, startIndex);
+    SkOpAngle* angle = this->spanToAngle(end, start);
+    if (angle->unorderable()) {
+        *unsortable = true;
+        markDone(start->starter(end));
+        return NULL;
+    }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumWinding = updateWinding(endIndex, startIndex);
+    int sumWinding = updateWinding(end, start);
     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 {
@@ -3224,11 +952,6 @@
         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);
             }
@@ -3236,24 +959,24 @@
         if (nextSegment->done()) {
             continue;
         }
-        if (nextSegment->isTiny(nextAngle)) {
-            continue;
-        }
         if (!activeAngle) {
-            nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpan* last = nextAngle->lastMarked();
+        SkOpSpanBase* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                    last->fSmall);
+            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");
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-    markDoneUnary(SkMin32(startIndex, endIndex));
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3267,57 +990,39 @@
     return nextSegment;
 }
 
-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)
-    {
+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)
 #if DEBUG_WINDING
         SkDebugf("%s simple\n", __FUNCTION__);
 #endif
-        int min = SkMin32(startIndex, endIndex);
-        if (fTs[min].fDone) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             return NULL;
         }
-        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());
+        markDone(startSpan);
+        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    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);
+    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;
+    }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
@@ -3325,16 +1030,13 @@
     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;
@@ -3342,7 +1044,7 @@
         }
         nextAngle = nextAngle->next();
     } while (nextAngle != angle);
-    markDone(SkMin32(startIndex, endIndex), 1);
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3356,105 +1058,39 @@
     return nextSegment;
 }
 
-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) {
+SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
+        bool* unsortable, SkChunkAlloc* allocator) {
     // iterate through T intersections and return topmost
     // topmost tangent from y-min to first pt is closer to horizontal
     SkASSERT(!done());
-    int firstT = -1;
-    /* SkPoint topPt = */ activeLeftTop(&firstT);
-    if (firstT < 0) {
+    SkOpSpanBase* firstT = NULL;
+    (void) this->activeLeftTop(&firstT);
+    if (!firstT) {
         *unsortable = !firstPass;
-        firstT = 0;
-        while (fTs[firstT].fDone) {
-            SkASSERT(firstT < fTs.count());
-            ++firstT;
+        firstT = &fHead;
+        while (firstT->upCast()->done()) {
+            firstT = firstT->upCast()->next();
         }
-        *tIndexPtr = firstT;
-        *endIndexPtr = nextExactSpan(firstT, 1);
+        *startPtr = firstT;
+        *endPtr = firstT->upCast()->next();
         return this;
     }
     // sort the edges to find the leftmost
     int step = 1;
-    int end;
-    if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
+    SkOpSpanBase* end;
+    if (firstT->final() || firstT->upCast()->done()) {
         step = -1;
-        end = nextSpan(firstT, step);
-        SkASSERT(end != -1);
+        end = firstT->prev();
+        SkASSERT(end);
+    } else {
+        end = firstT->upCast()->next();
     }
     // 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 != 0);
+    SkASSERT(firstT != end);
     SkOpAngle* markAngle = spanToAngle(firstT, end);
     if (!markAngle) {
-        markAngle = addSingletonAngles(step);
+        markAngle = addSingletonAngles(step, allocator);
     }
     markAngle->markStops();
     const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -3467,7 +1103,7 @@
     const SkOpAngle* angle = baseAngle;
     do {
         if (!angle->unorderable()) {
-            SkOpSegment* next = angle->segment();
+            const SkOpSegment* next = angle->segment();
             SkPathOpsBounds bounds;
             next->subDivideBounds(angle->end(), angle->start(), &bounds);
             bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -3495,9 +1131,10 @@
         *unsortable = angle->unorderable();
         if (firstPass || !*unsortable) {
             leftSegment = angle->segment();
-            *tIndexPtr = angle->end();
-            *endIndexPtr = angle->start();
-            if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
+            *startPtr = angle->end();
+            *endPtr = angle->start();
+            const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
+            if (!firstSpan->done()) {
                 break;
             }
         }
@@ -3508,157 +1145,52 @@
         return NULL;
     }
     if (leftSegment->verb() >= SkPath::kQuad_Verb) {
-        const int tIndex = *tIndexPtr;
-        const int endIndex = *endIndexPtr;
+        SkOpSpanBase* start = *startPtr;
+        SkOpSpanBase* end = *endPtr;
         bool swap;
-        if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
+        if (!leftSegment->clockwise(start, end, &swap)) {
     #if DEBUG_SWAP_TOP
-            SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
+            SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
                     __FUNCTION__,
-                    swap, leftSegment->debugInflections(tIndex, endIndex),
-                    leftSegment->serpentine(tIndex, endIndex),
-                    leftSegment->controlsContainedByEnds(tIndex, endIndex),
-                    leftSegment->monotonicInY(tIndex, endIndex));
+                    swap, leftSegment->debugInflections(start, end),
+                    leftSegment->monotonicInY(start, end));
     #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(*tIndexPtr, *endIndexPtr);
+                SkTSwap(*startPtr, *endPtr);
             }
         }
     }
-    SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
     return leftSegment;
 }
 
-int SkOpSegment::firstActive(int tIndex) const {
-    while (fTs[tIndex].fTiny) {
-        SkASSERT(!isCanceled(tIndex));
-        ++tIndex;
-    }
-    return tIndex;
+SkOpGlobalState* SkOpSegment::globalState() const {
+    return contour()->globalState(); 
 }
 
-// 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(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
-    fDoneSpans = 0;
-    fOperand = operand;
-    fXor = evenOdd;
+void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
+    fContour = contour;
+    fNext = NULL;
     fPts = pts;
     fVerb = verb;
-    fLoop = fMultiples = fSmall = fTiny = false;
+    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(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
-    int local = spanSign(start, end);
+void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
+        SkOpAngle::IncludeType angleIncludeType) {
+    int local = SkOpSegment::SpanSign(start, end);
     SkDEBUGCODE(bool success);
     if (angleIncludeType == SkOpAngle::kBinarySingle) {
-        int oppLocal = oppSign(start, end);
+        int oppLocal = SkOpSegment::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);
@@ -3679,12 +1211,13 @@
 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(int start, int end, double tHit, int winding, SkScalar hitDx,
-                              int oppWind, SkScalar hitOppDx) {
+bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
+        int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
+    SkASSERT(this == start->segment());
     SkASSERT(hitDx || !winding);
     SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
-    SkASSERT(dx);
-    int windVal = windValue(SkMin32(start, end));
+//    SkASSERT(dx);
+    int windVal = start->starter(end)->windValue();
 #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);
@@ -3693,9 +1226,9 @@
     if (abs(winding) < abs(sideWind)) {
         winding = sideWind;
     }
-    SkDEBUGCODE(int oppLocal = oppSign(start, end));
+    SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
     SkASSERT(hitOppDx || !oppWind || !oppLocal);
-    int oppWindVal = oppValue(SkMin32(start, end));
+    int oppWindVal = start->starter(end)->oppValue();
     if (!oppWind) {
         oppWind = dx < 0 ? oppWindVal : -oppWindVal;
     } else if (hitOppDx * dx >= 0) {
@@ -3714,149 +1247,57 @@
     return success;
 }
 
-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;
-                }
-            }
+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;
         }
-    } while (fTs[++tIndex].fT == 0);
+    }
     return false;
 }
 
-// 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
+bool SkOpSegment::isXor() const {
+    return fContour->isXor();
+}
 
-SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
-    int step = SkSign32(endIndex - index);
-    int min = SkMin32(index, endIndex);
-    markDoneBinary(min);
-    SkOpSpan* last = NULL;
+SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
+    int step = start->step(end);
+    SkOpSpan* minSpan = start->starter(end);
+    markDone(minSpan);
+    SkOpSpanBase* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
+    while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
         if (other->done()) {
             SkASSERT(!last);
             break;
         }
-        other->markDoneBinary(min);
+        other->markDone(minSpan);
     }
     return last;
 }
 
-SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
-    int step = SkSign32(endIndex - index);
-    int min = SkMin32(index, endIndex);
-    markDoneUnary(min);
-    SkOpSpan* last = NULL;
+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;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
-        if (other->done()) {
+    while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+        if (spanStart->windSum() != SK_MinS32) {
+            SkASSERT(spanStart->windSum() == winding);
             SkASSERT(!last);
             break;
         }
-        other->markDoneUnary(min);
-    }
-    return last;
-}
-
-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(&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(min, winding);
+        (void) other->markWinding(spanStart, winding);
     }
     if (lastPtr) {
         *lastPtr = last;
@@ -3864,37 +1305,32 @@
     return success;
 }
 
-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;
+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;
     SkOpSegment* other = this;
-    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);
+    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;
                 }
+            } else {
+                SkASSERT(spanStart->windSum() == oppWinding);
+                SkASSERT(spanStart->oppSum() == winding);
             }
             SkASSERT(!last);
-#endif
             break;
         }
-        if (fOperand == other->fOperand) {
-            (void) other->markWinding(min, winding, oppWinding);
+        if (this->operand() == other->operand()) {
+            (void) other->markWinding(spanStart, winding, oppWinding);
         } else {
-            (void) other->markWinding(min, oppWinding, winding);
+            (void) other->markWinding(spanStart, oppWinding, winding);
         }
     }
     if (lastPtr) {
@@ -3903,33 +1339,29 @@
     return success;
 }
 
-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) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
     }
-    SkOpSpan* last;
-    SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
+    SkOpSpanBase* last;
+    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
-        SkPathOpsDebug::WindingPrintf(last->fWindSum);
-        SkDebugf(" small=%d\n", last->fSmall);
+        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");
     }
 #endif
     return last;
 }
 
-SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
-                                 int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+                                   int oppSumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
@@ -3937,440 +1369,161 @@
     if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
         oppMaxWinding = oppSumWinding;
     }
-    SkOpSpan* last;
+    SkOpSpanBase* last = NULL;
     // caller doesn't require that this marks anything
-    (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
+    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
-        SkPathOpsDebug::WindingPrintf(last->fWindSum);
-        SkDebugf(" small=%d\n", last->fSmall);
+        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");
     }
 #endif
     return last;
 }
 
-// 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());
+void SkOpSegment::markDone(SkOpSpan* span) {
+    SkASSERT(this == span->segment());
+    if (span->done()) {
+        return;
+    }
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
+#endif
+    span->setDone(true);
+    ++fDoneCount;
+    debugValidate();
+}
+
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
+    SkASSERT(this == span->segment());
     SkASSERT(winding);
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        markOneDone(__FUNCTION__, lesser, winding);
-    }
-    do {
-        markOneDone(__FUNCTION__, index, winding);
-    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-}
-
-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) {
+    if (span->done()) {
         return false;
     }
 #if DEBUG_MARK_DONE
-    debugShowNewWinding(funName, *span, winding);
+    debugShowNewWinding(__FUNCTION__, span, winding);
 #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;
+    span->setWindSum(winding);
+    debugValidate();
     return true;
 }
 
-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(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->fOppSum = oppWinding;
-    debugValidate();
-    if (lastPtr) {
-        *lastPtr = span;
-    }
-    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 (!sumSet) {
-        for (int idx = 0; idx < points; ++idx){
-            sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
-        }
-    }
-    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 sum <= 0;
-}
-
-bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
-    SkASSERT(fVerb != SkPath::kLine_Verb);
-    if (fVerb == SkPath::kQuad_Verb) {
-        SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-        return dst.monotonicInY();
-    }
-    SkASSERT(fVerb == SkPath::kCubic_Verb);
-    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-    return dst.monotonicInY();
-}
-
-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());
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
+    SkASSERT(this == span->segment());
     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);
+    if (span->done()) {
+        return false;
     }
-    do {
-        success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
-   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
+#endif
+    span->setWindSum(winding);
+    span->setOppSum(oppWinding);
     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;
-        }
-        ++(*end);
-    }
-    *start = *end;
-    *end = nextExactSpan(*start, 1);
     return true;
 }
 
-static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
-    if (last && !endSpan->fSmall) {
-        *last = const_cast<SkOpSpan*>(endSpan);  // FIXME: get rid of cast
+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;
+    }
+    if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
+        return false;
+    }
+    return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
+}
+
+static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
+    if (last) {
+        *last = endSpan;
     }
     return NULL;
 }
 
-SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
-        SkOpSpan** last) const {
-    int origIndex = *indexPtr;
+bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+    SkASSERT(fVerb != SkPath::kLine_Verb);
+    if (fVerb == SkPath::kQuad_Verb) {
+        SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
+        return dst.monotonicInY();
+    }
+    SkASSERT(fVerb == SkPath::kCubic_Verb);
+    SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+    return dst.monotonicInY();
+}
+
+bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
+        SkOpSpanBase** end) {
+    while (span->final() || span->upCast()->done()) {
+        if (span->final()) {
+            return false;
+        }
+        span = span->upCast()->next();
+    }
+    *start = span;
+    *end = span->upCast()->next();
+    return true;
+}
+
+SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
+        SkOpSpanBase** last) const {
+    SkOpSpanBase* origStart = *startPtr;
     int step = *stepPtr;
-    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;
+    SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
+    SkASSERT(endSpan);
+    SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
+    SkOpSpanBase* foundSpan;
+    SkOpSpanBase* otherEnd;
     SkOpSegment* other;
     if (angle == NULL) {
-        if (endSpan.fT != 0 && endSpan.fT != 1) {
+        if (endSpan->t() != 0 && endSpan->t() != 1) {
             return NULL;
         }
-        other = endSpan.fOther;
-        foundIndex = endSpan.fOtherIndex;
-        otherEnd = other->nextExactSpan(foundIndex, step);
+        SkOpPtT* otherPtT = endSpan->ptT()->next();
+        other = otherPtT->segment();
+        foundSpan = otherPtT->span();
+        otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
     } 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);
         }
+#endif
         other = next->segment();
-        foundIndex = end = next->start();
+        foundSpan = endSpan = next->start();
         otherEnd = next->end();
     }
-    int foundStep = foundIndex < otherEnd ? 1 : -1;
+    int foundStep = foundSpan->step(otherEnd);
     if (*stepPtr != foundStep) {
-        return set_last(last, &endSpan);
+        return set_last(last, endSpan);
     }
-    SkASSERT(*indexPtr >= 0);
-    if (otherEnd < 0) {
+    SkASSERT(*startPtr);
+    if (!otherEnd) {
         return NULL;
     }
 //    SkASSERT(otherEnd >= 0);
-#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);
+    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);
     }
-#endif
-    *indexPtr = foundIndex;
+    *startPtr = foundSpan;
     *stepPtr = foundStep;
     if (minPtr) {
         *minPtr = foundMin;
@@ -4378,101 +1531,217 @@
     return other;
 }
 
-// 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;
+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();
         }
-        return to;
-    }
-    return -1;
+    } while ((span = span->next()->upCastable()));
 }
 
-// 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) {
+// 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;
             }
-            return to;
-        }
-    } 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)) {
+            if (opp->verb() == SkPath::kLine_Verb) {
                 continue;
             }
-            return to;
-        }
-    }
-    return -1;
-}
-
-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::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;
+            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;
             }
-        } else if (span.fPt == p2) {
-            p2IndexMax = index;
+            if (span->containsCoinEnd(opp)) {
+                continue;
+            } 
+            // if already visited and visited again, check for coin
+            if (span == &fHead) {
+                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 p1IndexMin > p2IndexMax;
+    } while ((span = span->next()->upCastable()));
+    clear_visited(&fHead);
 }
 
-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;
+// 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;
+                continue;
+            }
         }
-    }
+        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;
 }
 
-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);
+bool SkOpSegment::operand() const {
+    return fContour->operand();
+}
+
+bool SkOpSegment::oppXor() const {
+    return fContour->oppXor();
+}
+
+bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
+    if (fVerb == SkPath::kLine_Verb) {
+        return false;
+    }
+    // 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;
+}
+
+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::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);
     if (operand()) {
         *maxWinding = *sumSuWinding;
         *sumWinding = *sumSuWinding -= deltaSum;
@@ -4484,130 +1753,94 @@
         *oppMaxWinding = *sumSuWinding;
         *oppSumWinding = *sumSuWinding -= oppDeltaSum;
     }
-#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
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
 }
 
 void SkOpSegment::sortAngles() {
-    int spanCount = fTs.count();
-    if (spanCount <= 2) {
-        return;
-    }
-    int index = 0;
+    SkOpSpanBase* span = &this->fHead;
     do {
-        SkOpAngle* fromAngle = fTs[index].fFromAngle;
-        SkOpAngle* toAngle = fTs[index].fToAngle;
+        SkOpAngle* fromAngle = span->fromAngle();
+        SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
         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
-        if (toAngle) {
-            if (!baseAngle) {
-                baseAngle = toAngle;
-                if (inLoop(baseAngle, spanCount, &index)) {
-                    continue;
-                }
-            } else {
-                SkDEBUGCODE(int newIndex = index);
-                SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
+        SkOpAngle* baseAngle = fromAngle;
+        if (fromAngle && toAngle) {
 #if DEBUG_ANGLE
-                SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                        index);
-                wroteAfterHeader = true;
+            SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
+                    span->debugID());
+            wroteAfterHeader = true;
 #endif
-                baseAngle->insert(toAngle);
-            }
+            fromAngle->insert(toAngle);
+        } else if (!fromAngle) {
+            baseAngle = toAngle;
         }
-        SkOpAngle* nextFrom, * nextTo;
-        int firstIndex = index;
+        SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
         do {
-            SkOpSpan& span = fTs[index];
-            SkOpSegment* other = span.fOther;
-            SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
-            SkOpAngle* oAngle = oSpan.fFromAngle;
+            SkOpSpanBase* oSpan = ptT->span();
+            if (oSpan == span) {
+                continue;
+            }
+            SkOpAngle* oAngle = oSpan->fromAngle();
             if (oAngle) {
 #if DEBUG_ANGLE
                 if (!wroteAfterHeader) {
-                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                            index);
+                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+                            span->t(), span->debugID());
                     wroteAfterHeader = true;
                 }
 #endif
-                if (!oAngle->loopContains(*baseAngle)) {
+                if (!oAngle->loopContains(baseAngle)) {
                     baseAngle->insert(oAngle);
                 }
             }
-            oAngle = oSpan.fToAngle;
-            if (oAngle) {
+            if (!oSpan->final()) {
+                oAngle = oSpan->upCast()->toAngle();
+                if (oAngle) {
 #if DEBUG_ANGLE
-                if (!wroteAfterHeader) {
-                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                            index);
-                    wroteAfterHeader = true;
-                }
+                    if (!wroteAfterHeader) {
+                        SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+                                span->t(), span->debugID());
+                        wroteAfterHeader = true;
+                    }
 #endif
-                if (!oAngle->loopContains(*baseAngle)) {
-                    baseAngle->insert(oAngle);
+                    if (!oAngle->loopContains(baseAngle)) {
+                        baseAngle->insert(oAngle);
+                    }
                 }
             }
-            if (++index == spanCount) {
-                break;
+        } while ((ptT = ptT->next()) != stopPtT);
+        if (baseAngle->loopCount() == 1) {
+            span->setFromAngle(NULL);
+            if (toAngle) {
+                span->upCast()->setToAngle(NULL);
             }
-            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 (index < spanCount);
+    } while (!span->final() && (span = span->upCast()->next()));
 }
 
 // return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkPoint edge[4]) const {
     SkASSERT(start != end);
-    edge[0] = fTs[start].fPt;
+    const SkOpPtT& startPtT = *start->ptT();
+    const SkOpPtT& endPtT = *end->ptT();
+    edge[0] = startPtT.fPt;
     int points = SkPathOpsVerbToPoints(fVerb);
-    edge[points] = fTs[end].fPt;
+    edge[points] = endPtT.fPt;
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = fTs[start].fT;
-    double endT = fTs[end].fT;
+    double startT = startPtT.fT;
+    double endT = endPtT.fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
@@ -4637,17 +1870,19 @@
     return true;
 }
 
-// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkDCubic* result) const {
     SkASSERT(start != end);
-    (*result)[0].set(fTs[start].fPt);
+    const SkOpPtT& startPtT = *start->ptT();
+    const SkOpPtT& endPtT = *end->ptT();
+    (*result)[0].set(startPtT.fPt);
     int points = SkPathOpsVerbToPoints(fVerb);
-    (*result)[points].set(fTs[end].fPt);
+    (*result)[points].set(endPtT.fPt);
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = fTs[start].fT;
-    double endT = fTs[end].fT;
+    double startT = startPtT.fT;
+    double endT = endPtT.fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
@@ -4655,7 +1890,7 @@
             return false;
         }
         SkASSERT(fVerb == SkPath::kCubic_Verb);
-        if (start < end) {
+        if (startT == 0) {
             (*result)[1].set(fPts[1]);
             (*result)[2].set(fPts[2]);
             return false;
@@ -4673,49 +1908,29 @@
     return true;
 }
 
-void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkPathOpsBounds* bounds) const {
     SkPoint edge[4];
     subDivide(start, end, edge);
     (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
 }
 
-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) {
+void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
+    SkOpSpan* span = this->head();
+    do {
+        if (!span->done()) {
             break;
         }
-    }
-    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;
+    } while ((span = span->next()->upCastable()));
+    SkASSERT(span);
+    *start = span;
+    *end = span->next();
 }
 
-int SkOpSegment::updateOppWinding(int index, int endIndex) const {
-    int lesser = SkMin32(index, endIndex);
-    int oppWinding = oppSum(lesser);
-    int oppSpanWinding = oppSign(index, endIndex);
+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);
     if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
             && oppWinding != SK_MaxS32) {
         oppWinding -= oppSpanWinding;
@@ -4724,24 +1939,24 @@
 }
 
 int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateOppWinding(endIndex, startIndex);
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateOppWinding(endSpan, startSpan);
 }
 
 int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateOppWinding(startIndex, endIndex);
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateOppWinding(startSpan, endSpan);
 }
 
-int SkOpSegment::updateWinding(int index, int endIndex) const {
-    int lesser = SkMin32(index, endIndex);
-    int winding = windSum(lesser);
+int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+    const SkOpSpan* lesser = start->starter(end);
+    int winding = lesser->windSum();
     if (winding == SK_MinS32) {
         return winding;
     }
-    int spanWinding = spanSign(index, endIndex);
+    int spanWinding = SkOpSegment::SpanSign(start, end);
     if (winding && UseInnerWinding(winding - spanWinding, winding)
             && winding != SK_MaxS32) {
         winding -= spanWinding;
@@ -4750,26 +1965,15 @@
 }
 
 int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
-    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;
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateWinding(endSpan, startSpan);
 }
 
 int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateWindingReverse(endIndex, startIndex);
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateWinding(startSpan, endSpan);
 }
 
 // OPTIMIZATION: does the following also work, and is it any faster?
@@ -4784,25 +1988,17 @@
     return result;
 }
 
-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
+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
         return SK_MinS32;
     }
-    int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
+    int winding = crossOpp ? span->oppSum() : span->windSum();
     SkASSERT(winding != SK_MinS32);
-    int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
+    int windVal = crossOpp ? span->oppValue() : span->windValue();
 #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, t(tIndex), winding, windVal);
+            debugID(), crossOpp, tHit, span->t(), 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;
@@ -4828,20 +2024,6 @@
 }
 
 int SkOpSegment::windSum(const SkOpAngle* angle) const {
-    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;
+    const SkOpSpan* minSpan = angle->start()->starter(angle->end());
+    return minSpan->windSum();
 }
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index b4da929..c1c6e69 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -9,160 +9,262 @@
 
 #include "SkOpAngle.h"
 #include "SkOpSpan.h"
+#include "SkOpTAllocator.h"
 #include "SkPathOpsBounds.h"
 #include "SkPathOpsCurve.h"
-#include "SkTArray.h"
-#include "SkTDArray.h"
 
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-struct SkCoincidence;
+class SkOpCoincidence;
+class SkOpContour;
 class SkPathWriter;
 
 class SkOpSegment {
 public:
-    SkOpSegment() {
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-        fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
-#endif
-    }
+    enum AllowAlias {
+        kAllowAlias,
+        kNoAlias
+    };
 
     bool operator<(const SkOpSegment& rh) const {
         return fBounds.fTop < rh.fBounds.fTop;
     }
 
-    struct AlignedSpan  {
-        double fOldT;
-        double fT;
-        SkPoint fOldPt;
-        SkPoint fPt;
-        const SkOpSegment* fSegment;
-        const SkOpSegment* fOther1;
-        const SkOpSegment* fOther2;
-    };
+    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);
+
+    void addCubic(SkPoint pts[4], SkOpContour* parent) {
+        init(pts, parent, SkPath::kCubic_Verb);
+        fBounds.setCubicBounds(pts);
+    }
+
+    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;
+    }
+
+    void addLine(SkPoint pts[2], SkOpContour* parent) {
+        init(pts, parent, SkPath::kLine_Verb);
+        fBounds.set(pts, 2);
+    }
+
+    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;
+    }
+
+    void addQuad(SkPoint pts[3], SkOpContour* parent) {
+        init(pts, parent, SkPath::kQuad_Verb);
+        fBounds.setQuadBounds(pts);
+    }
+
+    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;
     }
 
-    // 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 bumpCount() {
+        ++fCount;
+    }
+
+    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;
     }
 
     int count() const {
-        return fTs.count();
+        return fCount;
     }
 
+    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);
+    }
+
+#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(fDoneSpans <= fTs.count());
-        return fDoneSpans == fTs.count();
-    }
-
-    bool done(int min) const {
-        return fTs[min].fDone;
+        SkASSERT(fDoneCount <= fCount);
+        return fDoneCount == fCount;
     }
 
     bool done(const SkOpAngle* angle) const {
-        return done(SkMin32(angle->start(), angle->end()));
+        return angle->start()->starter(angle->end())->done();
     }
 
     SkDPoint dPtAtT(double mid) const {
         return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
     }
 
-    SkVector dxdy(int index) const {
-        return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
+    SkDVector dSlopeAtT(double mid) const {
+        return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
     }
 
-    SkScalar dy(int index) const {
-        return dxdy(index).fY;
+    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;
     }
 
-    bool hasMultiples() const {
-        return fMultiples;
+    SkOpSpan* head() {
+        return &fHead;
     }
 
-    bool hasSmall() const {
-        return fSmall;
+    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 hasTiny() const {
-        return fTiny;
-    }
-
-    bool intersected() const {
-        return fTs.count() > 0;
-    }
-
-    bool isCanceled(int tIndex) const {
-        return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
-    }
-
-    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(int start, int end) const {
-        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
+    bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
+        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start->t(), end->t());
     }
 
-    bool operand() const {
-        return fOperand;
+    bool isXor() const;
+
+    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();
+
+    SkOpSegment* next() const {
+        return fNext;
     }
 
-    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
+    static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
+    SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
+    bool operand() const;
+
+    static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+        int result = start->t() < end->t() ? -start->upCast()->oppValue()
+                : end->upCast()->oppValue();
         return result;
     }
 
-    int oppSum(int tIndex) const {
-        return fTs[tIndex].fOppSum;
-    }
+    bool oppXor() const;
 
-    int oppSum(const SkOpAngle* angle) const {
-        int lesser = SkMin32(angle->start(), angle->end());
-        return fTs[lesser].fOppSum;
+    const SkOpSegment* prev() const {
+        return fPrev;
     }
 
-    int oppValue(int tIndex) const {
-        return fTs[tIndex].fOppValue;
-    }
-
-    int oppValue(const SkOpAngle* angle) const {
-        int lesser = SkMin32(angle->start(), angle->end());
-        return fTs[lesser].fOppValue;
-    }
-
-#if DEBUG_VALIDATE
-    bool oppXor() const {
-        return fOppXor;
-    }
-#endif
-
     SkPoint ptAtT(double mid) const {
         return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
     }
@@ -171,399 +273,113 @@
         return fPts;
     }
 
-    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, const SkOpPtT& test) const {
+        return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
     }
 
-    bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
-
-    void setOppXor(bool isOppXor) {
-        fOppXor = isOppXor;
+    bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
+        return ptsDisjoint(span.fT, span.fPt, t, pt);
     }
 
-    void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
-        int deltaSum = spanSign(index, endIndex);
+    bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
+
+    void resetVisited() {
+        fVisited = false;
+    }
+
+    void setContour(SkOpContour* contour) {
+        fContour = contour;
+    }
+
+    void setNext(SkOpSegment* next) {
+        fNext = next;
+    }
+
+    void setPrev(SkOpSegment* prev) {
+        fPrev = prev;
+    }
+
+    bool setVisited() {
+        if (fVisited) {
+            return false;
+        }
+        return (fVisited = true);
+    }
+
+    void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
+        int deltaSum = SpanSign(start, end);
         *maxWinding = *sumWinding;
         *sumWinding -= deltaSum;
     }
 
-    const SkOpSpan& span(int tIndex) const {
-        return fTs[tIndex];
-    }
+    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();
 
-    const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
-        SkASSERT(tStart != tEnd);
-        const SkOpSpan& span = fTs[tStart];
-        return tStart < tEnd ? span.fToAngle : span.fFromAngle;
-    }
-
-    // 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);
-    }
-
-    int spanSign(const SkOpAngle* angle) const {
-        SkASSERT(angle->segment() == this);
-        return spanSign(angle->start(), angle->end());
-    }
-
-    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
+    static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+        int result = start->t() < end->t() ? -start->upCast()->windValue()
+                : end->upCast()->windValue();
         return result;
     }
 
-    double t(int tIndex) const {
-        return fTs[tIndex].fT;
+    SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
+        SkASSERT(start != end);
+        return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
     }
 
-    double tAtMid(int start, int end, double mid) const {
-        return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+    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;
+
+    const SkOpSpanBase* tail() const {
+        return &fTail;
     }
 
-    void updatePts(const SkPoint pts[]) {
-        fPts = pts;
+    SkOpSpanBase* tail() {
+        return &fTail;
     }
 
+    static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
+        return start->t() * (1 - mid) + end->t() * mid;
+    }
+
+    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;
+
+    static bool UseInnerWinding(int outerWinding, int innerWinding);
+
     SkPath::Verb verb() const {
         return fVerb;
     }
 
-    int windSum(int tIndex) const {
-        return fTs[tIndex].fWindSum;
-    }
-
-    int windValue(int tIndex) const {
-        return fTs[tIndex].fWindValue;
-    }
-
-#if defined(SK_DEBUG) || DEBUG_WINDING
-    SkScalar xAtT(int index) const {
-        return xAtT(&fTs[index]);
-    }
-#endif
-
-#if DEBUG_VALIDATE
-    bool _xor() const {  // FIXME: used only by SkOpAngle::debugValidateLoop()
-        return fXor;
-    }
-#endif
-
-    const SkPoint& xyAtT(const SkOpSpan* span) const {
-        return span->fPt;
-    }
-
-    const SkPoint& xyAtT(int index) const {
-        return xyAtT(&fTs[index]);
-    }
-
-#if defined(SK_DEBUG) || DEBUG_WINDING
-    SkScalar yAtT(int index) const {
-        return yAtT(&fTs[index]);
-    }
-#endif
-
-    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 windingAtT(double tHit, const SkOpSpan* span, bool crossOpp, SkScalar* dx) const;
     int windSum(const SkOpAngle* angle) const;
-// available for testing only
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-    int debugID() const {
-        return fID;
+
+    SkPoint* writablePt(bool end) {
+        return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
     }
-#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:
-    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
+    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)
     SkPath::Verb fVerb;
-    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;
+    bool fVisited;  // used by missing coincidence check
+    PATH_OPS_DEBUG_CODE(int fID);
 };
 
 #endif
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
new file mode 100755
index 0000000..37d5120
--- /dev/null
+++ b/src/pathops/SkOpSpan.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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;
+}
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index d9ce44e..9e5939a 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -7,36 +7,460 @@
 #ifndef SkOpSpan_DEFINED
 #define SkOpSpan_DEFINED
 
+#include "SkPathOpsDebug.h"
 #include "SkPoint.h"
 
-class SkOpAngle;
+class SkChunkAlloc;
+struct SkOpAngle;
+class SkOpContour;
+class SkOpGlobalState;
 class SkOpSegment;
+class SkOpSpanBase;
+class SkOpSpan;
 
-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
+// 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
     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
diff --git a/src/pathops/SkOpTAllocator.h b/src/pathops/SkOpTAllocator.h
index c80c12f..e8835f0 100644
--- a/src/pathops/SkOpTAllocator.h
+++ b/src/pathops/SkOpTAllocator.h
@@ -19,6 +19,12 @@
         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();
     }
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 1a5bfc1..b0fd822 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -5,47 +5,25 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 #include "SkTSort.h"
 
-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;
+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;
     const double mid = *midPtr;
     const SkOpSegment* current = *currentPtr;
-    double tAtMid = current->tAtMid(index, endIndex, mid);
+    double tAtMid = SkOpSegment::TAtMid(start, end, mid);
     SkPoint basePt = current->ptAtT(tAtMid);
     int contourCount = contourList.count();
     SkScalar bestY = SK_ScalarMin;
     SkOpSegment* bestSeg = NULL;
-    int bestTIndex = 0;
+    SkOpSpan* bestTSpan = NULL;
     bool bestOpp;
     bool hitSomething = false;
     for (int cTest = 0; cTest < contourCount; ++cTest) {
@@ -57,37 +35,38 @@
         if (bestY > contour->bounds().fBottom) {
             continue;
         }
-        int segmentCount = contour->segments().count();
-        for (int test = 0; test < segmentCount; ++test) {
-            SkOpSegment* testSeg = &contour->segments()[test];
+        SkOpSegment* testSeg = contour->first();
+        SkASSERT(testSeg);
+        do {
             SkScalar testY = bestY;
             double testHit;
-            int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
-                    testOpp, testSeg == current);
-            if (testTIndex < 0) {
-                if (testTIndex == SK_MinS32) {
+            bool vertical;
+            SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
+                    testSeg == current, &testY, &testHit, &hitSomething, &vertical);
+            if (!testTSpan) {
+                if (vertical) {
                     hitSomething = true;
                     bestSeg = NULL;
                     goto abortContours;  // vertical encountered, return and try different point
                 }
                 continue;
             }
-            if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
-                double baseT = current->t(index);
-                double endT = current->t(endIndex);
+            if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
+                double baseT = start->t();
+                double endT = end->t();
                 double newMid = (testHit - baseT) / (endT - baseT);
 #if DEBUG_WINDING
-                double midT = current->tAtMid(index, endIndex, mid);
-                SkPoint midXY = current->xyAtT(midT);
-                double newMidT = current->tAtMid(index, endIndex, newMid);
-                SkPoint newXY = current->xyAtT(newMidT);
+                double midT = SkOpSegment::TAtMid(start, end, mid);
+                SkPoint midXY = current->ptAtT(midT);
+                double newMidT = SkOpSegment::TAtMid(start, end, newMid);
+                SkPoint newXY = current->ptAtT(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, current->xAtT(index), current->yAtT(index),
+                        baseT, start->pt().fX, start->pt().fY,
                         baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
                         baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
-                        endT, current->xAtT(endIndex), current->yAtT(endIndex));
+                        endT, end->pt().fX, end->pt().fY);
 #endif
                 *midPtr = newMid * 2;  // calling loop with divide by 2 before continuing
                 return SK_MinS32;
@@ -95,38 +74,39 @@
             bestSeg = testSeg;
             *bestHit = testHit;
             bestOpp = testOpp;
-            bestTIndex = testTIndex;
+            bestTSpan = testTSpan;
             bestY = testY;
-        }
+        } while ((testSeg = testSeg->next()));
     }
 abortContours:
     int result;
     if (!bestSeg) {
         result = hitSomething ? SK_MinS32 : 0;
     } else {
-        if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
+        if (bestTSpan->windSum() == SK_MinS32) {
             *currentPtr = bestSeg;
-            *indexPtr = bestTIndex;
-            *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
-            SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+            *startPtr = bestTSpan;
+            *endPtr = bestTSpan->next();
+            SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
             *tryAgain = true;
             return 0;
         }
-        result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
+        result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
         SkASSERT(result == SK_MinS32 || *bestDx);
     }
-    double baseT = current->t(index);
-    double endT = current->t(endIndex);
+    double baseT = (*startPtr)->t();
+    double endT = (*endPtr)->t();
     *bestHit = baseT + mid * (endT - baseT);
     return result;
 }
 
-SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
+SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
+         SkOpSpanBase** endPtr) {
     int contourCount = contourList.count();
     SkOpSegment* result;
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         SkOpContour* contour = contourList[cIndex];
-        result = contour->undoneSegment(start, end);
+        result = contour->undoneSegment(startPtr, endPtr);
         if (result) {
             return result;
         }
@@ -134,20 +114,23 @@
     return NULL;
 }
 
-SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
+SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr) {
     while (chase->count()) {
-        SkOpSpan* span;
+        SkOpSpanBase* span;
         chase->pop(&span);
-        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
-        SkOpSegment* segment = backPtr.fOther;
-        *tIndex = backPtr.fOtherIndex;
+        SkOpSegment* segment = span->segment();
+        *startPtr = span->ptT()->next()->span();
         bool sortable = true;
         bool done = true;
-        *endIndex = -1;
-        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
+        *endPtr = NULL;
+        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
                 &sortable)) {
-            *tIndex = last->start();
-            *endIndex = last->end();
+            if (last->unorderable()) {
+                continue;
+            }
+            *startPtr = last->start();
+            *endPtr = last->end();
     #if TRY_ROTATE
             *chase->insert(0) = span;
     #else
@@ -162,65 +145,58 @@
             continue;
         }
         // find first angle, initialize winding to computed wind sum
-        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
-        const SkOpAngle* firstAngle;
-        SkDEBUGCODE(firstAngle = angle);
-        SkDEBUGCODE(bool loop = false);
-        int winding;
+        const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
+        if (!angle) {
+            continue;
+        }
+        const SkOpAngle* firstAngle = angle;
+        bool loop = false;
+        int winding = SK_MinS32;
         do {
             angle = angle->next();
-            SkASSERT(angle != firstAngle || !loop);
-            SkDEBUGCODE(loop |= angle == firstAngle);
+            if (angle == firstAngle && loop) {
+                break;    // if we get here, there's no winding, loop is unorderable
+            }
+            loop |= angle == firstAngle;
             segment = angle->segment();
             winding = segment->windSum(angle);
         } while (winding == SK_MinS32);
-        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;
+        if (winding == SK_MinS32) {
+            continue;
         }
-        // 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)
+        int sumWinding = segment->updateWindingReverse(angle);
+        SkOpSegment* first = NULL;
         firstAngle = angle;
-        winding -= firstAngle->segment()->spanSign(firstAngle);
         while ((angle = angle->next()) != firstAngle) {
             segment = angle->segment();
-            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;
+            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;
                 }
-                // allowed to do nothing
-                (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
-                break;
+                // OPTIMIZATION: should this also add to the chase?
+                (void) segment->markAngle(maxWinding, sumWinding, angle);
             }
         }
-        *chase->insert(0) = span;
-        return segment;
+        if (first) {
+       #if TRY_ROTATE
+            *chase->insert(0) = span;
+       #else
+            *chase->append() = span;
+       #endif
+            return first;
+        }
     }
     return NULL;
 }
 
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
     int index;
     for (index = 0; index < contourList.count(); ++ index) {
         contourList[index]->debugShowActiveSpans();
@@ -228,11 +204,12 @@
 }
 #endif
 
-static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
-        int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
+static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
+        bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
+        bool* unsortable, bool* done, SkChunkAlloc* allocator) {
     SkOpSegment* result;
     const SkOpSegment* lastTopStart = NULL;
-    int lastIndex = -1, lastEndIndex = -1;
+    SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
     do {
         SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
         int contourCount = contourList.count();
@@ -261,27 +238,27 @@
             return NULL;
         }
         *topLeft = bestXY;
-        result = topStart->findTop(index, endIndex, unsortable, firstPass);
+        result = topStart->findTop(firstPass, start, end, unsortable, allocator);
         if (!result) {
-            if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
+            if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
                 *done = true;
                 return NULL;
             }
             lastTopStart = topStart;
-            lastIndex = *index;
-            lastEndIndex = *endIndex;
+            lastStart = *start;
+            lastEnd = *end;
         }
     } while (!result);
     return result;
 }
 
-static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
-        SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
+static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
+        SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
         SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
     double test = 0.9;
     int contourWinding;
     do {
-        contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
+        contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
                 tHit, hitDx, tryAgain, &test, opp);
         if (contourWinding != SK_MinS32 || *tryAgain) {
             return contourWinding;
@@ -296,9 +273,9 @@
     return contourWinding;
 }
 
-static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
-        SkOpSegment** current, int* index, int* endIndex) {
-    if (!(*current)->isVertical(*index, *endIndex)) {
+static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
+        SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
+    if (!(*current)->isVertical(*start, *end)) {
         return;
     }
     int contourCount = contourList.count();
@@ -307,7 +284,7 @@
         if (contour->done()) {
             continue;
         }
-        SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
+        SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
         if (nonVertical) {
             *current = nonVertical;
             return;
@@ -316,41 +293,41 @@
     return;
 }
 
-struct SortableTop {  // error if local in pre-C++11
-    SkOpSegment* fSegment;
-    int fIndex;
-    int fEndIndex;
+struct SortableTop2 {  // error if local in pre-C++11
+    SkOpSpanBase* fStart;
+    SkOpSpanBase* fEnd;
 };
 
-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);
+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);
     if (!current) {
         return NULL;
     }
-    const int startIndex = *indexPtr;
-    const int endIndex = *endIndexPtr;
+    SkOpSpanBase* start = *startPtr;
+    SkOpSpanBase* end = *endPtr;
+    SkASSERT(current == start->segment());
     if (*firstContour) {
-        current->initWinding(startIndex, endIndex, angleIncludeType);
+        current->initWinding(start, end, angleIncludeType);
         *firstContour = false;
         return current;
     }
-    int minIndex = SkMin32(startIndex, endIndex);
-    int sumWinding = current->windSum(minIndex);
+    SkOpSpan* minSpan = start->starter(end);
+    int sumWinding = minSpan->windSum();
     if (sumWinding == SK_MinS32) {
-        int index = endIndex;
-        int oIndex = startIndex;
-        do { 
-            const SkOpSpan& span = current->span(index);
-            if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
-                current->addSimpleAngle(index);
+        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);
             }
-            sumWinding = current->computeSum(oIndex, index, angleIncludeType);
-            SkTSwap(index, oIndex);
-        } while (sumWinding == SK_MinS32 && index == startIndex);
+            sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
+            SkTSwap(iSpan, oSpan);
+        } while (sumWinding == SK_MinS32 && iSpan == start);
     }
     if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
         return current;
@@ -364,26 +341,28 @@
     SkScalar hitDx = 0;
     SkScalar hitOppDx = 0;
     // keep track of subsequent returns to detect infinite loops
-    SkTDArray<SortableTop> sortableTops;
+    SkTDArray<SortableTop2> 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(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
-        skipVertical(contourList, &current, indexPtr, endIndexPtr);
+        SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+        SkASSERT(current == (*startPtr)->segment());
+        skipVertical(contourList, &current, startPtr, endPtr);
         SkASSERT(current);  // FIXME: if null, all remaining are vertical
-        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+        SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+        SkASSERT(current == (*startPtr)->segment());
         tryAgain = false;
-        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
+        contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &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 SortableTop& prev = sortableTops[index];
+                const SortableTop2& prev = sortableTops[index];
                 if (giveUp) {
-                    prev.fSegment->markDoneFinal(prev.fIndex);
-                } else if (prev.fSegment == current
-                        && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
+                    prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
+                } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
                     // 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;
@@ -395,14 +374,13 @@
                 return NULL;
             }
         }
-        SortableTop* sortableTop = sortableTops.append();
-        sortableTop->fSegment = current;
-        sortableTop->fIndex = *indexPtr;
-        sortableTop->fEndIndex = *endIndexPtr;
+        SortableTop2* sortableTop = sortableTops.append();
+        sortableTop->fStart = *startPtr;
+        sortableTop->fEnd = *endPtr;
 #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(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
-                *onlyVertical);
+                __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
+                tHit, hitDx, tryAgain, *onlyVertical);
 #endif
         if (*onlyVertical) {
             return current;
@@ -413,127 +391,35 @@
         if (angleIncludeType < SkOpAngle::kBinarySingle) {
             break;
         }
-        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
+        oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
                 &hitOppDx, &tryAgain, NULL, true);
+        SkASSERT(current == (*startPtr)->segment());
     } while (tryAgain);
-    bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
+    bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
             oppContourWinding, hitOppDx);
     if (current->done()) {
         return NULL;
     } else if (!success) {  // check if the span has a valid winding
-        int min = SkTMin(*indexPtr, *endIndexPtr);
-        const SkOpSpan& span = current->span(min);
-        if (span.fWindSum == SK_MinS32) {
+        SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
+            : (*endPtr)->upCast();
+        if (minSpan->windSum() == SK_MinS32) {
             return NULL;
         }
     }
     return current;
 }
 
-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;
-        }
-    }
-    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,
+void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
                      bool evenOdd, bool oppEvenOdd) {
-    int count = contours.count();
-    if (count == 0) {
+    do {
+        if (contour->count()) {
+            contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
+            *list.append() = contour;
+        }
+    } while ((contour = contour->next()));
+    if (list.count() < 2) {
         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);
 }
 
@@ -554,19 +440,22 @@
         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
-    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();
+    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();
 #if DEBUG_ASSEMBLE
         SkDebugf("%s contour", __FUNCTION__);
         if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -578,44 +467,42 @@
                 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
 #endif
         if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
-            eContour.toPath(simple);
+            eContour->toPath(simple);
             continue;
         }
-        runs.push_back(outer);
-    }
-    count = runs.count();
+        *runs.append() = eContour;
+    } while ((eContour = eContour->next()));
+    int count = runs.count();
     if (count == 0) {
         return;
     }
-    SkTArray<int, true> sLink, eLink;
-    sLink.push_back_n(count);
-    eLink.push_back_n(count);
+    SkTDArray<int> sLink, eLink;
+    sLink.append(count);
+    eLink.append(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
-    SkTArray<double, true> distances;
-    distances.push_back_n(entries);
+    SkTDArray<double> distances;
+    distances.append(entries);
     for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
-        outer = runs[rIndex >> 1];
-        const SkOpContour& oContour = contours[outer];
-        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
+        const SkOpContour* oContour = runs[rIndex >> 1];
+        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) {
-            int inner = runs[iIndex >> 1];
-            const SkOpContour& iContour = contours[inner];
-            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
+            const SkOpContour* iContour = runs[iIndex >> 1];
+            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
         }
     }
-    SkTArray<int, true> sortedDist;
-    sortedDist.push_back_n(entries);
+    SkTDArray<int> sortedDist;
+    sortedDist.append(entries);
     for (rIndex = 0; rIndex < entries; ++rIndex) {
         sortedDist[rIndex] = rIndex;
     }
@@ -678,17 +565,16 @@
                     eIndex < 0 ? ~eIndex : eIndex);
 #endif
         do {
-            outer = runs[rIndex];
-            const SkOpContour& contour = contours[outer];
+            const SkOpContour* contour = runs[rIndex];
             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,
@@ -742,36 +628,88 @@
 #endif
 }
 
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
-#if DEBUG_SHOW_WINDING
-    SkOpContour::debugShowWindingValues(contourList);
-#endif
-    if (!CoincidenceCheck(contourList, total)) {
+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)) {
         return false;
     }
-#if DEBUG_SHOW_WINDING
-    SkOpContour::debugShowWindingValues(contourList);
+    align(contourList);  // give all span members common values
+#if DEBUG_VALIDATE
+    globalState->setPhase(SkOpGlobalState::kIntersecting);
 #endif
-    fixOtherTIndex(contourList);
-    if (!checkEnds(contourList)) {  // check if connecting curve intersected at the same end
+    coincidence->addMissing(allocator);
+#if DEBUG_VALIDATE
+    globalState->setPhase(SkOpGlobalState::kWalking);
+#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
         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);
-    if (!calcAngles(contourList)) {
-        return false;
-    }
+    calcAngles(contourList, allocator);
     sortAngles(contourList);
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+    if (globalState->angleCoincidence()) {
+        missingCoincidence(contourList, coincidence, allocator);
+        if (!coincidence->apply()) {
+            return false;
+        }
+    }
+#if DEBUG_ACTIVE_SPANS
     DebugShowActiveSpans(*contourList);
 #endif
     return true;
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 0d8cfc4..1bf1791 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -8,24 +8,28 @@
 #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);
-// 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,
+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,
                      bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
+bool HandleCoincidence(SkTDArray<SkOpContour*>* , SkOpCoincidence* , SkChunkAlloc* ,
+                       SkOpGlobalState* );
 
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList);
 #endif
 
 #endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index 9d70d58..d4a5898 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.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 "SkLineParameters.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
@@ -26,8 +27,8 @@
         double priorT = t - step;
         SkASSERT(priorT >= min);
         SkDPoint lessPt = ptAtT(priorT);
-        if (approximately_equal(lessPt.fX, cubicAtT.fX)
-                && approximately_equal(lessPt.fY, cubicAtT.fY)) {
+        if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
+                && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
             return -1;  // binary search found no point at this axis intercept
         }
         double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@@ -41,10 +42,12 @@
             t = priorT;
         } else {
             double nextT = t + lastStep;
-            SkASSERT(nextT <= max);
+            if (nextT > max) {
+                return -1;
+            }
             SkDPoint morePt = ptAtT(nextT);
-            if (approximately_equal(morePt.fX, cubicAtT.fX)
-                    && approximately_equal(morePt.fY, cubicAtT.fY)) {
+            if (approximately_equal_half(morePt.fX, cubicAtT.fX)
+                    && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
                 return -1;  // binary search found no point at this axis intercept
             }
             double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@@ -88,35 +91,6 @@
     *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))
@@ -124,17 +98,120 @@
             && 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(distance)) {
+    if (!approximately_zero_when_compared_to(distance, largest)) {
         return false;
     }
     distance = lineParameters.controlPtDistance(*this, 2);
-    return approximately_zero(distance);
+    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;
 }
 
 bool SkDCubic::monotonicInY() const {
@@ -142,6 +219,13 @@
             && 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]);
@@ -163,26 +247,6 @@
     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;
@@ -505,25 +569,10 @@
 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]);
     }
diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h
index 1037cae..9932e1d 100644
--- a/src/pathops/SkPathOpsCubic.h
+++ b/src/pathops/SkPathOpsCubic.h
@@ -10,7 +10,6 @@
 
 #include "SkPath.h"
 #include "SkPathOpsPoint.h"
-#include "SkTArray.h"
 
 struct SkDCubicPair {
     const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@@ -19,13 +18,33 @@
 };
 
 struct SkDCubic {
+    static const int kPointCount = 4;
+    static const int kPointLast = kPointCount - 1;
+    static const int kMaxIntersections = 9;
+
     enum SearchAxis {
         kXAxis,
         kYAxis
     };
 
-    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]; }
+    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]; }
 
     void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
     double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
@@ -33,30 +52,35 @@
     SkDCubicPair chopAt(double t) const;
     bool clockwise() const;
     static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
-    bool controlsContainedByEnds() const;
+    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;
     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[4], double tValues[2]) {
+    static int FindInflections(const SkPoint a[kPointCount], 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[4]) {
+    void set(const SkPoint pts[kPointCount]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
@@ -65,7 +89,7 @@
 
     SkDCubic subDivide(double t1, double t2) const;
 
-    static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
+    static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
         SkDCubic cubic;
         cubic.set(a);
         return cubic.subDivide(t1, t2);
@@ -73,7 +97,7 @@
 
     void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
 
-    static void SubDivide(const SkPoint pts[4], const SkDPoint& a, const SkDPoint& d, double t1,
+    static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
                           double t2, SkDPoint p[2]) {
         SkDCubic cubic;
         cubic.set(pts);
@@ -81,16 +105,29 @@
     }
 
     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[4];
+    SkDPoint fPts[kPointCount];
 };
 
+/* 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
deleted file mode 100644
index d763444..0000000
--- a/src/pathops/SkPathOpsCubicSect.h
+++ /dev/null
@@ -1,175 +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.
- */
-#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
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 7db93f5..0331f34 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -7,6 +7,13 @@
 
 #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
 
@@ -26,10 +33,10 @@
 const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
 #endif
 
-bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
-        const SkOpSpan* span) {
+bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
+        const SkOpSpanBase* span) {
     for (int index = 0; index < chaseArray.count(); ++index) {
-        const SkOpSpan* entry = chaseArray[index];
+        const SkOpSpanBase* entry = chaseArray[index];
         if (entry == span) {
             return true;
         }
@@ -65,6 +72,8 @@
         SkDebugf("%d", wind);
     }
 }
+#endif //  defined SK_DEBUG || !FORCE_RELEASE
+
 
 #if DEBUG_SHOW_TEST_NAME
 void* SkPathOpsDebug::CreateNameStr() {
@@ -97,11 +106,160 @@
 }
 #endif
 
-#endif //  defined SK_DEBUG || !FORCE_RELEASE
-
 #include "SkOpAngle.h"
 #include "SkOpSegment.h"
 
+#if DEBUG_SWAP_TOP
+int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+    if (fVerb != SkPath::kCubic_Verb) {
+        return false;
+    }
+    SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+    double inflections[2];
+    return dst.findInflections(inflections);
+}
+#endif
+
+SkOpAngle* SkOpSegment::debugLastAngle() {
+    SkOpAngle* result = NULL;
+    SkOpSpan* span = this->head();
+    do {
+        if (span->toAngle()) {
+            SkASSERT(!result);
+            result = span->toAngle();
+        }
+    } while ((span = span->next()->upCastable()));
+    SkASSERT(result);
+    return result;
+}
+
+void SkOpSegment::debugReset() {
+    this->init(this->fPts, this->contour(), this->verb());
+}
+
+#if DEBUG_ACTIVE_SPANS
+void SkOpSegment::debugShowActiveSpans() const {
+    debugValidate();
+    if (done()) {
+        return;
+    }
+    int lastId = -1;
+    double lastT = -1;
+    const SkOpSpan* span = &fHead;
+    do {
+        if (span->done()) {
+            continue;
+        }
+        if (lastId == fID && lastT == span->t()) {
+            continue;
+        }
+        lastId = fID;
+        lastT = span->t();
+        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) {
+            SkDebugf("?");
+        } else {
+            SkDebugf("%d", span->windSum());
+        }
+        SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
+        SkDebugf("\n");
+   } while ((span = span->next()->upCastable()));
+}
+#endif
+
+#if DEBUG_MARK_DONE
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
+    const SkPoint& pt = span->ptT()->fPt;
+    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) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", winding);
+    }
+    SkDebugf(" windSum=");
+    if (span->windSum() == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->windSum());
+    }
+    SkDebugf(" windValue=%d\n", span->windValue());
+}
+
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
+                                      int oppWinding) {
+    const SkPoint& pt = span->ptT()->fPt;
+    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) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->oppSum());
+    }
+    SkDebugf(" windSum=");
+    if (span->windSum() == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->windSum());
+    }
+    SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
+}
+
+#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;
@@ -111,25 +269,59 @@
         SkDebugf("\n");
         next = next->fNext;
     } while (next && next != first);
+    next = first;
+    do {
+        next->debugValidate();
+        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
-
+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
+}
+
 void SkOpAngle::debugValidateNext() const {
+#if !FORCE_RELEASE
     const SkOpAngle* first = this;
     const SkOpAngle* next = first;
     SkTDArray<const SkOpAngle*>(angles);
@@ -145,422 +337,137 @@
             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
-bool SkOpSegment::controlsContainedByEnds(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.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
-
-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.fToAngle;
-        }
-    }
-    SkASSERT(result);
-    return result;
-}
-
-void SkOpSegment::debugReset() {
-    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 || 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;
-#endif
-    for (int i = 0; i < fTs.count(); ++i) {
-        if (fTs[i].fDone) {
-            continue;
-        }
-        SK_ALWAYSBREAK(i < fTs.count() - 1);
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
-        if (lastId == fID && lastT == fTs[i].fT) {
-            continue;
-        }
-        lastId = fID;
-        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 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", fTs[i].fWindSum);
-        }
-        SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
-    }
-}
-#endif
-
-#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);
-    }
-    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", span.fWindSum);
-    }
-    SkDebugf(" windValue=%d\n", span.fWindValue);
-}
-
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
-                                      int oppWinding) {
-    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);
-    }
-    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.fOppSum);
-    }
-    SkDebugf(" windSum=");
-    if (span.fWindSum == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", span.fWindSum);
-    }
-    SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
-}
-#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;
-}
-#endif
 
 void SkOpSegment::debugValidate() const {
 #if DEBUG_VALIDATE
-    int count = fTs.count();
-    SK_ALWAYSBREAK(count >= 2);
-    SK_ALWAYSBREAK(fTs[0].fT == 0);
-    SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
+    const SkOpSpanBase* span = &fHead;
+    double lastT = -1;
+    const SkOpSpanBase* prev = NULL;
+    int count = 0;
     int done = 0;
-    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);
+    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
-//            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;
+}
+
+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;
+                }
             }
         }
-        last = &span;
-        hasLoop |= last->fLoop;
+        ++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());
     }
-    SK_ALWAYSBREAK(done == fDoneSpans);
-//    if (fAngles.count() ) {
-//        fAngles.begin()->debugValidateLoop();
-//    }
+    if (fFromAngle) {
+        fFromAngle->debugValidate();
+    }
+    if (!this->final() && this->upCast()->toAngle()) {
+        this->upCast()->toAngle()->debugValidate();
+    }
+#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;
+                }
+            }
+        }
+        ++loop;
+    } while ((next = next->fNext) && next != this);
+    return 0;
+}
+
+void SkOpPtT::debugValidate() const {
+#if DEBUG_VALIDATE
+    if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
+        return;
+    }
+    SkASSERT(fNext);
+    SkASSERT(fNext != this);
+    SkASSERT(fNext->fNext);
+    SkASSERT(debugLoopLimit(false) == 0);
 #endif
 }
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 5770aef..72a9ea5 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -39,35 +39,22 @@
 
 #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_PAIR 0
+#define DEBUG_ADD_T 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_UNSORTABLE 0
+#define DEBUG_T_SECT 0
+#define DEBUG_T_SECT_DUMP 0
 #define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 0
 #define DEBUG_WINDING_AT_T 0
 
@@ -75,51 +62,56 @@
 
 #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_PAIR 1
+#define DEBUG_ADD_T 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 4
+#define DEBUG_LIMIT_WIND_SUM 5
 #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_UNSORTABLE 1
-#define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
+#define DEBUG_T_SECT 1
+#define DEBUG_T_SECT_DUMP 02
+#define DEBUG_VALIDATE 1
 #define DEBUG_WINDING 1
 #define DEBUG_WINDING_AT_T 1
 
 #endif
 
-#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}}"
+#ifdef SK_RELEASE
+    #define PATH_OPS_DEBUG_RELEASE(a, b) b
+    #define PATH_OPS_DEBUG_CODE(...)
+    #define PATH_OPS_DEBUG_PARAMS(...)
 #else
-#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 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(...)
+#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__
+#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
@@ -135,7 +127,6 @@
 #include "SkTLS.h"
 #endif
 
-#include "SkTArray.h"
 #include "SkTDArray.h"
 
 class SkPathOpsDebug {
@@ -156,7 +147,6 @@
     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);
@@ -171,66 +161,96 @@
 #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 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 *>* );
+
+    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);
 };
 
 // shorthand for calling from debugger
-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);
+template<typename TCurve> class SkTSect;
+template<typename TCurve> class SkTSpan;
 
-void Dump(const SkTDArray<SkOpSpan* >& chase);
-void Dump(const SkTDArray<SkOpSpan* >* chase);
+struct SkDQuad;
+struct SkDCubic;
 
-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);
+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);
 
-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);
+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 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);
+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>* );
 
 // generates tools/path_sorter.htm and path_visualizer.htm compatible data
-void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
+void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
+void DumpT(const SkDQuad& quad, double t);
 
-void DumpT(const struct SkDQuad& quad, double t);
+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 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
diff --git a/src/pathops/SkPathOpsLine.cpp b/src/pathops/SkPathOpsLine.cpp
index e4fc97b..70f2e12 100644
--- a/src/pathops/SkPathOpsLine.cpp
+++ b/src/pathops/SkPathOpsLine.cpp
@@ -6,14 +6,6 @@
  */
 #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
 
@@ -28,6 +20,7 @@
 //    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
@@ -110,19 +103,6 @@
     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) {
diff --git a/src/pathops/SkPathOpsLine.h b/src/pathops/SkPathOpsLine.h
index 74eb615..bb25162 100644
--- a/src/pathops/SkPathOpsLine.h
+++ b/src/pathops/SkPathOpsLine.h
@@ -20,27 +20,20 @@
         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
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index f2b25c0..77ae2de 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -5,27 +5,29 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
+static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr) {
     while (chase.count()) {
-        SkOpSpan* span;
+        SkOpSpanBase* span;
         chase.pop(&span);
-        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
-        SkOpSegment* segment = backPtr.fOther;
-        *tIndex = backPtr.fOtherIndex;
+        // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
+        *startPtr = span->ptT()->prev()->span();
+        SkOpSegment* segment = (*startPtr)->segment();
         bool sortable = true;
         bool done = true;
-        *endIndex = -1;
-        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
+        *endPtr = NULL;
+        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
                 &sortable)) {
             if (last->unorderable()) {
                 continue;
             }
-            *tIndex = last->start();
-            *endIndex = last->end();
+            *startPtr = last->start();
+            *endPtr = last->end();
    #if TRY_ROTATE
             *chase.insert(0) = span;
    #else
@@ -40,7 +42,7 @@
             continue;
         }
         // find first angle, initialize winding to computed fWindSum
-        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
+        const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
         if (!angle) {
             continue;
         }
@@ -65,33 +67,25 @@
             SkTSwap<int>(sumMiWinding, sumSuWinding);
         }
         SkOpSegment* first = NULL;
-        bool badData = false;
-        while ((angle = angle->next()) != firstAngle && !badData) {
+        firstAngle = angle;
+        while ((angle = angle->next()) != firstAngle) {
             segment = angle->segment();
-            int start = angle->start();
-            int end = angle->end();
+            SkOpSpanBase* start = angle->start();
+            SkOpSpanBase* 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;
-                    *tIndex = start;
-                    *endIndex = end;
-                }
-                if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
-                        oppSumWinding, angle)) {
-                    badData = true;
-                    break;
+                    *startPtr = start;
+                    *endPtr = end;
                 }
                 // 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;
@@ -104,36 +98,8 @@
     return NULL;
 }
 
-/*
-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) {
+static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
+        const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
     bool firstContour = true;
     bool unsortable = false;
     bool topUnsortable = false;
@@ -141,12 +107,14 @@
     SkPoint lastTopLeft;
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        int index, endIndex;
+        SkOpSpanBase* start;
+        SkOpSpanBase* end;
         bool topDone;
         bool onlyVertical = false;
         lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
-                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
+        SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kBinarySingle,
+                &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
+                allocator);
         if (!current) {
             if ((!topUnsortable || firstPass) && !topDone) {
                 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
@@ -165,69 +133,65 @@
             break;
         }
         firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpan*> chase;
+        SkTDArray<SkOpSpanBase*> chase;
         do {
-            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
+            if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
                 do {
                     if (!unsortable && current->done()) {
                         break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    int nextStart = index;
-                    int nextEnd = endIndex;
+                    SkOpSpanBase* nextStart = start;
+                    SkOpSpanBase* nextEnd = end;
                     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(index, endIndex, simple, true);
+                            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(), current->xyAtT(index).fX, current->xyAtT(index).fY,
-                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
+                    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);
         #endif
-                    current->addCurveTo(index, endIndex, simple, true);
+                    current->addCurveTo(start, end, simple, true);
                     current = next;
-                    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);
+                    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);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
-                if (last && !last->fChased && !last->fLoop) {
-                    last->fChased = true;
+                SkOpSpanBase* last = current->markAndChaseDone(start, end);
+                if (last && !last->chased()) {
+                    last->setChased(true);
                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
                     *chase.append() = last;
 #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
-                            last->fSmall);
+                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+                    if (!last->final()) {
+                         SkDebugf(" windSum=%d", last->upCast()->windSum());
+                    }
+                    SkDebugf("\n");
 #endif
                 }
             }
-            current = findChaseOp(chase, &index, &endIndex);
+            current = findChaseOp(chase, &start, &end);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -291,16 +255,19 @@
     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 DEBUG_SHOW_TEST_NAME
+#endif    
+#if 0 && DEBUG_SHOW_TEST_NAME
     char* debugName = DEBUG_FILENAME_STRING;
     if (debugName && debugName[0]) {
         SkPathOpsDebug::BumpTestName(debugName);
@@ -321,53 +288,54 @@
     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
 #endif
     // turn path into list of segments
-    SkTArray<SkOpContour> contours;
-    // FIXME: add self-intersecting cubics' T values to segment
-    SkOpEdgeBuilder builder(*minuend, contours);
+    SkChunkAlloc allocator(4096);  // FIXME: add a constant expression here, tune
+    SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
     if (builder.unparseable()) {
         return false;
     }
     const int xorMask = builder.xorMask();
     builder.addOperand(*subtrahend);
-    if (!builder.finish()) {
+    if (!builder.finish(&allocator)) {
         return false;
     }
+#if !FORCE_RELEASE
+    contour.dumpSegments(op);
+#endif
+
     result->reset();
     result->setFillType(fillType);
     const int xorOpMask = builder.xorMask();
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
+    SkTDArray<SkOpContour* > contourList;
+    MakeContourList(&contour, 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) && nextPtr != listEnd);
+        } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd);
     } while (currentPtr != listEnd);
+#if DEBUG_VALIDATE
+    globalState.setPhase(SkOpGlobalState::kWalking);
+#endif
     // eat through coincident edges
-
-    int total = 0;
-    int index;
-    for (index = 0; index < contourList.count(); ++index) {
-        total += contourList[index]->segments().count();
-    }
-    if (!HandleCoincidence(&contourList, total)) {
+    if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
         return false;
     }
     // construct closed contours
     SkPathWriter wrapper(*result);
-    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
+    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index 7ddfbfb..2d07427 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -25,21 +25,25 @@
 
     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;
@@ -50,6 +54,7 @@
         return v;
     }
 
+    // only used by testing
     double cross(const SkDVector& a) const {
         return fX * a.fY - fY * a.fX;
     }
@@ -98,11 +103,13 @@
         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;
@@ -122,7 +129,7 @@
         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 AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
     bool approximatelyEqual(const SkPoint& a) const {
@@ -145,44 +152,10 @@
         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 AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+        return AlmostPequalUlps((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);
     }
@@ -209,7 +182,7 @@
         return result;
     }
 
-    bool moreRoughlyEqual(const SkDPoint& a) const {
+    bool roughlyEqual(const SkDPoint& a) const {
         if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
             return true;
         }
@@ -220,10 +193,6 @@
         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);
diff --git a/src/pathops/SkPathOpsPostSect.cpp b/src/pathops/SkPathOpsPostSect.cpp
old mode 100644
new mode 100755
index 15a1900..eb2d1ab
--- a/src/pathops/SkPathOpsPostSect.cpp
+++ b/src/pathops/SkPathOpsPostSect.cpp
@@ -17,8 +17,8 @@
     return segment()->contour();
 }
 
-SkOpDebugState* SkOpPtT::debugState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
+SkOpGlobalState* SkOpPtT::globalState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
 }
 
 void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -28,7 +28,7 @@
     fNext = this;
     fDuplicatePt = duplicate;
     fDeleted = false;
-    PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
+    PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
 }
 
 bool SkOpPtT::onEnd() const {
@@ -45,7 +45,7 @@
     do {
         SkOpPtT* next = prev->fNext;
         if (next == this) {
-            prev->removeNext();
+            prev->removeNext(this);
             fDeleted = true;
             return prev;
         }
@@ -55,14 +55,14 @@
     return NULL;
 }
 
-void SkOpPtT::removeNext() {
+void SkOpPtT::removeNext(SkOpPtT* kept) {
     SkASSERT(this->fNext);
     SkOpPtT* next = this->fNext;
     this->fNext = next->fNext;
     SkOpSpanBase* span = next->span();
     next->setDeleted();
     if (span->ptT() == next) {
-        span->upCast()->detach();
+        span->upCast()->detach(kept);
     }
 }
 
@@ -199,7 +199,7 @@
                 // omit aliases that alignment makes redundant
                 if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
                     SkASSERT(test->alias());
-                    prev->removeNext();
+                    prev->removeNext(ptT);
                     test = prev;
                 } else {
                     SkASSERT(ptT->alias());
@@ -239,8 +239,8 @@
     return segment()->contour();
 }
 
-SkOpDebugState* SkOpSpanBase::debugState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
+SkOpGlobalState* SkOpSpanBase::globalState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
 }
 
 void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -252,7 +252,7 @@
     fAligned = true;
     fChased = false;
     PATH_OPS_DEBUG_CODE(fCount = 1);
-    PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
+    PATH_OPS_DEBUG_CODE(fID = ++globalState()->fSpanID);
 }
 
 // this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -261,7 +261,7 @@
     SkOpPtT* spanPtT = span->ptT();
     SkASSERT(this->t() != spanPtT->fT);
     SkASSERT(!zero_or_one(spanPtT->fT));
-    span->detach();
+    span->detach(this->ptT());
     SkOpPtT* remainder = spanPtT->next();
     ptT()->insert(spanPtT);
     while (remainder != spanPtT) {
@@ -304,7 +304,7 @@
     return false;
 }
 
-void SkOpSpan::detach() {
+void SkOpSpan::detach(SkOpPtT* kept) {
     SkASSERT(!final());
     SkOpSpan* prev = this->prev();
     SkASSERT(prev);
@@ -313,6 +313,9 @@
     prev->setNext(next);
     next->setPrev(prev);
     this->segment()->detach(this);
+    if (this->coincident()) {
+        this->globalState()->fCoincidence->fixUp(this->ptT(), kept);
+    }
     this->ptT()->setDeleted();
 }
 
diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp
index c1d068a..4913c9f 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -8,7 +8,61 @@
 #include "SkLineParameters.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsQuad.h"
-#include "SkPathOpsTriangle.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];
+    }
+}
 
 // from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
 // (currently only used by testing)
@@ -43,10 +97,6 @@
     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];
@@ -140,7 +190,12 @@
     // FIXME: maybe it's possible to avoid this and compare non-normalized
     lineParameters.normalize();
     double distance = lineParameters.controlPtDistance(*this);
-    return approximately_zero(distance);
+    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);
 }
 
 SkDCubic SkDQuad::toCubic() const {
@@ -240,13 +295,6 @@
 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])}};
@@ -258,7 +306,6 @@
         SkASSERT(i.used() <= 2);
         b = SkDPoint::Mid(b0[1], b1[1]);
     }
-#endif
     if (t1 == 0 || t2 == 0) {
         align(0, &b);
     }
diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h
index 932c5fb..81638cf 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -17,43 +17,61 @@
 };
 
 struct SkDQuad {
-    SkDPoint fPts[3];
+    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;
+    }
 
     SkDQuad flip() const {
         SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
         return result;
     }
 
-    void set(const SkPoint pts[3]) {
+    static bool IsCubic() { return false; }
+
+    void set(const SkPoint pts[kPointCount]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
     }
 
-    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]; }
+    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]; }
 
     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;
-    bool pointInHull(const SkDPoint&) const;
+    void otherPts(int oddMan, const SkDPoint* endPt[2]) 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[3], double t1, double t2) {
+    static SkDQuad SubDivide(const SkPoint a[kPointCount], 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[3], const SkDPoint& a, const SkDPoint& c,
+    static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
                               double t1, double t2) {
         SkDQuad quad;
         quad.set(pts);
@@ -64,7 +82,8 @@
 
     // utilities callable by the user from the debugger when the implementation code is linked in
     void dump() const;
-    void dumpComma(const char*) const;
+    void dumpID(int id) const;
+    void dumpInner() const;
 
 private:
 //  static double Tangent(const double* quadratic, double t);  // uncalled
diff --git a/src/pathops/SkPathOpsQuadSect.h b/src/pathops/SkPathOpsQuadSect.h
deleted file mode 100644
index 57f1aa0..0000000
--- a/src/pathops/SkPathOpsQuadSect.h
+++ /dev/null
@@ -1,175 +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.
- */
-#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
diff --git a/src/pathops/SkPathOpsRect.cpp b/src/pathops/SkPathOpsRect.cpp
index 2ceed32..5dd3d8d 100644
--- a/src/pathops/SkPathOpsRect.cpp
+++ b/src/pathops/SkPathOpsRect.cpp
@@ -9,11 +9,6 @@
 #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]);
@@ -30,13 +25,6 @@
     }
 }
 
-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);
 }
@@ -56,10 +44,3 @@
         add(c.ptAtT(tValues[x]));
     }
 }
-
-void SkDRect::setRawBounds(const SkDCubic& cubic) {
-    set(cubic[0]);
-    for (int x = 1; x < 4; ++x) {
-        add(cubic[x]);
-    }
-}
diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h
index 2c47f43..2b37a5f 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -13,18 +13,10 @@
     double fLeft, fTop, fRight, fBottom;
 
     void add(const SkDPoint& pt) {
-        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;
-        }
+        fLeft = SkTMin(fLeft, pt.fX);
+        fTop = SkTMin(fTop, pt.fY);
+        fRight = SkTMax(fRight, pt.fX);
+        fBottom = SkTMax(fBottom, pt.fY);
     }
 
     bool contains(const SkDPoint& pt) const {
@@ -32,12 +24,15 @@
                 && approximately_between(fTop, pt.fY, fBottom);
     }
 
-    bool intersects(SkDRect* r) const {
+    bool intersects(const SkDRect& r) const {
+        if (fLeft > fRight) {
+            SkDebugf("!");
+        }
         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) {
@@ -53,11 +48,8 @@
         return fBottom - fTop;
     }
 
-    void setBounds(const SkDLine&);
     void setBounds(const SkDCubic&);
     void setBounds(const SkDQuad&);
-    void setRawBounds(const SkDCubic&);
-    void setRawBounds(const SkDQuad&);
 };
 
 #endif
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index 57090ac..7a234ec 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -5,11 +5,13 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
+static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
+        SkChunkAlloc* allocator) {
     bool firstContour = true;
     bool unsortable = false;
     bool topUnsortable = false;
@@ -17,15 +19,24 @@
     SkPoint lastTopLeft;
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        int index, endIndex;
+        SkOpSpanBase* start;
+        SkOpSpanBase* end;
         bool topDone;
         bool onlyVertical = false;
         lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
-                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
+        SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
+                &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
+                allocator);
         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;
             }
@@ -34,62 +45,66 @@
             break;
         }
         firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpan*> chase;
+        SkTDArray<SkOpSpanBase*> chase;
         do {
-            if (current->activeWinding(index, endIndex)) {
+            if (current->activeWinding(start, end)) {
                 do {
                     if (!unsortable && current->done()) {
                           break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    int nextStart = index;
-                    int nextEnd = endIndex;
+                    SkOpSpanBase* nextStart = start;
+                    SkOpSpanBase* nextEnd = end;
                     SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
                             &unsortable);
                     if (!next) {
                         if (!unsortable && simple->hasMove()
                                 && current->verb() != SkPath::kLine_Verb
                                 && !simple->isClosed()) {
-                            current->addCurveTo(index, endIndex, simple, true);
-                            SkASSERT(simple->isClosed());
+                            current->addCurveTo(start, end, simple, true);
+                    #if DEBUG_ACTIVE_SPANS
+                            if (!simple->isClosed()) {
+                                DebugShowActiveSpans(contourList);
+                            }
+                    #endif
                         }
                         break;
                     }
         #if DEBUG_FLOW
             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
-                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
+                    current->debugID(), start->pt().fX, start->pt().fY,
+                    end->pt().fX, end->pt().fY);
         #endif
-                    current->addCurveTo(index, endIndex, simple, true);
+                    current->addCurveTo(start, end, simple, true);
                     current = next;
-                    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);
+                    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);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
-                if (last && !last->fChased && !last->fLoop) {
-                    last->fChased = true;
+                SkOpSpanBase* last = current->markAndChaseDone(start, end);
+                if (last && !last->chased()) {
+                    last->setChased(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 windSum=%d small=%d\n", __FUNCTION__,
-                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
-                            last->fSmall);
+                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+                    if (!last->final()) {
+                         SkDebugf(" windSum=%d", last->upCast()->windSum());
+                    }
+                    SkDebugf("\n");
 #endif
                 }
             }
-            current = FindChase(&chase, &index, &endIndex);
+            current = FindChase(&chase, &start, &end);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -102,9 +117,11 @@
 }
 
 // returns true if all edges were processed
-static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
+static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
+        SkChunkAlloc* allocator) {
     SkOpSegment* current;
-    int start, end;
+    SkOpSpanBase* start;
+    SkOpSpanBase* end;
     bool unsortable = false;
     bool closable = true;
     while ((current = FindUndone(contourList, &start, &end))) {
@@ -115,34 +132,38 @@
             }
     #endif
             SkASSERT(unsortable || !current->done());
-            int nextStart = start;
-            int nextEnd = end;
+            SkOpSpanBase* nextStart = start;
+            SkOpSpanBase* 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);
-                    SkASSERT(simple->isClosed());
+            #if DEBUG_ACTIVE_SPANS
+                    if (!simple->isClosed()) {
+                        DebugShowActiveSpans(contourList);
+                    }
+            #endif
                 }
                 break;
             }
         #if DEBUG_FLOW
             SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                    current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
-                    current->xyAtT(end).fX, current->xyAtT(end).fY);
+                    current->debugID(), start->pt().fX, start->pt().fY,
+                    end->pt().fX, end->pt().fY);
         #endif
             current->addCurveTo(start, end, simple, true);
             current = next;
             start = nextStart;
             end = nextEnd;
-        } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
+        } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
         if (!simple->isClosed()) {
             SkASSERT(unsortable);
-            int min = SkMin32(start, end);
-            if (!current->done(min)) {
+            SkOpSpan* spanStart = start->starter(end);
+            if (!spanStart->done()) {
                 current->addCurveTo(start, end, simple, true);
-                current->markDone(min, 1);
+                current->markDone(spanStart);
             }
             closable = false;
         }
@@ -156,52 +177,68 @@
 
 // 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
-    SkTArray<SkOpContour> contours;
-    SkOpEdgeBuilder builder(path, contours);
-    if (!builder.finish()) {
+    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)) {
         return false;
     }
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, false, false);
-    SkOpContour** currentPtr = contourList.begin();
+#if !FORCE_RELEASE
+    contour.dumpSegments((SkPathOp) -1);
+#endif
     result->reset();
     result->setFillType(fillType);
+    SkTDArray<SkOpContour* > contourList;
+    MakeContourList(&contour, contourList, false, false);
+    SkOpContour** currentPtr = contourList.begin();
     if (!currentPtr) {
         return true;
     }
-    SkOpContour** listEnd = contourList.end();
+    if ((*currentPtr)->count() == 0) {
+        SkASSERT((*currentPtr)->next() == NULL);
+        return true;
+    }
+    SkOpContour** listEnd2 = 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) && nextPtr != listEnd);
-    } while (currentPtr != listEnd);
-    if (!HandleCoincidence(&contourList, 0)) {
+        } 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)) {
         return false;
     }
     // construct closed contours
-    SkPathWriter simple(*result);
-    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
-                : !bridgeXor(contourList, &simple))
+    SkPathWriter wrapper(*result);
+    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
+                : !bridgeXor(contourList, &wrapper, &allocator))
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
         SkPathWriter assembled(temp);
-        Assemble(simple, &assembled);
+        Assemble(wrapper, &assembled);
         *result = *assembled.nativePath();
         result->setFillType(fillType);
     }
diff --git a/src/pathops/SkPathOpsTCubicSect.cpp b/src/pathops/SkPathOpsTCubicSect.cpp
index 0b3ddd7..10a84a3 100644
--- a/src/pathops/SkPathOpsTCubicSect.cpp
+++ b/src/pathops/SkPathOpsTCubicSect.cpp
@@ -7,9 +7,9 @@
 
 #include "SkPathOpsTSect.h"
 
-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));
+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));
     SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
diff --git a/src/pathops/SkPathOpsTQuadSect.cpp b/src/pathops/SkPathOpsTQuadSect.cpp
index 46ce5cf..06b5f2f 100644
--- a/src/pathops/SkPathOpsTQuadSect.cpp
+++ b/src/pathops/SkPathOpsTQuadSect.cpp
@@ -7,9 +7,9 @@
 
 #include "SkPathOpsTSect.h"
 
-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));
+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));
     SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 4e7d3b17..5c76da7 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -6,15 +6,25 @@
  */
 
 #include "SkChunkAlloc.h"
+#include "SkPathOpsBounds.h"
 #include "SkPathOpsRect.h"
 #include "SkPathOpsQuad.h"
 #include "SkIntersections.h"
-#include "SkTArray.h"
+#include "SkTSort.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;
     }
@@ -54,41 +64,73 @@
 template<typename TCurve>
 class SkTSpan {
 public:
-    void init(const TCurve& );
-    void initBounds(const TCurve& );
-
+    void addBounded(SkTSpan* );
     double closestBoundedT(const SkDPoint& pt) const;
+    bool contains(double t) const;
 
-    bool contains(double t) const {
-        return !! const_cast<SkTSpan*>(this)->innerFind(t);
-    }
-
-    bool contains(const SkTSpan* span) 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;
 
     double endT() const {
         return fEndT;
     }
 
-    SkTSpan* find(double t) {
-        SkTSpan* result = innerFind(t);
+    SkTSpan* findOppSpan(const SkTSpan* opp) const;
+
+    SkTSpan* findOppT(double t) const {
+        SkTSpan* result = oppT(t);
         SkASSERT(result);
         return result;
     }
 
-    bool intersects(const SkTSpan* span, bool* check);
+    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();
+    }
 
     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);
     }
@@ -99,29 +141,23 @@
         return fStartT;
     }
 
-    bool tightBoundsIntersects(const SkTSpan* span) const;
+private:
 
     // implementation is for testing only
-    void dump() const {
-        dump(NULL);
+    int debugID() const {
+        return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
     }
 
-private:
-    SkTSpan* innerFind(double t);
-    bool linearIntersects(const TCurve& ) const;
+    void dumpID() 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;
+    int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart);
+    int linearIntersects(const TCurve& ) const;
+    SkTSpan* oppT(double t) const;
 
-#if DEBUG_T_SECT
     void validate() const;
-#endif
+    void validateBounded() const;
+    void validatePerpT(double oppT) const;
+    void validatePerpPt(double t, const SkDPoint& ) const;
 
     TCurve fPart;
     SkTCoincident<TCurve> fCoinStart;
@@ -136,23 +172,33 @@
     bool fCollapsed;
     bool fHasPerp;
     bool fIsLinear;
-#if DEBUG_T_SECT
-    int fDebugID;
-    bool fDebugDeleted;
-#endif
+    bool fIsLine;
+    bool fDeleted;
+    PATH_OPS_DEBUG_CODE(SkTSect<TCurve>* fDebugSect);
+    PATH_OPS_DEBUG_T_SECT_CODE(int fID);
     friend class SkTSect<TCurve>;
 };
 
 template<typename TCurve>
 class SkTSect {
 public:
-    SkTSect(const TCurve& c  PATH_OPS_DEBUG_PARAMS(int id));
+    SkTSect(const TCurve& c  PATH_OPS_DEBUG_T_SECT_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(const SkTSect& opp) const;
-    void dumpBoth(const SkTSect* opp) const;
+    void dumpBoth(SkTSect* ) const;
+    void dumpBounds(int id) const;
+    void dumpCoin() const;
+    void dumpCoinCurves() const;
     void dumpCurves() const;
 
 private:
@@ -163,36 +209,72 @@
         kOneS2Set = 8
     };
 
+    SkTSpan<TCurve>* addFollowing(SkTSpan<TCurve>* prior);
+    void addForPerp(SkTSpan<TCurve>* span, double t);
     SkTSpan<TCurve>* addOne();
-    bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
+    
+    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);
     SkTSpan<TCurve>* boundsMax() const;
     void coincidentCheck(SkTSect* sect2);
-    static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
-    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 removeSpan(SkTSpan<TCurve>* span);
-    void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
-    void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
-    void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
-    const SkTSpan<TCurve>* tail() const;
-    void trim(SkTSpan<TCurve>* span, SkTSect* opp);
+    bool coincidentHasT(double t);
+    void computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+    int countConsecutiveSpans(SkTSpan<TCurve>* first, SkTSpan<TCurve>** last) const;
 
-#if DEBUG_T_SECT
-    int debugID() const { return fDebugID; }
+    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);
+    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 removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
+    SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan);
+    SkTSpan<TCurve>* tail();
+    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;
-#else
-    int debugID() const { return 0; }
-#endif
+    void validateBounded() const;
+
     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
@@ -208,8 +290,8 @@
     SkIntersections i;
     int used = i.intersectRay(c2, perp);
     // only keep closest
-    if (used == 0) {
-        fPerpT = -1;
+    if (used == 0 || used == 3) {
+        this->clear();
         return;
     } 
     fPerpT = i[0][0];
@@ -223,6 +305,10 @@
             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) {
@@ -232,29 +318,55 @@
 }
 
 template<typename TCurve>
-void SkTSpan<TCurve>::init(const TCurve& c) {
-    fPrev = fNext = NULL;
-    fIsLinear = false;
-    fStartT = 0;
-    fEndT = 1;
-    initBounds(c);
+void SkTSpan<TCurve>::addBounded(SkTSpan* span) {
+    if (this->findOppSpan(span)) {
+        return;
+    }
+    fBounded.push_back() = span;
 }
 
 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;
-#if DEBUG_T_SECT
-    fDebugDeleted = false;
-    if (fCollapsed) {
-        SkDebugf("");  // for convenient breakpoints
+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;
+    }
+    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>
@@ -279,55 +391,143 @@
     return result;
 }
 
+#ifdef SK_DEBUG
 template<typename TCurve>
-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) {
+bool SkTSpan<TCurve>::debugIsBefore(const SkTSpan* span) const {
+    const SkTSpan* work = this;
+    do {
+        if (span == work) {
             return true;
         }
-    }
+    } while ((work = work->fNext));
+    return false;
+}
+#endif
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::contains(double t) const {
+    const SkTSpan* work = this;
+    do {
+        if (between(work->fStartT, t, work->fEndT)) {
+            return true;
+        }
+    } while ((work = work->fNext));
     return false;
 }
 
 template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
-    SkTSpan* work = this;
-    do {
-        if (between(work->fStartT, t, work->fEndT)) {
-            return work;
+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;
         }
-    } while ((work = work->fNext));
+    }
     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>
-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;
+int SkTSpan<TCurve>::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) {
+    if (!fBounds.intersects(opp->fBounds)) {
+        return 0;
     }
-    if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
-        if (!*check) {
-            return true;
-        }
-        fIsLinear = true;
+    int hullSect = this->hullCheck(opp, start, oppStart);
+    if (hullSect >= 0) {
+        return hullSect;
     }
-    if (fIsLinear) {
-        *check = false;
-        return linearIntersects(span->fPart);
+    hullSect = opp->hullCheck(this, oppStart, start);
+    if (hullSect >= 0) {
+        return hullSect;
     }
-    return *check; 
+    return -1;
 }
 
 template<typename TCurve>
-bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+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
+    }
+#endif
+}
+
+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 {
     // looks like q1 is near-linear
-    int start = 0, end = TCurve::kPointCount - 1;  // the outside points are usually the extremes
+    int start = 0, end = TCurve::kPointLast;  // 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) {
@@ -347,20 +547,116 @@
     double origY = fPart[start].fY;
     double adj = fPart[end].fX - origX;
     double opp = fPart[end].fY - origY;
-    double sign;
+    double maxPart = SkTMax(fabs(adj), fabs(opp));
+    double sign = 0;  // initialization to shut up warning in release build
     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(test)) {
-            return true;
+        if (precisely_zero_when_compared_to(test, maxVal)) {
+            return 1;
+        }
+        if (approximately_zero_when_compared_to(test, maxVal)) {
+            return 3;
         }
         if (n == 0) {
             sign = test;
             continue;
         }
         if (test * sign < 0) {
-            return true;
+            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;
+        }
+    }
+    SkASSERT(0);
     return false;
 }
 
@@ -380,6 +676,8 @@
     fPrev = work;
     fNext = work->fNext;
     fIsLinear = work->fIsLinear;
+    fIsLine = work->fIsLine;
+
     work->fNext = this;
     if (fNext) {
         fNext->fPrev = this;
@@ -393,102 +691,74 @@
 }
 
 template<typename TCurve>
-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;
-        }
-    } else {
-        r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
-        if (TCurve::IsCubic()) {
-            r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
-        }
-    }
-    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;  
-    }
-}
-
-#if DEBUG_T_SECT
-template<typename TCurve>
 void SkTSpan<TCurve>::validate() const {
+#if DEBUG_T_SECT
     SkASSERT(fNext == NULL || fNext != fPrev);
     SkASSERT(fNext == NULL || this == fNext->fPrev);
-    SkASSERT(fBounds.width() || fBounds.height());
+    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(fStartT <= fEndT);
     SkASSERT(fBounded.count() > 0);
-    for (int index = 0; index < fBounded.count(); ++index) {
-        const SkTSpan* overlap = fBounded[index];
-        SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
-        SkASSERT(overlap->contains(this));
+    this->validateBounded();
+    if (fHasPerp) {
+        if (fCoinStart.isCoincident()) {
+            validatePerpT(fCoinStart.perpT());
+            validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
+        }
+        if (fCoinEnd.isCoincident()) {
+            validatePerpT(fCoinEnd.perpT());
+            validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
+        }
     }
-}
 #endif
+}
 
 template<typename TCurve>
-SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
+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));
+    }
+#endif
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::validatePerpT(double oppT) const {
+#if DEBUG_VALIDATE
+    for (int index = 0; index < fBounded.count(); ++index) {
+        const SkTSpan* overlap = fBounded[index];
+        if (between(overlap->fStartT, oppT, overlap->fEndT)) {
+            return;
+        }
+    }
+    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))
     : fCurve(c)
     , fHeap(sizeof(SkTSpan<TCurve>) * 4)
+    , fCoincident(NULL)
     , fDeleted(NULL)
     , fActiveCount(0)
-    PATH_OPS_DEBUG_PARAMS(fDebugID(id))
-    PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
-    PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(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))
 {
     fHead = addOne();
     fHead->init(c);
@@ -508,22 +778,22 @@
 #endif
     }
     ++fActiveCount; 
-#if DEBUG_T_SECT
-    result->fDebugID = fDebugCount++ * 2 + fDebugID;
-#endif
+    PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
+    PATH_OPS_DEBUG_CODE(result->fDebugSect = this);
     return result;
 }
 
 template<typename TCurve>
-bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
+bool SkTSect<TCurve>::binarySearchCoin(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;
@@ -541,8 +811,11 @@
         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);
@@ -574,194 +847,472 @@
 SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
     SkTSpan<TCurve>* test = fHead;
     SkTSpan<TCurve>* largest = fHead;
-    bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
+    bool lCollapsed = largest->fCollapsed;
     while ((test = test->fNext)) {
-        bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
-        if ((largestCoin && !testCoin) || (largestCoin == testCoin
-                && (largest->fBoundsMax < test->fBoundsMax
-                || (largest->fCollapsed && !test->fCollapsed)))) {
+        bool tCollapsed = test->fCollapsed;
+        if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
+                largest->fBoundsMax < test->fBoundsMax)) {
             largest = test;
-            largestCoin = testCoin;
         }
     }
-    return largestCoin ? NULL : largest;
+    return largest;
 }
 
 template<typename TCurve>
 void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
     SkTSpan<TCurve>* first = fHead;
-    SkTSpan<TCurve>* next;
+    SkTSpan<TCurve>* last, * next;
     do {
-        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);
+        int consecutive = this->countConsecutiveSpans(first, &last);
+        next = last->fNext;
         if (consecutive < COINCIDENT_SPAN_COUNT) {
             continue;
         }
-        setPerp(sect2->fCurve, first, last);
+        this->validate();
+        sect2->validate();
+        this->computePerpendiculars(sect2, first, last);
+        this->validate();
+        sect2->validate();
         // check to see if a range of points are on the curve
-        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;
-        }
+        SkTSpan<TCurve>* coinStart = first;
+        do {
+            coinStart = this->extractCoincident(sect2, coinStart, last);
+        } while (coinStart && !last->fDeleted);
     } while ((first = next));
 }
 
 template<typename TCurve>
-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;
+bool SkTSect<TCurve>::coincidentHasT(double t) {
+    SkTSpan<TCurve>* test = fCoincident;
+    while (test) {
+        if (between(test->fStartT, t, test->fEndT)) {
+            return true;
+        }
+        test = test->fNext;
     }
-    if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
-        return true;
-    }
-    return span->tightBoundsIntersects(oppSpan);
+    return false;
 }
 
 template<typename TCurve>
-void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
-    SkTSpan<TCurve>* work = first;
-    first = NULL;
+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;
     do {
-        if (work->fCoinStart.isCoincident()) {
-            if (!first) {
-                first = work;
+        if (!work->fHasPerp && !work->fCollapsed) {
+            if (prior) {
+                work->fCoinStart = prior->fCoinEnd;
+            } else {
+                work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
             }
-        } else if (first) {
-            break;
+            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);
+                }
+            }
+            work->fHasPerp = true;
         }
         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;
+        return NULL;
     }
     // march outwards to find limit of coincidence from here to previous and next spans
     double startT = first->fStartT;
-    double oppT;
+    double oppStartT;
+    double oppEndT SK_INIT_TO_AVOID_WARNING;
     SkTSpan<TCurve>* prev = first->fPrev;
-    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);
+    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 {
-                    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();
+                    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;
+                } else {
+                    oppSpan->fStartT = oppSpan->fEndT;
+                }
+                *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("!");
+        if (closest == DBL_MAX) {
+            return 0;
         }
-//        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);
-                    }
-                }
+        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>
@@ -782,80 +1333,118 @@
 }
 
 template<typename TCurve>
-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;
+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;
         }
-    } else {
-        fHead = next;
-        if (next) {
-            next->fPrev = NULL;
+        if (bounded->fDeleted) {  // may have been deleted when opp did 'remove all but'
+            continue;
+        }
+        SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
+        if (bounded->removeBounded(span)) {
+            opp->removeSpan(bounded);
         }
     }
-    --fActiveCount;
-    span->fNext = fDeleted;
-    fDeleted = span;
-#if DEBUG_T_SECT
-    SkASSERT(!span->fDebugDeleted);
-    span->fDebugDeleted = true;
-#endif
+    SkASSERT(!span->fDeleted);
+    SkASSERT(span->findOppSpan(keep));
+    SkASSERT(keep->findOppSpan(span));
 }
 
 template<typename TCurve>
-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;
+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];
+#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));
+#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;
+    }
+    first->fNext = final;
+}
+
+template<typename TCurve>
 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];
-        removeOne(bounded, span);  // shuffles last into position 0
-        opp->removeOne(span, bounded);
+        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));
+
     }
 }
 
 template<typename TCurve>
-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);
+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;
     }
-    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);
+    *priorSpan = prev;
+    return test && test->fStartT <= t ? test : NULL;
 }
 
 template<typename TCurve>
-const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
-    const SkTSpan<TCurve>* result = fHead;
-    const SkTSpan<TCurve>* next = fHead;
+SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
+    SkTSpan<TCurve>* result = fHead;
+    SkTSpan<TCurve>* next = fHead;
     while ((next = next->fNext)) {
         if (next->fEndT > result->fEndT) {
             result = next;
@@ -869,32 +1458,75 @@
 template<typename TCurve>
 void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
     span->initBounds(fCurve);
-    int count = span->fBounded.count();
-    for (int index = 0; index < count; ) {
+    for (int index = 0; index < span->fBounded.count(); ) {
         SkTSpan<TCurve>* test = span->fBounded[index];
-        bool sects = intersects(span, opp, test);
-        if (sects) {
+        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);
+            }
             ++index;
         } else {
-            removeOne(test, span);
-            opp->removeOne(span, test);
-            --count;
+            if (span->removeBounded(test)) {
+                this->removeSpan(span);
+            }
+            if (test->removeBounded(span)) {
+                opp->removeSpan(test);
+            }
         }
     }
 }
 
-#if DEBUG_T_SECT
+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;
+        }
+    }
+}
+
+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;
+}
+
+
 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);
-        double last = 0;
+        SkDEBUGCODE(double last = 0);
         do {
             span->validate();
             SkASSERT(span->fStartT >= last);
-            last = span->fEndT;
+            SkDEBUGCODE(last = span->fEndT);
             ++count;
         } while ((span = span->fNext) != NULL);
     }
@@ -906,54 +1538,81 @@
         ++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;
-    // check for zero
-    if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+    if (sect1->fCurve[0] == sect2->fCurve[0]) {
         zeroOneSet |= kZeroS1Set | kZeroS2Set;
-        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])) {
+        intersections->insert(0, 0, sect1->fCurve[0]);
+    }
+    if (sect1->fCurve[0] == sect2->fCurve[TCurve::kPointLast]) {
         zeroOneSet |= kZeroS1Set | kOneS2Set;
-        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 (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+        intersections->insert(0, 1, sect1->fCurve[0]);
+    }
+    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) {
         zeroOneSet |= kOneS1Set | kZeroS2Set;
-        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])) {
+        intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
+    }
+    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[TCurve::kPointLast]) {
         zeroOneSet |= kOneS1Set | kOneS2Set;
-        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]);
-        }
+    }
+    // check for zero
+    if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
+            && 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])) {
+        zeroOneSet |= kZeroS1Set | kOneS2Set;
+        intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
+    }
+    // check for one
+    if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
+            && 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])) {
+        zeroOneSet |= kOneS1Set | kOneS2Set;
+        intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
+                sect2->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();
@@ -1033,14 +1692,14 @@
         fClosest.push_back().reset();
     }
 
-    void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
+    bool 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;
+            return false;
         }
         for (int index = 0; index < fUsed; ++index) {
             SkClosestRecord<TCurve>* test = &fClosest[index];
@@ -1050,37 +1709,46 @@
                 }
                 test->update(*record);
                 record->reset();
-                return;
+                return false;
             }
         }
         ++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) {
-            const SkClosestRecord<TCurve>& test = fClosest[index];
-            test.addIntersection(intersections);
+            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);
         }
     }
 
-    // this is oversized by one so that an extra record can merge into final one
-    SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
+    // this is oversized so that an extra records can merge into final one
+    SkSTArray<TCurve::kMaxIntersections * 2, 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);
+    intersections->setMax(TCurve::kMaxIntersections * 2);  // give extra for slop
     SkTSpan<TCurve>* span1 = sect1->fHead;
     SkTSpan<TCurve>* span2 = sect2->fHead;
-    bool check;
-    if (!span1->intersects(span2, &check)) {
+    int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
+//    SkASSERT(between(0, sect, 2));
+    if (!sect) {
         return;
     }
-    if (check) {
+    if (sect == 2 && oppSect == 2) {
         (void) EndsEqual(sect1, sect2, intersections);
         return;
     }
@@ -1096,12 +1764,12 @@
         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;
@@ -1110,50 +1778,52 @@
         }
         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 DEBUG_T_SECT
-        sect1->validate();
-        sect2->validate();
-#endif
-#if DEBUG_T_SECT_DUMP > 1
-        sect1->dumpBoth(*sect2);
+        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);
 #endif
         if (!sect1->fHead || !sect2->fHead) {
-            return;
+            break;
         }
     } while (true);
-    if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
-        // check for coincidence
-        SkTSpan<TCurve>* first = sect1->fHead;
+    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
         do {
-            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) {
+            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) {
                 intersections->clearCoincidence(index);
             }
-        } while ((first = first->fNext));
+        } while ((coincident = coincident->fNext));
+    }
+    if (!sect1->fHead || !sect2->fHead) {
+        return;
     }
     int zeroOneSet = EndsEqual(sect1, sect2, intersections);
     sect1->recoverCollapsed();
@@ -1163,33 +1833,41 @@
     const SkTSpan<TCurve>* head1 = result1;
     if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
         const SkDPoint& start1 = sect1->fCurve[0];
-        double t = head1->closestBoundedT(start1);
-        if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
-            intersections->insert(0, t, start1);
+        if (head1->isBounded()) {
+            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];
-        double t = head2->closestBoundedT(start2);
-        if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
-            intersections->insert(t, 0, start2);
+        if (head2->isBounded()) {
+            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];
-        double t = tail1->closestBoundedT(end1);
-        if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
-            intersections->insert(1, t, end1);
+        if (tail1->isBounded()) {
+            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];
-        double t = tail2->closestBoundedT(end2);
-        if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
-            intersections->insert(t, 1, end2);
+        if (tail2->isBounded()) {
+            double t = tail2->closestBoundedT(end2);
+            if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
+                intersections->insert(t, 1, end2);
+            }
         }
     }
     SkClosestSect<TCurve> closest;
@@ -1201,11 +1879,39 @@
             break;
         }
         SkTSpan<TCurve>* result2 = sect2->fHead;
+        bool found = false;
         while (result2) {
-            closest.find(result1, result2);
+            found |= 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);
 }
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index 0f63f39..d03efeb 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -8,14 +8,16 @@
 #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
-    SkTArray<SkOpContour> contours;
-    SkOpEdgeBuilder builder(path, contours);
-    if (!builder.finish()) {
+    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
+    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+    if (!builder.finish(&allocator)) {
         return false;
     }
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, false, false);
+    SkTDArray<SkOpContour* > contourList;
+    MakeContourList(&contour, contourList, false, false);
     SkOpContour** currentPtr = contourList.begin();
     result->setEmpty();
     if (!currentPtr) {
diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp
deleted file mode 100644
index 77845e0..0000000
--- a/src/pathops/SkPathOpsTriangle.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8cc8c6d..0000000
--- a/src/pathops/SkPathOpsTriangle.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 01fec0d..0248e71 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,111 @@
     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) {
@@ -92,6 +197,7 @@
 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;
@@ -141,12 +247,6 @@
     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;
 }
@@ -156,6 +256,10 @@
     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) {
@@ -304,7 +408,8 @@
 
 // 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));
+    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
+            || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
     return (a - b) * (c - b) <= 0;
 }
 
@@ -312,6 +417,15 @@
     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;
 }
@@ -324,7 +438,6 @@
 struct SkDVector;
 struct SkDLine;
 struct SkDQuad;
-struct SkDTriangle;
 struct SkDCubic;
 struct SkDRect;
 
diff --git a/src/pathops/SkQuarticRoot.cpp b/src/pathops/SkQuarticRoot.cpp
deleted file mode 100644
index f9a7bf5..0000000
--- a/src/pathops/SkQuarticRoot.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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
deleted file mode 100644
index 6ce0867..0000000
--- a/src/pathops/SkQuarticRoot.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * 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
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 6f06447..c19cd3d 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -272,6 +272,11 @@
 }
 
 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;
diff --git a/src/pathops/SkReduceOrder.h b/src/pathops/SkReduceOrder.h
index 4ff9a1d..397b58d 100644
--- a/src/pathops/SkReduceOrder.h
+++ b/src/pathops/SkReduceOrder.h
@@ -7,11 +7,9 @@
 #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 {