shape ops work in progress
authorcaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 10 Apr 2012 18:28:55 +0000 (18:28 +0000)
committercaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 10 Apr 2012 18:28:55 +0000 (18:28 +0000)
more quadratics work

git-svn-id: http://skia.googlecode.com/svn/trunk@3643 2bbb7eff-a529-9590-31e7-b0007b416f81

experimental/Intersection/EdgeWalker.cpp
experimental/Intersection/EdgeWalkerQuadratics_Test.cpp
experimental/Intersection/EdgeWalker_TestUtility.cpp
experimental/Intersection/edge.xcodeproj/project.pbxproj
experimental/Intersection/op.htm

index 70fa8e6..f3dff48 100644 (file)
@@ -16,7 +16,7 @@
 #include "ShapeOps.h"
 #include "TSearch.h"
 
-#if 0 // set to 1 for no debugging whatsoever
+#if 01 // set to 1 for no debugging whatsoever
 const bool gShowDebugf = false; // FIXME: remove once debugging is complete
 
 #define DEBUG_DUMP 0
@@ -25,13 +25,15 @@ const bool gShowDebugf = false; // FIXME: remove once debugging is complete
 #define DEBUG_ADD_BOTTOM_TS 0
 #define DEBUG_ABOVE_BELOW 0
 #define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ASSEMBLE 0
+#define DEBUG_BRIDGE 0
 #define DEBUG_SORT_HORIZONTAL 0
 #define DEBUG_OUT 0
 #define DEBUG_OUT_LESS_THAN 0
 #define DEBUG_ADJUST_COINCIDENT 0
 #define DEBUG_BOTTOM 0
 #define DEBUG_SPLIT 0
-
+#define DEBUG_STITCH_EDGE 0
 #else
 const bool gShowDebugf = true; // FIXME: remove once debugging is complete
 
@@ -41,15 +43,25 @@ const bool gShowDebugf = true; // FIXME: remove once debugging is complete
 #define DEBUG_ADD_BOTTOM_TS 0
 #define DEBUG_ABOVE_BELOW 01
 #define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ASSEMBLE 1
+#define DEBUG_BRIDGE 1
 #define DEBUG_SORT_HORIZONTAL 01
 #define DEBUG_OUT 01
 #define DEBUG_OUT_LESS_THAN 0
 #define DEBUG_ADJUST_COINCIDENT 1
 #define DEBUG_BOTTOM 0
 #define DEBUG_SPLIT 1
+#define DEBUG_STITCH_EDGE 1
 
 #endif
 
+#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
+static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
+#endif
+#if DEBUG_STITCH_EDGE
+static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
+#endif
+
 static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
         Intersections& intersections) {
     const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
@@ -196,7 +208,29 @@ static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
     sub[3].fX = SkDoubleToScalar(dst[3].x);
     sub[3].fY = SkDoubleToScalar(dst[3].y);
 }
+
+static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[3];
+    QuadSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 3; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+
+static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[4];
+    CubicSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 4; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+
 /*
 list of edges
 bounds for edge
@@ -336,49 +370,57 @@ public:
                 break;
             }
             int closeEdgeIndex = -listIndex - 1;
+            // the curve is deferred and not added right away because the
+            // following edge may extend the first curve.
             SkPoint firstPt, lastCurve[4];
             uint8_t lastVerb;
+#if DEBUG_ASSEMBLE
+            int firstIndex, lastIndex;
+            const int tab = 8;
+#endif
             bool doMove = true;
             int edgeIndex;
             do {
                 SkPoint* ptArray = fEdges[listIndex].fPts;
                 uint8_t verb = fEdges[listIndex].fVerb;
-                SkPoint* start, * end;
+                SkPoint* curve[4];
                 if (advance < 0) {
-                    start = &ptArray[verb];
-                    end = &ptArray[0];
+                    curve[0] = &ptArray[verb];
+                    if (verb == SkPath::kCubic_Verb) {
+                        curve[1] = &ptArray[2];
+                        curve[2] = &ptArray[1];
+                    }
+                    curve[verb] = &ptArray[0];
                 } else {
-                    start = &ptArray[0];
-                    end = &ptArray[verb];
+                    curve[0] = &ptArray[0];
+                    if (verb == SkPath::kCubic_Verb) {
+                        curve[1] = &ptArray[1];
+                        curve[2] = &ptArray[2];
+                    }
+                    curve[verb] = &ptArray[verb];
+                }
+                if (verb == SkPath::kQuad_Verb) {
+                    curve[1] = &ptArray[1];
                 }
                 if (doMove) {
-                    firstPt = *start;
-                    simple.moveTo(start->fX, start->fY);
-                    if (gShowDebugf) {
-                        SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
-                                start->fX, start->fY);
-                    }
-                    lastCurve[0] = *start;
-                    if (verb == SkPath::kQuad_Verb) {
-                        lastCurve[1] = ptArray[1];
-                    } else if (verb == SkPath::kCubic_Verb) {
-                        if (advance < 0) {
-                            lastCurve[1] = ptArray[2];
-                            lastCurve[2] = ptArray[1];
-                        } else {
-                            lastCurve[1] = ptArray[1];
-                            lastCurve[2] = ptArray[2];
-                        }
+                    firstPt = *curve[0];
+                    simple.moveTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
+                            listIndex + 1, curve[0]->fX, curve[0]->fY);
+                    firstIndex = listIndex;
+#endif
+                    for (int index = 0; index <= verb; ++index) {
+                        lastCurve[index] = *curve[index];
                     }
-                    lastCurve[verb] = *end;
-                    lastVerb = verb;
                     doMove = false;
                 } else {
-                    bool gap = lastCurve[verb] != *start;
-                    if (gap) {
+                    bool gap = lastCurve[lastVerb] != *curve[0];
+                    if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
                         // FIXME: see comment in bridge -- this probably
                         // conceals errors
-                        SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10);
+                        SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
+                                curve[0]->fY) <= 10);
                         switch (lastVerb) {
                             case SkPath::kLine_Verb:
                                 simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
@@ -393,43 +435,41 @@ public:
                                         lastCurve[3].fX, lastCurve[3].fY);
                                 break;
                         }
-                        if (gShowDebugf) {
-                            const char* verbStr[] = {"", "line", "quad", "cubic"};
-                            SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__,
-                                    verbStr[lastVerb], lastCurve[lastVerb].fX,
-                                    lastCurve[lastVerb].fY);
-                        }
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
+                                kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
+                                lastCurve[lastVerb].fY);
+#endif
                     }
-                    if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) {
+                    int firstCopy = 1;
+                    if (gap || (lastVerb == SkPath::kLine_Verb &&
+                            !extendLine(lastCurve, *curve[verb]))) {
                         // FIXME: see comment in bridge -- this probably
                         // conceals errors
-                        SkASSERT(lastCurve[lastVerb] == *start ||
-                                (fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10));
-                        simple.lineTo(start->fX, start->fY);
-                        if (gShowDebugf) {
-                            SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
-                                    start->fX, start->fY);
-                        }
-                        lastCurve[0] = *start;
+                        SkASSERT(lastCurve[lastVerb] == *curve[0] ||
+                                (fFill && UlpsDiff(lastCurve[lastVerb].fY,
+                                curve[0]->fY) <= 10));
+                        simple.lineTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
+                                lastIndex + 1, curve[0]->fX, curve[0]->fY);
+#endif
+                        firstCopy = 0;
+                    } else if (lastVerb != SkPath::kLine_Verb) {
+                        firstCopy = 0;
                     }
-                    if (verb == SkPath::kQuad_Verb) {
-                        lastCurve[1] = ptArray[1];
-                    } else if (verb == SkPath::kCubic_Verb) {
-                        if (advance < 0) {
-                            lastCurve[1] = ptArray[2];
-                            lastCurve[2] = ptArray[1];
-                        } else {
-                            lastCurve[1] = ptArray[1];
-                            lastCurve[2] = ptArray[2];
-                        }
+                    for (int index = firstCopy; index <= verb; ++index) {
+                        lastCurve[index] = *curve[index];
                     }
-                    lastCurve[verb] = *end;
-                    lastVerb = verb;
                 }
+                lastVerb = verb;
+#if DEBUG_ASSEMBLE
+                lastIndex = listIndex;
+#endif
                 if (advance < 0) {
                     edgeIndex = fTops[listIndex];
                     fTops[listIndex] = 0;
-                 } else {
+                } else {
                     edgeIndex = fBottoms[listIndex];
                     fBottoms[listIndex] = 0;
                 }
@@ -442,37 +482,44 @@ public:
                     }
                 }
                 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
+                    switch (lastVerb) {
+                        case SkPath::kLine_Verb:
+                            simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
+                            break;
+                        case SkPath::kQuad_Verb:
+                            simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
+                                    lastCurve[2].fX, lastCurve[2].fY);
+                            break;
+                        case SkPath::kCubic_Verb:
+                            simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
+                                    lastCurve[2].fX, lastCurve[2].fY,
+                                    lastCurve[3].fX, lastCurve[3].fY);
+                            break;
+                    }
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
+                            lastIndex + 1, kLVerbStr[lastVerb],
+                            lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
+#endif
                     if (lastCurve[lastVerb] != firstPt) {
-                        switch (lastVerb) {
-                            case SkPath::kLine_Verb:
-                                simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
-                                break;
-                            case SkPath::kQuad_Verb:
-                                simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
-                                        lastCurve[2].fX, lastCurve[2].fY);
-                                break;
-                            case SkPath::kCubic_Verb:
-                                simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
-                                        lastCurve[2].fX, lastCurve[2].fY,
-                                        lastCurve[3].fX, lastCurve[3].fY);
-                                break;
-                        }
-                        if (gShowDebugf) {
-                            const char* verbStr[] = {"", "line", "quad", "cubic"};
-                            SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__,
-                                    verbStr[lastVerb],
-                                    lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
-                        }
+                        simple.lineTo(firstPt.fX, firstPt.fY);
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
+                                firstIndex + 1, firstPt.fX, firstPt.fY);
+#endif
                     }
-                    simple.lineTo(firstPt.fX, firstPt.fY);
                     simple.close();
-                    if (gShowDebugf) {
-                        SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
-                                firstPt.fX, firstPt.fY);
-                    }
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%*s   close\n", tab, "");
+#endif
                     break;
                 }
-                // if this and next edge go different directions 
+                // if this and next edge go different directions
+#if DEBUG_ASSEMBLE
+                SkDebugf("%*s   advance=%d edgeIndex=%d flip=%s\n", tab, "",
+                        advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
+                        "true" : "false");
+#endif
                 if (advance > 0 ^ edgeIndex < 0) {
                     advance = -advance;
                 }
@@ -618,6 +665,22 @@ public:
         }
         SkASSERT(fMismatches.count() == 0);
 #endif
+#if DEBUG_BRIDGE
+    for (index = 0; index < count; ++index) {
+        const OutEdge& edge = fEdges[index];
+        uint8_t verb = edge.fVerb;
+        SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n", 
+                index == 0 ? __FUNCTION__ : "      ",
+                index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
+                edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
+    }
+    for (index = 0; index < count; ++index) {
+        SkDebugf("       top    of % 2d connects to %s of % 2d\n", index + 1,
+                fTops[index] < 0 ? "top   " : "bottom", abs(fTops[index]));
+        SkDebugf("       bottom of % 2d connects to %s of % 2d\n", index + 1,
+                fBottoms[index] < 0 ? "top   " : "bottom", abs(fBottoms[index]));
+    }
+#endif
     }
 
 protected:
@@ -696,6 +759,8 @@ public:
                 className, fTopIntercepts);
         SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
                 className, fBottomIntercepts);
+        SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
+                className, fExplicit);
     }
 #endif
 
@@ -781,7 +846,7 @@ struct InEdge {
         return insertedAt;
     }
     
-    void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd,
+    void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
             int verbStart, int verbEnd) {
         InEdge* edge = edges.push_back_n(1);
         int verbCount = verbEnd - verbStart;
@@ -793,25 +858,35 @@ struct InEdge {
         edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
         edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
         edge->setBounds();
-        edge->fID = id + edges.count();
         edge->fWinding = fWinding;
         edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
     }
 
-    void addSplit(SkTArray<InEdge>& edges, int id, SkPoint* pts, uint8_t verb,
-            double* ts, int tCount, bool flipped) {
+    void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
+            Intercepts& intercepts, int firstT, int lastT, bool flipped) {
         InEdge* edge = edges.push_back_n(1);
         edge->fIntercepts.push_back_n(1);
-        edge->fIntercepts[0].fTs.append(tCount, ts);
+        if (firstT == 0) {
+            *edge->fIntercepts[0].fTs.append() = 0;
+        } else {
+            *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
+        }
+        bool add1 = lastT == intercepts.fTs.count();
+        edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
+        if (add1) {
+            *edge->fIntercepts[0].fTs.append() = 1;
+        }
         edge->fIntercepts[0].fExplicit = true;
-        edge->fPts.append(verb, pts);
+        edge->fPts.append(verb + 1, pts);
         edge->fVerbs.append(1, &verb);
-        edge->setBounds();
-        edge->fID = id + edges.count();
-        edge->fWinding = fWinding;
+        // FIXME: bounds could be better for partial Ts
+        edge->setSubBounds();
         edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
         if (flipped) {
-            flip();
+            edge->flipTs();
+            edge->fWinding = -fWinding;
+        } else {
+            edge->fWinding = fWinding;
         }
     }
 
@@ -831,17 +906,16 @@ struct InEdge {
         return false;
     }
 
-    void complete(signed char winding, int id) {
+    void complete(signed char winding) {
         setBounds();
         fIntercepts.push_back_n(fVerbs.count());
         if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
             flip();
         }
         fContainsIntercepts = fIntersected = false;
-        fID = id;
     }
     
-     void flip() {
+    void flip() {
         size_t index;
         size_t last = fPts.count() - 1;
         for (index = 0; index < last; ++index, --last) {
@@ -852,6 +926,18 @@ struct InEdge {
             SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
         }
     }
+    
+    void flipTs() {
+        SkASSERT(fIntercepts.count() == 1);
+        Intercepts& intercepts = fIntercepts[0];
+        SkASSERT(intercepts.fExplicit);
+        SkTDArray<double>& ts = intercepts.fTs;
+        size_t index;
+        size_t last = ts.count() - 1;
+        for (index = 0; index < last; ++index, --last) {
+            SkTSwap<double>(ts[index], ts[last]);
+        }
+    }
 
     void reset() {
         fCached.reset();
@@ -859,7 +945,6 @@ struct InEdge {
         fPts.reset();
         fVerbs.reset();
         fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
-        fID = -1;
         fWinding = 0;
         fContainsIntercepts = false;
         fIntersected = false;
@@ -881,8 +966,25 @@ struct InEdge {
             ++ptPtr;
         }
     }
+    
+    // recompute bounds based on subrange of T values
+    void setSubBounds() {
+        SkASSERT(fIntercepts.count() == 1);
+        Intercepts& intercepts = fIntercepts[0];
+        SkASSERT(intercepts.fExplicit);
+        SkASSERT(fVerbs.count() == 1);
+        SkTDArray<double>& ts = intercepts.fTs;
+        if (fVerbs[0] == SkPath::kQuad_Verb) {
+            SkASSERT(fPts.count() == 3);
+            QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+        } else {
+            SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
+            SkASSERT(fPts.count() == 4);
+            CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+        }
+    }
 
-    void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) {
+    void splitInflectionPts(SkTArray<InEdge>& edges) {
         if (!fIntersected) {
             return;
         }
@@ -918,11 +1020,9 @@ struct InEdge {
                         int nextPt = pts - fPts.begin();
                         int nextVerb = verbs - 1 - fVerbs.begin();
                         if (lastVerb < nextVerb) {
-                            addPartial(edges, idStart, lastPt, nextPt,
-                                    lastVerb, nextVerb);
+                            addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
             #if DEBUG_SPLIT
-                            SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__,
-                                    fID);
+                            SkDebugf("%s addPartial 1\n", __FUNCTION__);
             #endif
                         }
                         lastPt = nextPt;
@@ -931,20 +1031,18 @@ struct InEdge {
                 } else {
                     if (firstSplit >= 0) {
                         if (lastSplit < firstSplit) {
-                            addSplit(edges, idStart, pts, verb,
-                                    &intercepts.fTs[lastSplit],
-                                    firstSplit - lastSplit, false);
+                            addSplit(edges, pts, verb, intercepts,
+                                    lastSplit, firstSplit, false);
             #if DEBUG_SPLIT
-                            SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n",
-                                    __FUNCTION__, fID, lastSplit, firstSplit);
+                            SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
+                                    __FUNCTION__, lastSplit, firstSplit);
             #endif
                         }
-                        addSplit(edges, idStart, pts, verb,
-                                &intercepts.fTs[firstSplit],
-                                tIndex - firstSplit, true);
+                        addSplit(edges, pts, verb, intercepts, 
+                                firstSplit, tIndex, true);
             #if DEBUG_SPLIT
-                        SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n",
-                                __FUNCTION__, fID, firstSplit, tIndex);
+                        SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
+                                __FUNCTION__, firstSplit, tIndex);
             #endif
                         lastSplit = tIndex;
                         firstSplit = -1;
@@ -956,20 +1054,18 @@ struct InEdge {
                 if (firstSplit < 0) {
                     firstSplit = lastSplit;
                 } else {
-                    addSplit(edges, idStart, pts, verb,
-                            &intercepts.fTs[lastSplit], firstSplit - lastSplit,
-                            false);
+                    addSplit(edges, pts, verb, intercepts, lastSplit,
+                            firstSplit, false);
             #if DEBUG_SPLIT
-                    SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__,
-                            fID, lastSplit, firstSplit);
+                    SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
+                            lastSplit, firstSplit);
             #endif
                 }
-                addSplit(edges, idStart, pts, verb,
-                        &intercepts.fTs[firstSplit], tIndex - firstSplit,
-                        pts[verb].fY < y);
+                addSplit(edges, pts, verb, intercepts, firstSplit,
+                        tIndex, pts[verb].fY < y);
             #if DEBUG_SPLIT
-                SkDebugf("%s addSplit 4 edge=%d tIndex=%d,%d %s\n", __FUNCTION__,
-                        fID, firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
+                SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
+                        firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
             #endif
             }
         }
@@ -978,10 +1074,9 @@ struct InEdge {
             int nextVerb = verbs - 1 - fVerbs.begin();
             if (lastVerb < nextVerb) {
                 int nextPt = pts - fPts.begin();
-                addPartial(edges, idStart, lastPt, nextPt,
-                        lastVerb, nextVerb);
+                addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
             #if DEBUG_SPLIT
-                SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID);
+                SkDebugf("%s addPartial 2\n", __FUNCTION__);
             #endif
             }
             // OPTIMIZATION: reuse the edge instead of marking it empty
@@ -1015,7 +1110,7 @@ struct InEdge {
             SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
                     className, i, fVerbs[i]);
         }
-        SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
+        SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
                 className, fBounds.fLeft, fBounds.fTop,
                 fBounds.fRight, fBounds.fBottom);
         SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
@@ -1049,7 +1144,6 @@ InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges
     : fPath(path)
     , fCurrentEdge(NULL)
     , fEdges(edges)
-    , fID(0)
     , fHorizontalEdges(horizontalEdges)
     , fIgnoreHorizontal(ignoreHorizontal)
     , fContainsCurves(false)
@@ -1061,10 +1155,6 @@ bool containsCurves() const {
     return fContainsCurves;
 }
 
-int nextID() {
-    return ++fID;
-}
-
 protected:
 
 void addEdge() {
@@ -1076,7 +1166,7 @@ void addEdge() {
 
 bool complete() {
     if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
-        fCurrentEdge->complete(fWinding, nextID());
+        fCurrentEdge->complete(fWinding);
         fCurrentEdge = NULL;
         return true;
     }
@@ -1180,7 +1270,6 @@ private:
     SkPath::Verb fVerb;
     int fPtCount;
     int fPtOffset;
-    int fID;
     int8_t fWinding;
     bool fIgnoreHorizontal;
     bool fContainsCurves;
@@ -1729,19 +1818,17 @@ static void debugShowLineIntersection(int pts, const WorkEdge& wt,
     SkPoint wtOutPt, wnOutPt;
     LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
     LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
-    SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
+    SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
             __FUNCTION__,
             wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
-            wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
-            test->fID, next->fID);
+            wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
     if (pts == 2) {
         SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
     }
-    SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
+    SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
             __FUNCTION__,
             wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
-            wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
-            test->fID, next->fID);
+            wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
     if (pts == 2) {
         SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
     }
@@ -1979,10 +2066,15 @@ static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
     if (edgeCount == 0) {
         return;
     }
+    int id = 0;
     for (size_t index = 0; index < edgeCount; ++index) {
-        *edgeList.append() = &edges[index];
+        InEdge& edge = edges[index];
+        if (!edge.fWinding) {
+            continue;
+        }
+        edge.fID = ++id;
+        *edgeList.append() = &edge;
     }
-    edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
     *edgeList.append() = &edgeSentinel;
     QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
 }
@@ -2240,7 +2332,6 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
         bool moreToDo, aboveBottom;
         do {
             double currentT = activePtr->t();
-            SkASSERT(currentT < 1);
             const SkPoint* points = wt.fPts;
             double nextT;
             do {
@@ -2270,32 +2361,30 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
                 }
                 if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
                         != clipped[verb].fY : clipped[0] != clipped[verb])) {
-                    if (gShowDebugf) {
-                        const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
-                        SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
-                                " v=%d t=(%1.9g,%1.9g)\n", tab, "",
-                                verbStr[verb], clipped[0].fX, clipped[0].fY,
-                                clipped[verb].fX, clipped[verb].fY,
-                                activePtr->ID(),
-                                activePtr->fWorkEdge.fVerb
-                                - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
-                                currentT, nextT);
-                    }
+#if DEBUG_STITCH_EDGE
+                    SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
+                            " v=%d t=(%1.9g,%1.9g)\n", tab, "",
+                            kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+                            clipped[verb].fX, clipped[verb].fY,
+                            activePtr->ID(),
+                            activePtr->fWorkEdge.fVerb
+                            - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+                            currentT, nextT);
+#endif
                     outBuilder.addCurve(clipped, (SkPath::Verb) verb,
                             activePtr->fWorkEdge.fEdge->fID,
                             activePtr->fCloseCall);
                 } else {
-                    if (gShowDebugf ) {
-                        const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
-                        SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
-                                " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
-                                verbStr[verb], clipped[0].fX, clipped[0].fY,
-                                clipped[verb].fX, clipped[verb].fY,
-                                activePtr->ID(),
-                                activePtr->fWorkEdge.fVerb
-                                - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
-                                currentT, nextT);
-                    }
+#if DEBUG_STITCH_EDGE
+                    SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
+                            " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
+                            kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+                            clipped[verb].fX, clipped[verb].fY,
+                            activePtr->ID(),
+                            activePtr->fWorkEdge.fVerb
+                            - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+                            currentT, nextT);
+#endif
                 }
             // by advancing fAbove/fBelow, the next call to sortHorizontal
             // will use these values if they're still valid instead of
@@ -2369,6 +2458,7 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
     InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
     SkTDArray<InEdge*> edgeList;
     InEdge edgeSentinel;
+    edgeSentinel.reset();
     makeEdgeList(edges, edgeSentinel, edgeList);
     SkTDArray<HorizontalEdge*> horizontalList;
     HorizontalEdge horizontalSentinel;
@@ -2407,13 +2497,13 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
         currentPtr = edgeList.begin();
         SkTArray<InEdge> splits;
         do {
-            (*currentPtr)->splitInflectionPts(splits, builder.nextID());
+            (*currentPtr)->splitInflectionPts(splits);
         } while (*++currentPtr != &edgeSentinel);
         if (splits.count()) {
-            edges.pop_back(); // pop the sentinel
             for (int index = 0; index < splits.count(); ++index) {
                 edges.push_back(splits[index]);
             }
+            edgeList.reset();
             makeEdgeList(edges, edgeSentinel, edgeList);
         }
     }
index 584ecda..31a5a23 100644 (file)
@@ -58,9 +58,13 @@ static void (*simplifyTests[])() = {
 
 static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
 
-static void (*firstTest)() = 0;
+static void (*firstTest)() = testSimplifyQuadratic3;
+static bool skipAll = false;
 
 void SimplifyQuadraticPaths_Test() {
+    if (skipAll) {
+        return;
+    }
     size_t index = 0;
     if (firstTest) {
         while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
index 0b2fd06..0844458 100644 (file)
@@ -3,6 +3,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPaint.h"
+#include <algorithm>
 
 static bool gShowPath = false;
 static bool gComparePaths = true;
@@ -124,7 +125,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two,
     canvas.drawPath(one, paint);
     canvas.restore();
     canvas.save();
-    canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1);
+    canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
     canvas.drawPath(two, paint);
     canvas.restore();
     for (int y = 0; y < bitHeight; ++y) {
@@ -184,13 +185,19 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
         }
     }
     if (!gDrawAllAsciiPaths) {
-        errors = scaledDrawTheSame(one, two, 9, 7, false, bitmap, canvas);
-        if (errors > 4) {
-            scaledDrawTheSame(one, two, 9, 7, true, bitmap, canvas);
+        const SkRect& bounds1 = one.getBounds();
+        const SkRect& bounds2 = two.getBounds();
+        SkRect larger = bounds1;
+        larger.join(bounds2);
+        SkScalar xScale = std::max(80.0f / larger.width(), 1.0f);
+        SkScalar yScale = std::max(60.0f / larger.height(), 1.0f);
+        errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
+        if (errors > 8) {
+            scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
         }
     }
     if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); 
-    if (errors > 4 && gComparePathsAssert) {
+    if (errors > 31 && gComparePathsAssert) {
         showPath(one);
         showPath(two, "simplified:");
         SkASSERT(0);
index c91ac45..6a6ea32 100644 (file)
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
-               FE74136014F6866000056D7B /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
-                       proxyType = 2;
-                       remoteGlobalIDString = ECDC7853EF9A45553165AE98;
-                       remoteInfo = opts_ssse3;
-               };
                FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */;
                        remoteGlobalIDString = CDE03B47AA5CD6CE32E53995;
                        remoteInfo = ports;
                };
+               FEE70DA6153487F200814606 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
+                       proxyType = 2;
+                       remoteGlobalIDString = ECDC7853EF9A45553165AE98 /* libopts_ssse3.a */;
+                       remoteInfo = opts_ssse3;
+               };
                FEED7261144DD38D0059E97B /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = FEED725D144DD38D0059E97B /* views.xcodeproj */;
                        isa = PBXGroup;
                        children = (
                                FEF87C2C13E0410900335C58 /* libopts.a */,
-                               FE74136114F6866000056D7B /* libopts_ssse3.a */,
+                               FEE70DA7153487F200814606 /* libopts_ssse3.a */,
                        );
                        name = Products;
                        sourceTree = "<group>";
 /* End PBXProject section */
 
 /* Begin PBXReferenceProxy section */
-               FE74136114F6866000056D7B /* libopts_ssse3.a */ = {
+               FEA5F4E11497FFF6005052F9 /* libports.a */ = {
                        isa = PBXReferenceProxy;
                        fileType = archive.ar;
-                       path = libopts_ssse3.a;
-                       remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */;
+                       path = libports.a;
+                       remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
-               FEA5F4E11497FFF6005052F9 /* libports.a */ = {
+               FEE70DA7153487F200814606 /* libopts_ssse3.a */ = {
                        isa = PBXReferenceProxy;
                        fileType = archive.ar;
-                       path = libports.a;
-                       remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
+                       path = libopts_ssse3.a;
+                       remoteRef = FEE70DA6153487F200814606 /* PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
                FEED7262144DD38D0059E97B /* libviews.a */ = {
index b2c9123..2078396 100644 (file)
@@ -69,11 +69,55 @@ path.lineTo(460.782654, -112.96492);
 path.lineTo(326.610535, 34.0393639);
 path.close();
 </div>
+<div id="test_5div">
+original:
+path.moveTo(0, 0);
+path.quadTo(20, 0, 20, 20);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 20);
+path.quadTo(0, 0, 20, 0);
+path.lineTo(0, 20);
+path.close();
+simplified:
+path.moveTo(0, 0);
+path.lineTo(5, 5);
+path.quadTo(0, 10, 0, 20);
+path.lineTo(20, 20);
+path.quadTo(20, 10, 15, 5);
+path.lineTo(20, 0);
+path.quadTo(14.1421356, 0, 10, 1.71572876);
+path.quadTo(5.85786438, 0, 0, 0);
+path.close();
+</div>
+<div id="test_6div">
+original:
+path.moveTo(0, 20);
+path.quadTo(20, 0, 40, 20);
+path.lineTo(0, 20);
+path.close();
+path.moveTo(40, 10);
+path.quadTo(20, 30, 0, 10);
+path.lineTo(40, 10);
+path.close();
+simplified:
+path.moveTo(0, 10);
+path.quadTo(2.92893219, 12.9289322, 5.85786438, 15);
+path.quadTo(2.92893219, 17.0710678, 0, 20);
+path.lineTo(40, 20);
+path.quadTo(37.0710678, 17.0710678, 34.1421356, 15);
+path.quadTo(37.0710678, 12.9289322, 40, 10);
+path.lineTo(0, 10);
+path.close();
+</div>
+
 </div>
 
 <script type="text/javascript">
 
 var testDivs = [
+    test_5div,
+    test_6div,
     test_4div,
     test_3div,
     test_2div,
@@ -96,16 +140,26 @@ function parse(test) {
     var contourStrs = test.split("path.close();");
     var pattern = /-?\d+\.*\d*/g;
     for (var c in contourStrs) {
-        var points = contourStrs[c].match(pattern);
-        var pts = [];
-        for (var wd in points) {
-            var num = parseFloat(points[wd]);
-            if (isNaN(num)) continue;
-            pts.push(num );
+        var contour = contourStrs[c];
+        var verbStrs = contour.split("path");
+        var verbs = [];
+        for (var v in verbStrs) {
+            var verbStr = verbStrs[v];
+            var points = verbStr.match(pattern);
+            var pts = [];
+            for (var wd in points) {
+                var num = parseFloat(points[wd]);
+                if (isNaN(num)) continue;
+                pts.push(num);
+            }
+            if (pts.length > 0)
+                verbs.push(pts);
         }
-        contours.push(pts);
+        if (verbs.length > 0)
+            contours.push(verbs);
     }
-    tests.push(contours);
+    if (contours.length > 0)
+        tests.push(contours);
 }
 
 function init(test) {
@@ -116,13 +170,15 @@ function init(test) {
     var xmax = -Infinity;
     var ymin = Infinity;
     var ymax = -Infinity;
-    for (pts in test) {
-        var pt = test[pts];
-        for (i = 0; i < pt.length; i += 2) {
-            xmin = Math.min(xmin, pt[i]);
-            ymin = Math.min(ymin, pt[i + 1]);
-            xmax = Math.max(xmax, pt[i]);
-            ymax = Math.max(ymax, pt[i + 1]);
+    for (var contours in test) {
+        var contour = test[contours];
+        for (var verbs in contour) {
+            var verb = contour[verbs];
+            var last = verb.length;
+            xmin = Math.min(xmin, verb[0]);
+            ymin = Math.min(ymin, verb[1]);
+            xmax = Math.max(xmax, verb[last - 2]);
+            ymax = Math.max(ymax, verb[last - 1]);
         }
     }
     var subscale = 1;
@@ -183,27 +239,46 @@ function draw(test, _at_x, _at_y, scale) {
         ctx.fillText(num.toFixed(0), 0, i * unit + _at_y + 0);
     }
     ctx.strokeStyle = "red";
-    for (pts in test) {
-        var pt = test[pts];
-        var x = pt[0];
-        var y = pt[1];
+    var contours, verbs, pts;
+    for (contours in test) {
+        var contour = test[contours];
+        if (contours == 2) ctx.strokeStyle = "blue";
         ctx.beginPath();
-        ctx.moveTo(xoffset + x * unit, yoffset + y * unit);
-        for (i = 2; i < pt.length; i += 2) {
-            x = pt[i];
-            y = pt[i + 1];
-            ctx.lineTo(xoffset + x * unit, yoffset + y * unit);
+        var first = true;
+        for (verbs in contour) {
+            var verb = contour[verbs];
+            switch (verb.length) {
+                case 2:
+                    if (first) {
+                        ctx.moveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+                        first = false;
+                    } else
+                        ctx.lineTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+                    break;
+                case 4:
+                    ctx.quadraticCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+                        xoffset + verb[2] * unit, yoffset + verb[3] * unit);
+                    break;
+                case 6:
+                    ctx.bezierCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+                        xoffset + verb[2] * unit, yoffset + verb[3] * unit,
+                        xoffset + verb[4] * unit, yoffset + verb[5] * unit);
+                    break;
+            }
         }
         ctx.stroke();
     }
     
     ctx.fillStyle="blue";
-    for (pts in test) {
-        var pt = test[pts];
-        for (i = 0; i < pt.length; i += 2) {
-            x = pt[i];
-            y = pt[i + 1];
-            drawPoint(x, y, xoffset, yoffset, unit);
+    for (contours in test) {
+        var contour = test[contours];
+        for (verbs in contour) {
+            var verb = contour[verbs];
+            for (i = 0; i < verb.length; i += 2) {
+                x = verb[i];
+                y = verb[i + 1];
+                drawPoint(x, y, xoffset, yoffset, unit);
+            }
         }
     }
 }