work in progress
authorcaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 28 Feb 2012 16:57:05 +0000 (16:57 +0000)
committercaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 28 Feb 2012 16:57:05 +0000 (16:57 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@3276 2bbb7eff-a529-9590-31e7-b0007b416f81

experimental/Intersection/EdgeWalker.cpp
experimental/Intersection/Intersection_Tests.cpp
experimental/Intersection/Intersection_Tests.h
experimental/Intersection/TSearch.h
experimental/Intersection/edge.xcodeproj/project.pbxproj

index 5178bb2..fc53f63 100644 (file)
@@ -14,9 +14,9 @@
 #include "SkTDArray.h"
 #include "TSearch.h"
 
-static bool fShowDebugf = true; // FIXME: remove once debugging is complete
-
-const int kOpenerVerbBitShift = 3; // leaves 3 bits for SkPath::Verb
+static bool gShowDebugf = true; // FIXME: remove once debugging is complete
+static bool gShowPath = false;
+static bool gDebugLessThan = false;
 
 static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
         double aRange[2], double bRange[2]) {
@@ -30,6 +30,13 @@ static int LineIntersect(const SkPoint a[2], SkScalar y, double aRange[2]) {
     return horizontalIntersect(aLine, y, aRange);
 }
 
+static SkScalar LineXAtT(const SkPoint a[2], double t) {
+    _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    double x;
+    xy_at_t(aLine, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
 static SkScalar LineYAtT(const SkPoint a[2], double t) {
     _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
     double y;
@@ -119,17 +126,15 @@ static bool extendLine(const SkPoint line[2], const SkPoint& add) {
 
 struct OutEdge {
     bool operator<(const OutEdge& rh) const {
-        const SkPoint& first = fPts.begin()[0];
-        const SkPoint& rhFirst = rh.fPts.begin()[0];
+        const SkPoint& first = fPts[0];
+        const SkPoint& rhFirst = rh.fPts[0];
         return first.fY == rhFirst.fY
                 ? first.fX < rhFirst.fX
                 : first.fY < rhFirst.fY;
     }
     
-    SkTDArray<SkPoint> fPts;
-    // contains the SkPath verb, plus 1<<kOpenerVerbBitShift if edge opens span
-    SkTDArray<uint8_t> fVerbs; // FIXME: not read from everywhere
-    bool fOpener;
+    SkPoint fPts[4];
+    uint8_t fVerb; // FIXME: not read from everywhere
 };
 
 class OutEdgeBuilder {
@@ -138,34 +143,11 @@ public:
         : fFill(fill) {
         }
 
-    void addLine(const SkPoint line[2], bool opener) {
-        size_t count = fEdges.count();
-        for (size_t index = 0; index < count; ++index) {
-            OutEdge& edge = fEdges[index];
-            if (opener != edge.fOpener) {
-                continue;
-            }
-            SkTDArray<SkPoint>& pts = edge.fPts;
-            SkPoint& last = pts.top();
-            if (last == line[0]) {
-                SkTDArray<uint8_t>& verbs = edge.fVerbs;
-                uint8_t lastVerb = verbs.top();
-                if (lastVerb == SkPath::kLine_Verb
-                        && extendLine(&last - 1, line[1])) {
-                    last = line[1];
-                } else {
-                    *pts.append() = line[1];
-                    *verbs.append() = SkPath::kLine_Verb;
-                }
-                return;
-            }
-        }
+    void addLine(const SkPoint line[2]) {
         OutEdge& newEdge = fEdges.push_back();
-        *newEdge.fPts.append() = line[0];
-        *newEdge.fVerbs.append() = SkPath::kMove_Verb;
-        *newEdge.fPts.append() = line[1];
-        *newEdge.fVerbs.append() = SkPath::kLine_Verb;
-        newEdge.fOpener = opener;
+        newEdge.fPts[0] = line[0];
+        newEdge.fPts[1] = line[1];
+        newEdge.fVerb = SkPath::kLine_Verb;
     }
 
     void assemble(SkPath& simple) {
@@ -182,46 +164,70 @@ public:
             if (listIndex >= listCount) {
                 break;
             }
-            SkPoint firstPt;
+            SkPoint firstPt, lastLine[2];
             bool doMove = true;
+            bool closed = false;
             int edgeIndex;
             do {
-                SkTDArray<SkPoint>& ptArray = fEdges[listIndex].fPts;
-                SkASSERT(ptArray.count() > 0);
-                SkPoint* pts, * end;
+                SkPoint* ptArray = fEdges[listIndex].fPts;
+                uint8_t verb = fEdges[listIndex].fVerb;
+                SkPoint* start, * end;
                 if (advance < 0) {
-                    pts = ptArray.end() - 1;
-                    end = ptArray.begin();
+                    start = &ptArray[verb];
+                    end = &ptArray[0];
                 } else {
-                    pts = ptArray.begin();
-                    end = ptArray.end() - 1;
+                    start = &ptArray[0];
+                    end = &ptArray[verb];
                 }
-                if (doMove) {
-                    firstPt = pts[0];
-                    simple.moveTo(pts[0].fX, pts[0].fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
-                    doMove = false;
-                } else {
-                    simple.lineTo(pts[0].fX, pts[0].fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s 1 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
-                    if (firstPt == pts[0]) {
-                        simple.close();
-                        if (fShowDebugf) {
-                            SkDebugf("%s close\n", __FUNCTION__);
+                switch (verb) {
+                    case SkPath::kLine_Verb:
+                        bool gap;
+                        if (doMove) {
+                            firstPt = *start;
+                            simple.moveTo(start->fX, start->fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
+                                        start->fX, start->fY);
+                            }
+                            lastLine[0] = *start;
+                            lastLine[1] = *end;
+                            doMove = false;
+                            closed = false;
+                            break;
+                        }
+                        gap = lastLine[1] != *start;
+                        if (gap) {
+                            SkASSERT(fFill && lastLine[1].fY == start->fY);
+                            simple.lineTo(lastLine[1].fX, lastLine[1].fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
+                                        lastLine[1].fX, lastLine[1].fY);
+                            }
+                        }
+                        if (gap || !extendLine(lastLine, *end)) {
+                            SkASSERT(lastLine[1] == *start ||
+                                    (fFill && lastLine[1].fY == start->fY));
+                            simple.lineTo(start->fX, start->fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
+                                        start->fX, start->fY);
+                            }
+                            lastLine[0] = *start;
+                        }
+                        lastLine[1] = *end;
+                        if (firstPt == *end) {
+                            simple.lineTo(end->fX, end->fY);
+                            simple.close();
+                            if (gShowDebugf) {
+                                SkDebugf("%s close 1 (%g, %g)\n", __FUNCTION__,
+                                        end->fX, end->fY);
+                            }
+                            closed = true;
                         }
                         break;
-                    }
-                }
-                while (pts != end) {
-                    pts += advance;
-                    simple.lineTo(pts->fX, pts->fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s 2 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
+                    default:
+                        // FIXME: add other curve types
+                        ;
                 }
                 if (advance < 0) {
                     edgeIndex = fTops[listIndex];
@@ -230,6 +236,15 @@ public:
                     edgeIndex = fBottoms[listIndex];
                     fBottoms[listIndex] = 0;
                 }
+                if (!edgeIndex) {
+                    simple.lineTo(firstPt.fX, firstPt.fY);
+                    simple.close();
+                    if (gShowDebugf) {
+                        SkDebugf("%s close 2 (%g,%g)\n", __FUNCTION__,
+                            firstPt.fX, firstPt.fY);
+                    }
+                    break;
+                }
                 listIndex = abs(edgeIndex) - 1;
                 if (edgeIndex < 0) {
                     fTops[listIndex] = 0;
@@ -240,21 +255,63 @@ public:
                 if (advance > 0 ^ edgeIndex < 0) {
                     advance = -advance;
                 }
-            } while (edgeIndex);
+            } while (edgeIndex && !closed);
         } while (true);
     }
     
-    static bool lessThan(SkTArray<OutEdge>& edges, const int* onePtr,
-            const int* twoPtr) {
-        int one = *onePtr;
-        const OutEdge& oneEdge = edges[(one < 0 ? -one : one) - 1];
-        const SkPoint* onePt = one < 0 ? oneEdge.fPts.begin()
-                : oneEdge.fPts.end() - 1;
-        int two = *twoPtr;
-        const OutEdge& twoEdge = edges[(two < 0 ? -two : two) - 1];
-        const SkPoint* twoPt = two < 0 ? twoEdge.fPts.begin()
-                : twoEdge.fPts.end() - 1;
-        return onePt->fY == twoPt->fY ? onePt->fX < twoPt->fX : onePt->fY < twoPt->fY;
+    // sort points by y, then x
+    // if x/y is identical, sort bottoms before tops
+    // if identical and both tops/bottoms, sort by angle
+    static bool lessThan(SkTArray<OutEdge>& edges, const int one,
+            const int two) {
+        const OutEdge& oneEdge = edges[abs(one) - 1];
+        int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
+        const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
+        const OutEdge& twoEdge = edges[abs(two) - 1];
+        int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
+        const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
+        if (startPt1.fY != startPt2.fY) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
+                        one, two, startPt1.fY, startPt2.fY,
+                        startPt1.fY < startPt2.fY ? "true" : "false");
+            }
+            return startPt1.fY < startPt2.fY;
+        }
+        if (startPt1.fX != startPt2.fX) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
+                        one, two, startPt1.fX, startPt2.fX,
+                        startPt1.fX < startPt2.fX ? "true" : "false");
+            }
+            return startPt1.fX < startPt2.fX;
+        }
+        const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
+        const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
+        SkScalar dy1 = startPt1.fY - endPt1.fY;
+        SkScalar dy2 = startPt2.fY - endPt2.fY;
+        SkScalar dy1y2 = dy1 * dy2;
+        if (dy1y2 < 0) { // different signs
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
+                        dy1 > 0 ? "true" : "false");
+            }
+            return dy1 > 0; // one < two if one goes up and two goes down
+        }
+        if (dy1y2 == 0) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
+                        one, two, endPt1.fX < endPt2.fX ? "true" : "false");
+            }
+            return endPt1.fX < endPt2.fX;
+        } 
+        SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
+        SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
+        if (gDebugLessThan) {
+            SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
+                    one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
+        }
+        return dy2 > 0 ^ dx1y2 < dx2y1;
     }
 
     // Sort the indices of paired points and then create more indices so
@@ -265,17 +322,19 @@ public:
         if (!count) {
             return;
         }
-        SkASSERT(!fFill || (count & 1) == 0);
+        SkASSERT(!fFill || count > 1);
         fTops.setCount(count);
         sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
         fBottoms.setCount(count);
         sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
         SkTDArray<int> order;
         for (index = 1; index <= count; ++index) {
-            *order.append() = index;
             *order.append() = -index;
         }
-        QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), count * 2, lessThan);
+        for (index = 1; index <= count; ++index) {
+            *order.append() = index;
+        }
+        QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
         int* lastPtr = order.end() - 1;
         int* leftPtr = order.begin();
         while (leftPtr < lastPtr) {
@@ -286,13 +345,18 @@ public:
             int rightIndex = *rightPtr;
             int rightOutIndex = abs(rightIndex) - 1;
             const OutEdge& right = fEdges[rightOutIndex];
-        // OPTIMIZATION: if fFill is true, we don't need leftMatch, rightMatch
-            SkPoint& leftMatch = left.fPts[leftIndex < 0 ? 0
-                    : left.fPts.count() - 1];
-            SkPoint& rightMatch = right.fPts[rightIndex < 0 ? 0
-                    : right.fPts.count() - 1];
-            SkASSERT(!fFill || leftMatch.fY == rightMatch.fY);
-            if (fFill || leftMatch == rightMatch) {
+            bool pairUp = fFill;
+            if (!pairUp) {
+                const SkPoint& leftMatch =
+                        left.fPts[leftIndex < 0 ? 0 : left.fVerb];
+                const SkPoint& rightMatch =
+                        right.fPts[rightIndex < 0 ? 0 : right.fVerb];
+                pairUp = leftMatch == rightMatch;
+            } else {
+                SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
+                        == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
+            }
+            if (pairUp) {
                 if (leftIndex < 0) {
                     fTops[leftOutIndex] = rightIndex;
                 } else {
@@ -492,18 +556,36 @@ void walk() {
     while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
         switch (fVerb) {
             case SkPath::kMove_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.moveTo(%g, %g);\n", fPts[0].fX, fPts[0].fY);
+                }
                 startEdge();
                 continue;
             case SkPath::kLine_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.lineTo(%g, %g);\n", fPts[1].fX, fPts[1].fY);
+                }
                 winding = direction(2);
                 break;
             case SkPath::kQuad_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
+                        fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY);
+                }
                 winding = direction(3);
                 break;
             case SkPath::kCubic_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
+                        fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY,
+                        fPts[3].fX, fPts[3].fY);
+                }
                 winding = direction(4);
                 break;
             case SkPath::kClose_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.close();\n");
+                }
                 SkASSERT(fCurrentEdge);
                 complete();
                 continue;
@@ -521,8 +603,9 @@ void walk() {
             // FIXME: if prior verb or this verb is a horizontal line, reverse
             // it instead of starting a new edge
             SkASSERT(fCurrentEdge);
-            fCurrentEdge->complete(fWinding);
-            startEdge();
+            if (complete()) {
+                startEdge();
+            }
         }
         fWinding = winding;
         addEdge();
@@ -532,6 +615,9 @@ void walk() {
             fEdges.pop_back();
         }
     }
+    if (gShowPath) {
+        SkDebugf("\n");
+    }
 }
 
 private:
@@ -558,7 +644,7 @@ struct WorkEdge {
         fVerb = edge->fVerbs.begin();
     }
 
-    bool next() {
+    bool advance() {
         SkASSERT(fVerb < fEdge->fVerbs.end());
         fPts += *fVerb++;
         return fVerb != fEdge->fVerbs.end();
@@ -586,16 +672,38 @@ struct WorkEdge {
 // ActiveEdge* may be faster to insert and search
 struct ActiveEdge {
     bool operator<(const ActiveEdge& rh) const {
-        return fX < rh.fX;
+        return fXAbove != rh.fXAbove ? fXAbove < rh.fXAbove
+                : fXBelow < rh.fXBelow;
     }
 
     void calcLeft() {
-        fX = fWorkEdge.fPts[fWorkEdge.verb()].fX;
+        // OPTIMIZE: put a kDone_Verb at the end of the verb list?
+        if (fDone)
+            return; // nothing to do; use last
+        switch (fWorkEdge.verb()) {
+            case SkPath::kLine_Verb: {
+                // OPTIMIZATION: if fXAbove, fXBelow have already been computed
+                //  for the fTIndex, don't do it again
+                // For identical x, this lets us know which edge is first.
+                // If both edges have T values < 1, check x at next T (fXBelow).
+                int add = (fTIndex <= fTs->count()) - 1;
+                double tAbove = t(fTIndex + add);
+                fXAbove = LineXAtT(fWorkEdge.fPts, tAbove);
+                double tBelow = t(fTIndex - ~add);
+                fXBelow = LineXAtT(fWorkEdge.fPts, tBelow);
+                break;
+            }
+            default:
+                // FIXME: add support for all curve types
+                SkASSERT(0);
+        }
     }
 
     void init(const InEdge* edge) {
         fWorkEdge.init(edge);
         initT();
+        fDone = false;
+        fYBottom = SK_ScalarMin;
     }
     
     void initT() {
@@ -608,19 +716,48 @@ struct ActiveEdge {
   //  but templated arrays don't allow returning a pointer to the end() element
         fTIndex = 0;
     }
+    
+    bool isCoincidentWith(const ActiveEdge* edge) const {
+        if (fXAbove != edge->fXAbove || fXBelow != edge->fXBelow) {
+            return false;
+        }
+        switch (fWorkEdge.verb()) {
+            case SkPath::kLine_Verb: {
+                return (fWorkEdge.fPts[0].fX - fWorkEdge.fPts[1].fX) *
+                        (edge->fWorkEdge.fPts[0].fY - edge->fWorkEdge.fPts[1].fY) ==
+                        (fWorkEdge.fPts[0].fY - fWorkEdge.fPts[1].fY) *
+                        (edge->fWorkEdge.fPts[0].fX - edge->fWorkEdge.fPts[1].fX);
+            }
+            default:
+                // FIXME: add support for all curve types
+                SkASSERT(0);
+        }
+        return false;
+    }
 
-    bool nextT() {
+    bool advanceT() {
         SkASSERT(fTIndex <= fTs->count());
-        return ++fTIndex == fTs->count() + 1;
+        return ++fTIndex <= fTs->count();
     }
     
-    bool next() {
-        bool result = fWorkEdge.next();
+    bool advance() {
+    // FIXME: flip sense of next
+        bool result = fWorkEdge.advance();
+        fDone = !result;
         initT();
         return result;
     }
+    
+    bool done(SkScalar y) {
+        return fDone || fYBottom > y;
+    }
+    
+    double nextT() {
+        SkASSERT(fTIndex <= fTs->count());
+        return t(fTIndex + 1);
+    }
 
-    double t() {
+    double t() const {
         if (fTIndex == 0) {
             return 0;
         }
@@ -630,11 +767,24 @@ struct ActiveEdge {
         return (*fTs)[fTIndex - 1];
     }
 
+    double t(int tIndex) const {
+        if (tIndex == 0) {
+            return 0;
+        }
+        if (tIndex > fTs->count()) {
+            return 1;
+        }
+        return (*fTs)[tIndex - 1];
+    }
+
     WorkEdge fWorkEdge;
     const SkTDArray<double>* fTs;
-    SkScalar fX;
+    SkScalar fXAbove;
+    SkScalar fXBelow;
+    SkScalar fYBottom;
     int fTIndex;
     bool fSkip;
+    bool fDone;
 };
 
 static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
@@ -657,7 +807,6 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
             WorkEdge wt;
             wt.init(test);
             do {
-                // FIXME: add all curve types
                 // OPTIMIZATION: if bottom intersection does not change
                 // the winding on either side of the split, don't intersect
                 if (wt.verb() == SkPath::kLine_Verb) {
@@ -667,25 +816,33 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
                         test->fContainsIntercepts |= test->add(wtTs, pts,
                                 wt.verbIndex());
                     }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
-            } while (wt.next());
+            } while (wt.advance());
         }
         test = *++testPtr;
     }
 }
 
 static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
-    InEdge** testPtr = currentPtr;
-    InEdge* test = *testPtr;
-    while (testPtr != lastPtr - 1) {
-        InEdge* next = *++testPtr;
-        if (!test->cached(next)
-                && Bounds::Intersects(test->fBounds, next->fBounds)) {
+    InEdge** testPtr = currentPtr - 1;
+    while (++testPtr != lastPtr - 1) {
+        InEdge* test = *testPtr;
+        InEdge** nextPtr = testPtr;
+        do {
+            InEdge* next = *++nextPtr;
+            if (test->cached(next)) {
+                continue;
+            }
+            if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
+                continue;
+            }
             WorkEdge wt, wn;
             wt.init(test);
             wn.init(next);
             do {
-                // FIXME: add all combinations of curve types
                 if (wt.verb() == SkPath::kLine_Verb
                         && wn.verb() == SkPath::kLine_Verb) {
                     double wtTs[2], wnTs[2];
@@ -696,10 +853,12 @@ static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
                         next->fContainsIntercepts |= next->add(wnTs, pts,
                                 wn.verbIndex());
                     }
+                } else {
+                    // FIXME: add all combinations of curve types
+                    SkASSERT(0);
                 }
-            } while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
-        }
-        test = next;
+            } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
+        } while (nextPtr != lastPtr);
     }
 }
 
@@ -739,7 +898,6 @@ static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
         WorkEdge wt;
         wt.init(test);
         do {
-            // FIXME: add all curve types
             const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
             const SkTDArray<double>& fTs = intercepts.fTs;
             size_t count = fTs.count();
@@ -749,9 +907,12 @@ static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
                     if (yIntercept > y && bottom > yIntercept) {
                         bottom = yIntercept;
                     }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
             }
-        } while (wt.next());
+        } while (wt.advance());
     }
 }
 
@@ -842,7 +1003,7 @@ static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
     for (index = 1; index < edgeCount; ++index) {
         winding += activePtr->fWorkEdge.winding();
         ActiveEdge* nextPtr = edgeList[index];
-        if (activePtr->fX == nextPtr->fX) {
+        if (activePtr->isCoincidentWith(nextPtr)) {
             if (!firstCoincident) {
                 firstCoincident = activePtr;
             }
@@ -871,7 +1032,7 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
     int winding = 0;
     ActiveEdge** activeHandle = edgeList.begin() - 1;
     ActiveEdge** lastActive = edgeList.end();
-    if (fShowDebugf) {
+    if (gShowDebugf) {
         SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
     }
     while (++activeHandle != lastActive) {
@@ -879,23 +1040,27 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
         const WorkEdge& wt = activePtr->fWorkEdge;
         int lastWinding = winding;
         winding += wt.winding();
+        if (activePtr->done(y)) {
+            continue;
+        }
         int opener = (lastWinding & windingMask) == 0;
         bool closer = (winding & windingMask) == 0;
         SkASSERT(!opener | !closer);
         bool inWinding = opener | closer;
-        opener <<= kOpenerVerbBitShift;
+        const SkPoint* clipped;
+        uint8_t verb = wt.verb();
+        bool moreToDo, aboveBottom;
         do {
             double currentT = activePtr->t();
             SkASSERT(currentT < 1);
             const SkPoint* points = wt.fPts;
-            bool last;
+            double nextT;
             do {
-                last = activePtr->nextT();
-                double nextT = activePtr->t();
-                // FIXME: add all combinations of curve types
-                if (wt.verb() == SkPath::kLine_Verb) {
+                nextT = activePtr->nextT();
+                if (verb == SkPath::kLine_Verb) {
                     SkPoint clippedPts[2];
-                    const SkPoint* clipped;
+                    // FIXME: obtuse: want efficient way to say 
+                    // !currentT && currentT != 1 || !nextT && nextT != 1
                     if (currentT * nextT != 0 || currentT + nextT != 1) {
                         // OPTIMIZATION: if !inWinding, we only need 
                         // clipped[1].fY
@@ -905,25 +1070,23 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
                         clipped = points;
                     }
                     if (inWinding && !activePtr->fSkip) {
-                        if (fShowDebugf) {
+                        if (gShowDebugf) {
                             SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
                                     clipped[0].fX, clipped[0].fY,
                                     clipped[1].fX, clipped[1].fY);
                         }
-                        outBuilder.addLine(clipped, opener);
-                    }
-                    if (clipped[1].fY >= bottom) {
-                        if (last) {
-                            activePtr->next();
-                        }
-                        goto nextEdge;
+                        outBuilder.addLine(clipped);
                     }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
                 currentT = nextT;
-            } while (!last);
-        } while (activePtr->next());
-nextEdge:
-        ;
+                moreToDo = activePtr->advanceT();
+                activePtr->fYBottom = clipped[verb].fY;
+                aboveBottom = activePtr->fYBottom < bottom;
+            } while (moreToDo & aboveBottom);
+        } while ((moreToDo || activePtr->advance()) & aboveBottom);
     }
 }
 
@@ -933,9 +1096,10 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
     simple.reset();
     simple.setFillType(SkPath::kEvenOdd_FillType);
     // turn path into list of edges increasing in y
-    // if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
-    // once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
-    //  and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
+    // if an edge is a quad or a cubic with a y extrema, note it, but leave it
+    // unbroken. Once we have a list, sort it, then walk the list (walk edges
+    // twice that have y extrema's on top)  and detect crossings -- look for raw
+    // bounds that cross over, then tight bounds that cross
     SkTArray<InEdge> edges;
     InEdgeBuilder builder(path, asFill, edges);
     SkTDArray<InEdge*> edgeList;
@@ -965,276 +1129,3 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
     outBuilder.bridge();
     outBuilder.assemble(simple);
 }
-
-static void testSimplifyCoincidentVertical() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(10, 30, 30, 40);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentHorizontal() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(30, 10, 40, 30);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyMulti() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(20, 20, 40, 40);
-    simplify(path, true, out);
-    SkPath expected;
-    expected.setFillType(SkPath::kEvenOdd_FillType);
-    expected.moveTo(10,10); // two cutout corners
-    expected.lineTo(10,30);
-    expected.lineTo(20,30);
-    expected.lineTo(20,40);
-    expected.lineTo(40,40);
-    expected.lineTo(40,20);
-    expected.lineTo(30,20);
-    expected.lineTo(30,10);
-    expected.lineTo(10,10);
-    expected.close();
-    if (out != expected) {
-        SkDebugf("%s expected equal\n", __FUNCTION__);
-    }
-    
-    path = out;
-    path.addRect(30, 10, 40, 20);
-    path.addRect(10, 30, 20, 40);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-    
-    path = out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    simplify(path, true, out);
-    if (!out.isEmpty()) {
-        SkDebugf("%s expected empty\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyAddL() {
-    SkPath path, out;
-    path.moveTo(10,10); // 'L' shape
-    path.lineTo(10,40);
-    path.lineTo(40,40);
-    path.lineTo(40,20);
-    path.lineTo(30,20);
-    path.lineTo(30,10);
-    path.lineTo(10,10);
-    path.close();
-    path.addRect(30, 10, 40, 20); // missing notch of 'L'
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentCCW() {
-    SkPath path, out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentCW() {
-    SkPath path, out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
-    simplify(path, true, out);
-    if (!out.isEmpty()) {
-        SkDebugf("%s expected empty\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCorner() {
-    SkPath path, out;
-    path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
-    path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
-    simplify(path, true, out);
-    SkTDArray<SkRect> boundsArray;
-    contourBounds(out, boundsArray);
-    if (boundsArray.count() != 2) {
-        SkDebugf("%s expected 2 contours\n", __FUNCTION__);
-        return;
-    }
-    SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
-    SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
-    if (boundsArray[0] != one && boundsArray[0] != two
-            || boundsArray[1] != one && boundsArray[1] != two) {
-        SkDebugf("%s expected match\n", __FUNCTION__);
-    }
-}
-
-// non-intersecting test points, two equal sized rectangles
-static void lookForFailingTests(const SkPoint* pts, size_t ptsSize, int width,
-        int height, const SkRect& center) {
-    size_t index = 0;
-    for ( ; index < ptsSize; ++index) {
-        SkPath path, out;
-        path.addRect(center);
-        SkRect test = SkRect::MakeXYWH(pts[index].fX,
-                pts[index].fY, width, height);
-        path.addRect(test);
-        simplify(path, true, out);
-        SkPath::Iter iter(out, false);
-        SkPoint pts[2];
-        SkRect bounds[2];
-        bounds[0].setEmpty();
-        bounds[1].setEmpty();
-        SkRect* boundsPtr = bounds;
-        int count = 0;
-        SkPath::Verb verb;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            switch (verb) {
-                case SkPath::kMove_Verb:
-                    if (!boundsPtr->isEmpty()) {
-                        SkASSERT(boundsPtr == bounds);
-                        ++boundsPtr;
-                    }
-                    boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
-                    count = 0;
-                    break;
-                case SkPath::kLine_Verb: 
-                    count = 1;
-                    break;
-                case SkPath::kClose_Verb:
-                    count = 0;
-                    break;
-                default:
-                    SkDEBUGFAIL("bad verb");
-                    return;
-            }
-            for (int i = 1; i <= count; ++i) {
-                boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
-            }
-        }
-        SkASSERT(bounds[0] == center && bounds[1] == test
-            || bounds[1] == center && bounds[0] == test);
-    }
-}
-
-static void twoEqualRects() {
-    SkPoint pts[] = {
-        { 0,  0}, {10,  0}, {20,  0}, {30,  0}, {40,  0}, {50,  0}, {60,  0}, // above
-                  { 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
-                  {10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60},           // below
-                  {60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
-    };
-    size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
-    SkRect center = SkRect::MakeLTRB(30, 30, 50, 50);
-    lookForFailingTests(pts, ptsCount, 20, 20, center);
-}
-
-static void largerOuter() {
-    SkRect center = SkRect::MakeLTRB(50, 50, 70, 70);
-    const size_t count = 9;
-    SkPoint pts[count];
-    size_t index;
-    for (index = 0; index < count; ++index) { // above
-        pts[index].fX = index * 10;
-        pts[index].fY = 0;
-    }
-    lookForFailingTests(pts, count, 40, 20, center);
-    for (index = 0; index < count; ++index) { // left
-        pts[index].fX = 0;
-        pts[index].fY = index * 10;
-    }
-    lookForFailingTests(pts, count, 20, 40, center);
-    for (index = 0; index < count; ++index) { // below
-        pts[index].fX = index * 10;
-        pts[index].fY = 80;
-    }
-    lookForFailingTests(pts, count, 40, 20, center);
-    for (index = 0; index < count; ++index) { // right
-        pts[index].fX = 80;
-        pts[index].fY = index * 10;
-    }
-    lookForFailingTests(pts, count, 20, 40, center);
-}
-
-static void smallerOuter() {
-    SkPoint pts[] = {
-        { 0,  0}, {10,  0}, {20,  0}, {30,  0}, {40,  0}, {50,  0}, {60,  0}, // above
-                  { 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
-                  {10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60},           // below
-                  {60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
-    };
-    size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
-    SkRect center = SkRect::MakeLTRB(20, 20, 50, 50);
-    lookForFailingTests(pts, ptsCount, 10, 10, center);
-}
-
-void testSimplify();
-
-void (*simplifyTests[])() = {
-    testSimplifyCorner,
-    testSimplifyCoincidentCW,
-    testSimplifyCoincidentCCW,
-    testSimplifyCoincidentVertical, 
-    testSimplifyCoincidentHorizontal,
-    testSimplifyAddL,                
-    testSimplifyMulti,               
-};
-
-size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
-
-static void (*firstTest)() = 0;
-static bool lookForFailing = false;
-
-void testSimplify() {
-/* look for failing test cases */
-    if (lookForFailing) {
-// rects do not touch
-        twoEqualRects();
-        largerOuter();
-        smallerOuter();
-    }
-    size_t index = 0;
-    if (firstTest) {
-        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
-            ++index;
-        }
-    }
-    for ( ; index < simplifyTestsCount; ++index) {
-        (*simplifyTests[index])();
-    }
-}
-
-
index f27a3cf..42f4a26 100644 (file)
@@ -17,7 +17,8 @@ void Intersection_Tests() {
     LineQuadraticIntersection_Test();
     LineCubicIntersection_Test();
 
-    testSimplify();
+    SimplifyPolygonPaths_Test();
+    SimplifyRectangularPaths_Test();
 
     QuadraticCoincidence_Test();
     QuadraticReduceOrder_Test();
index deb08ca..9def8b5 100644 (file)
@@ -11,6 +11,8 @@ void LineCubicIntersection_Test();
 void LineIntersection_Test();
 void LineParameter_Test();
 void LineQuadraticIntersection_Test();
+void SimplifyPolygonPaths_Test();
+void SimplifyRectangularPaths_Test();
 void QuadraticBezierClip_Test();
 void QuadraticCoincidence_Test();
 void QuadraticIntersection_Test();
index 1f76252..44342e0 100644 (file)
@@ -40,41 +40,32 @@ void QSort(T** base, size_t count)
 }
 
 template <typename S, typename T>
-static void QSort_Partition(S& context, T* first, T* last,
-        bool (*lessThan)(S&, const T*, const T*))
+static T* QSort_Partition(S& context, T* left, T* right, T* pivot,
+        bool (*lessThan)(S&, const T, const T))
 {
-    T*   left = first;
-    T*   rite = last;
-    T*   pivot = left;
-
-    while (left <= rite) {
-        while (left < last && lessThan(context, left, pivot))
-            left += 1;
-        while (first < rite && lessThan(context, pivot, rite))
-            rite -= 1;
-        if (left <= rite) {
-            if (left < rite) {
-                SkTSwap(*left, *rite);
-            }
-            left += 1;
-            rite -= 1;
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (lessThan(context, *left, pivotValue)) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
         }
+        left += 1;
     }
-    if (first < rite)
-        QSort_Partition(context, first, rite, lessThan);
-    if (left < last)
-        QSort_Partition(context, left, last, lessThan);
+    SkTSwap(*newPivot, *right);
+    return newPivot;
 }
 
 template <typename S, typename T>
-void QSort(S& context, T* base, size_t count,
-        bool (*lessThan)(S& , const T*, const T*))
+void QSort(S& context, T* left, T* right,
+        bool (*lessThan)(S& , const T, const T))
 {
-    SkASSERT(base);
-    SkASSERT(lessThan);
-
-    if (count <= 1) {
+    if (left >= right) {
         return;
     }
-    QSort_Partition(context, base, base + (count - 1), lessThan);
+    T* pivot = left + (right - left >> 1);
+    pivot = QSort_Partition(context, left, right, pivot, lessThan);
+    QSort(context, left, pivot - 1, lessThan);
+    QSort(context, pivot + 1, right, lessThan);
 }
index 227b1d9..6180ae8 100644 (file)
                FE7134F514D1E7C70008E392 /* LineParameterization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7134F414D1E7C70008E392 /* LineParameterization.cpp */; };
                FE71351314D2E9F50008E392 /* RectUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE71351214D2E9F50008E392 /* RectUtilities.cpp */; };
                FE71358614D309E90008E392 /* EdgeWalker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE71358514D309E90008E392 /* EdgeWalker.cpp */; };
+               FE7413AE14F689E700056D7B /* libopts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2C13E0410900335C58 /* libopts.a */; };
+               FE7413D414F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */; };
+               FE7413D814F691C200056D7B /* EdgeWalker_TestUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */; };
                FEA5F4E21498000C005052F9 /* libports.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA5F4E11497FFF6005052F9 /* libports.a */; };
+               FEA61B0014EF589900B736CB /* libanimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7268144DD3EA0059E97B /* libanimator.a */; };
+               FEA61B2C14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */; };
                FEA671D013C4A21600FE6FC1 /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671CF13C4A21600FE6FC1 /* AGL.framework */; };
                FEA671D813C4A21600FE6FC1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671D713C4A21600FE6FC1 /* Foundation.framework */; };
                FEA671DA13C4A21600FE6FC1 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671D913C4A21600FE6FC1 /* OpenGL.framework */; };
@@ -54,7 +59,6 @@
                FECAA6E114BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FECAA6E014BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp */; };
                FED53C391483CB9400F6359E /* Inline_Tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED53C381483CB9400F6359E /* Inline_Tests.cpp */; };
                FEED7245144DD2250059E97B /* SkEventNotifier.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEED723E144DD2250059E97B /* SkEventNotifier.mm */; };
-               FEED7291144DD45E0059E97B /* libanimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7268144DD3EA0059E97B /* libanimator.a */; };
                FEED7292144DD4610059E97B /* libexperimental.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED726E144DD4050059E97B /* libexperimental.a */; };
                FEED7293144DD4620059E97B /* libskgr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7276144DD4140059E97B /* libskgr.a */; };
                FEED7294144DD4630059E97B /* libgr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7278144DD4140059E97B /* libgr.a */; };
                FEED76EE144F66E90059E97B /* Intersection_Tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEED76ED144F66E90059E97B /* Intersection_Tests.cpp */; };
                FEF87C3C13E0413500335C58 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C1A13E040E000335C58 /* libcore.a */; };
                FEF87C3D13E0413A00335C58 /* libeffects.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2313E040F100335C58 /* libeffects.a */; };
-               FEF87C3E13E0413F00335C58 /* libopts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2C13E0410900335C58 /* libopts.a */; };
                FEF87C3F13E0414400335C58 /* libutils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C3B13E0412600335C58 /* libutils.a */; };
 /* 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 */;
                FE71351214D2E9F50008E392 /* RectUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RectUtilities.cpp; sourceTree = "<group>"; };
                FE71358514D309E90008E392 /* EdgeWalker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalker.cpp; sourceTree = "<group>"; };
                FE713C6114D9879B0008E392 /* TSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSearch.h; sourceTree = "<group>"; };
+               FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalkerPolygons_Test.cpp; sourceTree = "<group>"; };
+               FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalker_TestUtility.cpp; sourceTree = "<group>"; };
+               FE7413DB14F6926D00056D7B /* EdgeWalker_Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EdgeWalker_Test.h; sourceTree = "<group>"; };
                FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ports.xcodeproj; path = ../../out/gyp/ports.xcodeproj; sourceTree = SOURCE_ROOT; };
+               FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalkerRectangles_Test.cpp; sourceTree = "<group>"; };
                FEA670F013C49E2200FE6FC1 /* SkAntiEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAntiEdge.cpp; sourceTree = "<group>"; };
                FEA670F113C49E2200FE6FC1 /* SkAntiEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAntiEdge.h; sourceTree = "<group>"; };
                FEA6710713C4A13900FE6FC1 /* gyp */ = {isa = PBXFileReference; lastKnownFileType = folder; name = gyp; path = ../../out/gyp; sourceTree = SOURCE_ROOT; };
                        files = (
                                FEF87C3C13E0413500335C58 /* libcore.a in Frameworks */,
                                FEF87C3D13E0413A00335C58 /* libeffects.a in Frameworks */,
-                               FEF87C3E13E0413F00335C58 /* libopts.a in Frameworks */,
                                FEF87C3F13E0414400335C58 /* libutils.a in Frameworks */,
                                FEA671D813C4A21600FE6FC1 /* Foundation.framework in Frameworks */,
                                FEA671D013C4A21600FE6FC1 /* AGL.framework in Frameworks */,
                                FEA671DA13C4A21600FE6FC1 /* OpenGL.framework in Frameworks */,
-                               FEED7291144DD45E0059E97B /* libanimator.a in Frameworks */,
                                FEED7292144DD4610059E97B /* libexperimental.a in Frameworks */,
                                FEED7293144DD4620059E97B /* libskgr.a in Frameworks */,
                                FEED7294144DD4630059E97B /* libgr.a in Frameworks */,
                                FEED75DD144DD6590059E97B /* QuartzCore.framework in Frameworks */,
                                FEED75DF144DD6840059E97B /* libz.dylib in Frameworks */,
                                FEA5F4E21498000C005052F9 /* libports.a in Frameworks */,
+                               FEA61B0014EF589900B736CB /* libanimator.a in Frameworks */,
+                               FE7413AE14F689E700056D7B /* libopts.a in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                FECA9A5914B3B09100B35E2C /* CubicParameterization_Test.cpp */,
                                FECAAB7F14BDFAFD00B35E2C /* CubicParameterization_TestUtility.cpp */,
                                FEED7625144F22E20059E97B /* CubicReduceOrder_Test.cpp */,
+                               FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */,
+                               FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */,
+                               FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */,
                                FED53C381483CB9400F6359E /* Inline_Tests.cpp */,
                                FEED7680144F2E480059E97B /* Intersection_Tests.h */,
                                FEED76ED144F66E90059E97B /* Intersection_Tests.cpp */,
                                FECAA6E014BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp */,
                                FEED7673144F2D770059E97B /* TestUtilities.h */,
                                FEED764B144F29BD0059E97B /* TestUtilities.cpp */,
+                               FE7413DB14F6926D00056D7B /* EdgeWalker_Test.h */,
                        );
                        name = Tests;
                        sourceTree = "<group>";
                        isa = PBXGroup;
                        children = (
                                FEF87C2C13E0410900335C58 /* libopts.a */,
+                               FE74136114F6866000056D7B /* libopts_ssse3.a */,
                        );
                        name = Products;
                        sourceTree = "<group>";
 /* End PBXProject section */
 
 /* Begin PBXReferenceProxy section */
+               FE74136114F6866000056D7B /* libopts_ssse3.a */ = {
+                       isa = PBXReferenceProxy;
+                       fileType = archive.ar;
+                       path = libopts_ssse3.a;
+                       remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+               };
                FEA5F4E11497FFF6005052F9 /* libports.a */ = {
                        isa = PBXReferenceProxy;
                        fileType = archive.ar;
                                FE7134F514D1E7C70008E392 /* LineParameterization.cpp in Sources */,
                                FE71351314D2E9F50008E392 /* RectUtilities.cpp in Sources */,
                                FE71358614D309E90008E392 /* EdgeWalker.cpp in Sources */,
+                               FEA61B2C14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp in Sources */,
+                               FE7413D414F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp in Sources */,
+                               FE7413D814F691C200056D7B /* EdgeWalker_TestUtility.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INFOPLIST_FILE = "edge-Info.plist";
                                INSTALL_PATH = "$(HOME)/Applications";
-                               LIBRARY_SEARCH_PATHS = (
-                                       "$(inherited)",
-                                       "\"$(SRCROOT)/../../../xcode/core/build/Debug\"",
-                                       "\"$(SRCROOT)/../../../xcode/maccore/build/Debug\"",
-                               );
+                               LIBRARY_SEARCH_PATHS = "$(inherited)";
                                PREBINDING = NO;
                                PRODUCT_NAME = edge;
                                SKIP_INSTALL = YES;
                                GCC_PREFIX_HEADER = edge_Prefix.pch;
                                INFOPLIST_FILE = "edge-Info.plist";
                                INSTALL_PATH = "$(HOME)/Applications";
-                               LIBRARY_SEARCH_PATHS = (
-                                       "$(inherited)",
-                                       "\"$(SRCROOT)/../../../xcode/core/build/Debug\"",
-                                       "\"$(SRCROOT)/../../../xcode/maccore/build/Debug\"",
-                               );
+                               LIBRARY_SEARCH_PATHS = "$(inherited)";
                                PRODUCT_NAME = edge;
                        };
                        name = Release;