shape ops work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@5959 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/Intersection/EdgeDemo.cpp b/experimental/Intersection/EdgeDemo.cpp
index 7870764..973b981 100644
--- a/experimental/Intersection/EdgeDemo.cpp
+++ b/experimental/Intersection/EdgeDemo.cpp
@@ -220,7 +220,7 @@
     if (!closed) {
         tiny.close();
     }
-    if (false && show) {
+    if (show) {
         showPath(tiny, NULL);
         SkDebugf("simplified:\n");
     }
@@ -229,13 +229,13 @@
 
 static void tryRonco(const SkPath& path) {
     const SkRect& overall = path.getBounds();
-    const int divs = 4;
+    const int divs = 64;
     SkScalar cellWidth = overall.width() / divs * 2;
     SkScalar cellHeight = overall.height() / divs * 2;
     SkRect target;
     if (true) {
-        int xDiv = 1;
-        int yDiv = 2;
+        int xDiv = 28;
+        int yDiv = 17;
         target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
                 overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
                  cellWidth, cellHeight);
@@ -280,13 +280,14 @@
 #if 0
     for (int mask = 0; mask < 1 << testStrLen; ++mask) {
         char maskStr[testStrLen];
-        mask = 12;
+        mask = 15;
         for (int letter = 0; letter < testStrLen; ++letter) {
             maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
         }
         paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
    //     showPath(path, NULL);
    //     SkDebugf("%d simplified:\n", mask);
+        tryRonco(path);
         testSimplifyx(path);
     }
 #endif
diff --git a/experimental/Intersection/EdgeDemoApp.mm b/experimental/Intersection/EdgeDemoApp.mm
index 77e5e3e..b65a295 100644
--- a/experimental/Intersection/EdgeDemoApp.mm
+++ b/experimental/Intersection/EdgeDemoApp.mm
@@ -16,7 +16,7 @@
     };
 protected:
     virtual void onDraw(SkCanvas* canvas) {
-        static int step = 0 ; // 17904; // drawLetters first error
+        static int step = 0; // 12752; // 17908 ; // 17904; // drawLetters first error
                              // drawStars triggers error at 23275
                              // error is not easy to debug in its current state
         static double seconds;
diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp
index 69c464a..8a8cb30 100644
--- a/experimental/Intersection/Intersection_Tests.cpp
+++ b/experimental/Intersection/Intersection_Tests.cpp
@@ -14,9 +14,9 @@
 
 void Intersection_Tests() {
     int testsRun = 0;
+    SimplifyNew_Test();
     QuadraticIntersection_Test();
     MiniSimplify_Test();
-    SimplifyNew_Test();
     SimplifyAngle_Test();
     QuarticRoot_Test();
  //   QuadraticIntersection_Test();
diff --git a/experimental/Intersection/ShapeOps.cpp b/experimental/Intersection/ShapeOps.cpp
index 4058a16..f33c8b1 100644
--- a/experimental/Intersection/ShapeOps.cpp
+++ b/experimental/Intersection/ShapeOps.cpp
@@ -65,6 +65,7 @@
         int oppWinding = current->oppSign(index, endIndex);
         bool active = windingIsActive(winding, spanWinding, oppWinding, op);
         SkTDArray<Span*> chaseArray;
+        bool unsortable = false;
         do {
         #if DEBUG_WINDING
             SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
@@ -77,9 +78,12 @@
                 int nextStart = index;
                 int nextEnd = endIndex;
                 Segment* next = current->findNextOp(chaseArray, active,
-                        nextStart, nextEnd, winding, spanWinding, op,
+                        nextStart, nextEnd, winding, spanWinding, unsortable, op,
                         aXorMask, bXorMask);
                 if (!next) {
+                    // FIXME: if unsortable, allow partial paths to be later
+                    // assembled
+                    SkASSERT(!unsortable);
                     if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
                         lastPt = current->addCurveTo(index, endIndex, simple, true);
                         SkASSERT(*firstPt == lastPt);
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index ea71f58..dbb8988 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -49,7 +49,7 @@
 
 const bool gRunTestsInOneThread = true;
 
-#define DEBUG_ACTIVE_SPANS 0
+#define DEBUG_ACTIVE_SPANS 1
 #define DEBUG_ADD_INTERSECTING_TS 1
 #define DEBUG_ADD_T_PAIR 1
 #define DEBUG_ANGLE 1
@@ -481,6 +481,8 @@
     int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
     int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
     bool fDone; // if set, this span to next higher T has been processed
+    bool fUnsortableStart; // set when start is part of an unsortable pair
+    bool fUnsortableEnd; // set when end is part of an unsortable pair
 };
 
 // sorting angles
@@ -527,6 +529,14 @@
                 && !approximately_zero_squared(cmp)) {
             return cmp < 0;
         }
+        // at this point, the initial tangent line is coincident
+        if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
+            // FIXME: running demo will trigger this assertion
+            // (don't know if commenting out will trigger further assertion or not)
+            // commenting it out allows demo to run in release, though
+     //       SkASSERT(fSide != rh.fSide);
+            return fSide < rh.fSide;
+        }
         // see if either curve can be lengthened and try the tangent compare again
         if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
                 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
@@ -542,14 +552,6 @@
                 return longer < rhLonger;
             }
         }
-        // at this point, the initial tangent line is coincident
-        if (fSide * rh.fSide <= 0) {
-            // FIXME: running demo will trigger this assertion
-            // (don't know if commenting out will trigger further assertion or not)
-            // commenting it out allows demo to run in release, though
-     //       SkASSERT(fSide != rh.fSide);
-            return fSide < rh.fSide;
-        }
         SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
         SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
         // FIXME: until I can think of something better, project a ray from the
@@ -573,8 +575,14 @@
             roots = QuadRayIntersect(fPts, ray, i);
             rroots = QuadRayIntersect(rh.fPts, ray, ri);
         } while ((roots == 0 || rroots == 0) && (flip ^= true));
-        SkASSERT(roots > 0);
-        SkASSERT(rroots > 0);
+        if (roots == 0 || rroots == 0) {
+            // FIXME: we don't have a solution in this case. The interim solution
+            // is to mark the edges as unsortable, exclude them from this and
+            // future computations, and allow the returned path to be fragmented
+            fUnsortable = true;
+            rh.fUnsortable = true;
+            return this < &rh; // even with no solution, return a stable sort
+        }
         _Point loc;
         double best = SK_ScalarInfinity;
         double dx, dy, dist;
@@ -649,6 +657,7 @@
         fVerb = verb;
         fSpans = &spans;
         fReversed = false;
+        fUnsortable = false;
         setSpans();
     }
 
@@ -687,19 +696,23 @@
         return SkSign32(fStart - fEnd);
     }
 
+    const SkTDArray<Span>* spans() const {
+        return fSpans;
+    }
+
     int start() const {
         return fStart;
     }
+    
+    bool unsortable() const {
+        return fUnsortable;
+    }
 
 #if DEBUG_ANGLE
     const SkPoint* pts() const {
         return fPts;
     }
 
-    const SkTDArray<Span>* spans() const {
-        return fSpans;
-    }
-
     SkPath::Verb verb() const {
         return fVerb;
     }
@@ -720,18 +733,9 @@
     int fStart;
     int fEnd;
     bool fReversed;
+    mutable bool fUnsortable; // this alone is editable by the less than operator
 };
 
-static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
-    int angleCount = angles.count();
-    int angleIndex;
-    angleList.setReserve(angleCount);
-    for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
-        *angleList.append() = &angles[angleIndex];
-    }
-    QSort<Angle>(angleList.begin(), angleList.end() - 1);
-}
-
 // Bounds, unlike Rect, does not consider a line to be empty.
 struct Bounds : public SkRect {
     static bool Intersects(const Bounds& a, const Bounds& b) {
@@ -1131,6 +1135,8 @@
         if ((span->fDone = newT == 1)) {
             ++fDoneSpans;
         }
+        span->fUnsortableStart = false;
+        span->fUnsortableEnd = false;
         return insertedAt;
     }
 
@@ -1486,10 +1492,13 @@
         // OPTIMIZATION: check all angles to see if any have computed wind sum
         // before sorting (early exit if none)
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        bool sortable = SortAngles(angles, sorted);
 #if DEBUG_SORT
         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
 #endif
+        if (!sortable) {
+            return SK_MinS32;
+        }
         int angleCount = angles.count();
         const Angle* angle;
         const Segment* base;
@@ -1651,7 +1660,8 @@
     }
 
     Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
-            int& nextStart, int& nextEnd, int& winding, int& spanWinding, ShapeOp op,
+            int& nextStart, int& nextEnd, int& winding, int& spanWinding, 
+            bool& unsortable, ShapeOp op,
             const int aXorMask, const int bXorMask) {
         const int startIndex = nextStart;
         const int endIndex = nextEnd;
@@ -1706,13 +1716,17 @@
         addTwoAngles(startIndex, end, angles);
         buildAngles(end, angles);
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        bool sortable = SortAngles(angles, sorted);
         int angleCount = angles.count();
         int firstIndex = findStartingEdge(sorted, startIndex, end);
         SkASSERT(firstIndex >= 0);
     #if DEBUG_SORT
         debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
     #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
         SkASSERT(sorted[firstIndex]->segment() == this);
     #if DEBUG_WINDING
         SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
@@ -1883,7 +1897,8 @@
     // it is guaranteed to have an end which describes a non-zero length (?)
     // winding -1 means ccw, 1 means cw
     Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
-            int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
+            int& nextStart, int& nextEnd, int& winding, int& spanWinding,
+            bool& unsortable) {
         const int startIndex = nextStart;
         const int endIndex = nextEnd;
         int outerWinding = winding;
@@ -1937,13 +1952,17 @@
         addTwoAngles(startIndex, end, angles);
         buildAngles(end, angles);
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        bool sortable = SortAngles(angles, sorted);
         int angleCount = angles.count();
         int firstIndex = findStartingEdge(sorted, startIndex, end);
         SkASSERT(firstIndex >= 0);
     #if DEBUG_SORT
         debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
     #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
         SkASSERT(sorted[firstIndex]->segment() == this);
     #if DEBUG_WINDING
         SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
@@ -2068,7 +2087,7 @@
         return nextSegment;
     }
 
-    Segment* findNextXor(int& nextStart, int& nextEnd) {
+    Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
         const int startIndex = nextStart;
         const int endIndex = nextEnd;
         SkASSERT(startIndex != endIndex);
@@ -2126,13 +2145,17 @@
         addTwoAngles(startIndex, end, angles);
         buildAngles(end, angles);
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        bool sortable = SortAngles(angles, sorted);
         int angleCount = angles.count();
         int firstIndex = findStartingEdge(sorted, startIndex, end);
         SkASSERT(firstIndex >= 0);
     #if DEBUG_SORT
         debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
     #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
         SkASSERT(sorted[firstIndex]->segment() == this);
         int nextIndex = firstIndex + 1;
         int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
@@ -2302,6 +2325,12 @@
         }
     }
 
+ //   start here;
+    // either: 
+    // a) mark spans with either end unsortable as done, or
+    // b) rewrite findTop / findTopSegment / findTopContour to iterate further
+    //    when encountering an unsortable span
+
     // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
     // and use more concise logic like the old edge walker code?
     // FIXME: this needs to deal with coincident edges
@@ -2316,9 +2345,10 @@
         int count = fTs.count();
         // see if either end is not done since we want smaller Y of the pair
         bool lastDone = true;
+        bool lastUnsortableEnd;
         for (int index = 0; index < count; ++index) {
             const Span& span = fTs[index];
-            if (!span.fDone || !lastDone) {
+            if ((!span.fDone && !span.fUnsortableStart) || (!lastDone && !lastUnsortableEnd)) {
                 const SkPoint& intercept = xyAtT(&span);
                 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
                         && topPt.fX > intercept.fX)) {
@@ -2329,6 +2359,7 @@
                 }
             }
             lastDone = span.fDone;
+            lastUnsortableEnd = span.fUnsortableEnd;
         }
         // sort the edges to find the leftmost
         int step = 1;
@@ -2345,7 +2376,7 @@
         addTwoAngles(end, firstT, angles);
         buildAngles(firstT, angles);
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        (void) SortAngles(angles, sorted);
     #if DEBUG_SORT
         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
     #endif
@@ -2354,6 +2385,11 @@
         Segment* leftSegment;
         do {
             const Angle* angle = sorted[++firstT];
+            if (angle->unsortable()) {
+                // FIXME: if all angles are unsortable, find next topmost
+                SkASSERT(firstT < angles.count() - 1);
+                continue;
+            }
             leftSegment = angle->segment();
             tIndex = angle->end();
             endIndex = angle->start();
@@ -2687,6 +2723,33 @@
         fTs.reset();
     }
 
+    static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
+        int angleCount = angles.count();
+        int angleIndex;
+        angleList.setReserve(angleCount);
+        for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+            *angleList.append() = &angles[angleIndex];
+        }
+        QSort<Angle>(angleList.begin(), angleList.end() - 1);
+        bool result = true;
+        for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+            Angle& angle = angles[angleIndex];
+            if (angle.unsortable()) {
+                // so that it is available for early exclusion in findTop and others
+                const SkTDArray<Span>* spans = angle.spans();
+                Span* span = const_cast<Span*>(&(*spans)[angle.start()]);
+                if (angle.start() < angle.end()) {
+                    span->fUnsortableStart = true;
+                } else {
+                    --span;
+                    span->fUnsortableEnd = true;
+                }
+                result = false;
+            }
+        }
+        return result;
+    }
+
     // OPTIMIZATION: mark as debugging only if used solely by tests
     const Span& span(int tIndex) const {
         return fTs[tIndex];
@@ -2968,9 +3031,10 @@
                 lastSum = windSum;
                 windSum -= segment.spanSign(&angle);
             }
-            SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
+            SkDebugf("%s [%d] %s id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
                     " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
-                    __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
+                    __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE ***" : "",
+                    segment.fID, kLVerbStr[segment.fVerb],
                     start, segment.xAtT(&sSpan),
                     segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
                     segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
@@ -4075,7 +4139,7 @@
         // returns the first counterclockwise hour before 6 o'clock,
         // or if the base point is rightmost, returns the first clockwise
         // hour after 6 o'clock
-        sortAngles(angles, sorted);
+        (void) Segment::SortAngles(angles, sorted);
 #if DEBUG_SORT
         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
 #endif
@@ -4089,6 +4153,9 @@
         bool baseMatches = test->yAtT(tIndex) == basePt.fY;
         for (int index = 0; index < count; ++index) {
             angle = sorted[index];
+            if (angle->unsortable()) {
+                continue;
+            }
             if (baseMatches && angle->isHorizontal()) {
                 continue;
             }
@@ -4235,10 +4302,14 @@
             continue;
         }
         SkTDArray<Angle*> sorted;
-        sortAngles(angles, sorted);
+        bool sortable = Segment::SortAngles(angles, sorted);
 #if DEBUG_SORT
         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
 #endif
+        if (!sortable) {
+            chase.pop(&span);
+            continue;
+        }
         // find first angle, initialize winding to computed fWindSum
         int firstIndex = -1;
         const Angle* angle;
@@ -4331,8 +4402,10 @@
 // is an option, choose first edge that continues the inside.
     // since we start with leftmost top edge, we'll traverse through a
     // smaller angle counterclockwise to get to the next edge.
-static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
+// returns true if all edges were processed
+static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
     bool firstContour = true;
+    bool unsortable = false;
     do {
         Segment* topStart = findTopContour(contourList);
         if (!topStart) {
@@ -4392,11 +4465,11 @@
         #endif
             const SkPoint* firstPt = NULL;
             do {
-                SkASSERT(!current->done());
+                SkASSERT(unsortable || !current->done());
                 int nextStart = index;
                 int nextEnd = endIndex;
                 Segment* next = current->findNextWinding(chaseArray, active,
-                        nextStart, nextEnd, winding, spanWinding);
+                        nextStart, nextEnd, winding, spanWinding, unsortable);
                 if (!next) {
                     if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
                         lastPt = current->addCurveTo(index, endIndex, simple, true);
@@ -4443,19 +4516,22 @@
             active = windingIsActive(winding, spanWinding);
         } while (true);
     } while (true);
+    return !unsortable;
 }
 
-static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
+// returns true if all edges were processed
+static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
     Segment* current;
     int start, end;
+    bool unsortable = false;
     while ((current = findUndone(contourList, start, end))) {
         const SkPoint* firstPt = NULL;
         SkPoint lastPt;
         do {
-            SkASSERT(!current->done());
+            SkASSERT(unsortable || !current->done());
             int nextStart = start;
             int nextEnd = end;
-            Segment* next = current->findNextXor(nextStart, nextEnd);
+            Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
             if (!next) {
                 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
                     lastPt = current->addCurveTo(start, end, simple, true);
@@ -4481,6 +4557,7 @@
         debugShowActiveSpans(contourList);
     #endif
     }
+    return !unsortable;
 }
 
 static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
@@ -4503,6 +4580,11 @@
     QSort<Contour>(list.begin(), list.end() - 1);
 }
 
+static void assemble(SkPath& simple) {
+    // TODO: find the non-closed paths and connect them together
+    SkASSERT(0);
+}
+
 void simplifyx(const SkPath& path, SkPath& simple) {
     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
     simple.reset();
@@ -4533,10 +4615,11 @@
     coincidenceCheck(contourList);
     fixOtherTIndex(contourList);
     // construct closed contours
-    if (builder.xorMask() == kWinding_Mask) {
-        bridgeWinding(contourList, simple);
-    } else {
-        bridgeXor(contourList, simple);
+    if (builder.xorMask() == kWinding_Mask
+                ? !bridgeWinding(contourList, simple)
+                : !bridgeXor(contourList, simple)) 
+    { // if some edges could not be resolved, assemble remaining fragments
+        assemble(simple);
     }
 }
 
diff --git a/experimental/Intersection/SimplifyFindNext_Test.cpp b/experimental/Intersection/SimplifyFindNext_Test.cpp
index 199ba1d..b6f5d1e 100644
--- a/experimental/Intersection/SimplifyFindNext_Test.cpp
+++ b/experimental/Intersection/SimplifyFindNext_Test.cpp
@@ -35,8 +35,10 @@
     int nextStart = startIndex;
     int nextEnd = endIndex;
     SkTDArray<SimplifyFindNextTest::Span*> chaseArray;
+    bool unsortable = false;
     SimplifyFindNextTest::Segment* next = segment.findNextWinding(chaseArray,
-            true, nextStart, nextEnd, contourWinding, spanWinding);
+            true, nextStart, nextEnd, contourWinding, spanWinding,
+            unsortable);
     pts[1] = next->xyAtT(&next->span(nextStart));
     SkASSERT(pts[0] == pts[1]);
     return next;
diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp
index d2f8b2c..9841176 100644
--- a/experimental/Intersection/SimplifyNew_Test.cpp
+++ b/experimental/Intersection/SimplifyNew_Test.cpp
@@ -2828,7 +2828,7 @@
     testSimplifyx(path);
 }
 
-static void (*firstTest)() = testLine73x;
+static void (*firstTest)() = testQuadratic7;
 
 static struct {
     void (*fun)();
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
index d6ff6f9..eb737ab 100644
--- a/experimental/Intersection/op.htm
+++ b/experimental/Intersection/op.htm
@@ -2274,11 +2274,56 @@
 path.quadTo(388.299255,136.674606, 380.294495,140.44487);
 </div>
 
+<div id="testQuadratic47o">
+path.moveTo(343.939362, 212.598053);
+path.lineTo(378.457642, 118.940636);
+path.lineTo(383.692657, 141.516571);
+path.lineTo(350.319519, 231.902115);
+path.lineTo(343.939362, 212.598053);
+path.close();
+path.moveTo(325.429016, 162.047577);
+path.quadTo(336.348907, 149.123688, 353.36264, 149.123688);
+path.quadTo(369.476624, 149.123688, 378.269806, 160.575241);
+path.lineTo(325.429016, 162.047577);
+path.close();
+path.moveTo(370.867188, 186.014069);
+path.quadTo(370.867188, 161.229614, 352.381104, 161.229614);
+path.quadTo(333.813202, 161.229614, 331.686493, 186.014069);
+path.lineTo(370.867188, 186.014069);
+path.close();
+path.moveTo(353.161499, 195.011719);
+path.quadTo(353.161499, 174.726105, 363.876862, 161.96579);
+path.lineTo(353.161499, 195.011719);
+path.close();
+</div>
+
+<div id="testQuadratic47s">
+path.moveTo(366.466309, 151.476364);
+path.lineTo(378.457642,118.940636);
+path.lineTo(383.692657,141.516571);
+path.lineTo(377.159943,159.209305);
+path.quadTo(377.728729,159.87059, 378.269806,160.575241);
+path.lineTo(376.638824,160.620682);
+path.lineTo(370.26593,177.8806);
+path.quadTo(368.708496,168.390671, 363.116943,164.309357);
+path.lineTo(356.079041,186.014069);
+path.lineTo(367.262817,186.014069);
+path.lineTo(350.319519,231.902115);
+path.lineTo(343.939362,212.598053);
+path.lineTo(353.736816,186.014923);
+path.lineTo(353.737122,186.014069);
+path.lineTo(353.736938,186.014069);
+path.quadTo(353.736877,186.014496, 353.736816,186.014923);
+path.quadTo(353.161499,190.31131, 353.161499,195.011719);
+</div>
+
 </div>
 
 <script type="text/javascript">
 
 var testDivs = [
+    testQuadratic47o,
+    testQuadratic47s,
     testQuadratic46o,
     testQuadratic46s,
     testQuadratic45o,
diff --git a/gyp/shapeops_demo.gyp b/gyp/shapeops_demo.gyp
index b6718cb..1279294 100644
--- a/gyp/shapeops_demo.gyp
+++ b/gyp/shapeops_demo.gyp
@@ -76,7 +76,7 @@
         'pdf.gyp:pdf',
       ],
       'conditions' : [
-       [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
         }],
         [ 'skia_os == "win"', {
         }],
diff --git a/gyp/shapeops_edge.gyp b/gyp/shapeops_edge.gyp
index 11fe4b2..b3f63f4 100644
--- a/gyp/shapeops_edge.gyp
+++ b/gyp/shapeops_edge.gyp
@@ -55,6 +55,7 @@
         '../experimental/Intersection/LineQuadraticIntersection.cpp',
         '../experimental/Intersection/LineQuadraticIntersection_Test.cpp',
         '../experimental/Intersection/LineUtilities.cpp',
+        '../experimental/Intersection/MiniSimplify_Test.cpp',
         '../experimental/Intersection/QuadraticBezierClip.cpp',
         '../experimental/Intersection/QuadraticBezierClip_Test.cpp',
         '../experimental/Intersection/QuadraticBounds.cpp',