work in progress
authorcaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 5 Mar 2012 22:01:21 +0000 (22:01 +0000)
committercaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 5 Mar 2012 22:01:21 +0000 (22:01 +0000)
of note, all edge walker tests succeed at this point

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

experimental/Intersection/EdgeWalker.cpp
experimental/Intersection/EdgeWalkerPolygons_Test.cpp
experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp [new file with mode: 0644]
experimental/Intersection/Intersection_Tests.cpp
experimental/Intersection/Intersection_Tests.h
experimental/Intersection/LineIntersection.cpp
experimental/Intersection/edge.xcodeproj/project.pbxproj

index 2c1e5f5..771e639 100644 (file)
@@ -14,9 +14,9 @@
 #include "SkTDArray.h"
 #include "TSearch.h"
 
-static bool gShowDebugf = true; // FIXME: remove once debugging is complete
+static bool gShowDebugf = false; // FIXME: remove once debugging is complete
 static bool gShowPath = false;
-static bool gDebugLessThan = true;
+static bool gDebugLessThan = false;
 
 static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
         double aRange[2], double bRange[2]) {
@@ -165,9 +165,9 @@ public:
             if (listIndex >= listCount) {
                 break;
             }
+            int closeEdgeIndex = -listIndex - 1;
             SkPoint firstPt, lastLine[2];
             bool doMove = true;
-            bool closed = false;
             int edgeIndex;
             do {
                 SkPoint* ptArray = fEdges[listIndex].fPts;
@@ -193,7 +193,6 @@ public:
                             lastLine[0] = *start;
                             lastLine[1] = *end;
                             doMove = false;
-                            closed = false;
                             break;
                         }
                         gap = lastLine[1] != *start;
@@ -216,15 +215,6 @@ public:
                             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;
                     default:
                         // FIXME: add other curve types
@@ -237,26 +227,31 @@ public:
                     edgeIndex = fBottoms[listIndex];
                     fBottoms[listIndex] = 0;
                 }
-                if (!edgeIndex) {
+                if (edgeIndex) {
+                    listIndex = abs(edgeIndex) - 1;
+                    if (edgeIndex < 0) {
+                        fTops[listIndex] = 0;
+                    } else {
+                        fBottoms[listIndex] = 0;
+                    }
+                }
+                if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
+                    if (lastLine[1] != firstPt) {
+                        simple.lineTo(lastLine[1].fX, lastLine[1].fY);
+                    }
                     simple.lineTo(firstPt.fX, firstPt.fY);
                     simple.close();
                     if (gShowDebugf) {
-                        SkDebugf("%s close 2 (%g,%g)\n", __FUNCTION__,
-                            firstPt.fX, firstPt.fY);
+                        SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
+                                firstPt.fX, firstPt.fY);
                     }
                     break;
                 }
-                listIndex = abs(edgeIndex) - 1;
-                if (edgeIndex < 0) {
-                    fTops[listIndex] = 0;
-                } else {
-                    fBottoms[listIndex] = 0;
-                }
                 // if this and next edge go different directions 
                 if (advance > 0 ^ edgeIndex < 0) {
                     advance = -advance;
                 }
-            } while (edgeIndex && !closed);
+            } while (edgeIndex);
         } while (true);
     }
     
@@ -396,8 +391,15 @@ struct Bounds : public SkRect {
     }
 };
 
-struct Intercepts {
+class Intercepts {
+public:
+    Intercepts()
+        : fTopIntercepts(0)
+        , fBottomIntercepts(0) {
+    }
     SkTDArray<double> fTs;
+    int fTopIntercepts;
+    int fBottomIntercepts;
 };
 
 struct InEdge {
@@ -407,13 +409,18 @@ struct InEdge {
                 : fBounds.fTop < rh.fBounds.fTop;
     }
 
-    bool add(double* ts, size_t count, ptrdiff_t verbIndex) {
+    void add(double* ts, size_t count, ptrdiff_t verbIndex) {
         // FIXME: in the pathological case where there is a ton of intercepts, binary search?
         bool foundIntercept = false;
         Intercepts& intercepts = fIntercepts[verbIndex];
         for (size_t index = 0; index < count; ++index) {
             double t = ts[index];
-            if (t <= 0 || t >= 1) {
+            if (t <= 0) {
+                fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
+                continue;
+            }
+            if (t >= 1) {
+                fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
                 continue;
             }
             foundIntercept = true;
@@ -424,14 +431,15 @@ struct InEdge {
                     if (delta > 0) {
                         *intercepts.fTs.insert(idx2) = t;
                     }
-                    return foundIntercept;
+                    fContainsIntercepts = true;
+                    return;
                 }
             }
             if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
                 *intercepts.fTs.append() = t;
             }
         }
-        return foundIntercept;
+        fContainsIntercepts |= foundIntercept;
     }
 
     bool cached(const InEdge* edge) {
@@ -483,7 +491,7 @@ struct InEdge {
     // temporary data : move this to a separate struct?
     SkTDArray<const InEdge*> fCached; // list of edges already intercepted
     SkTArray<Intercepts> fIntercepts; // one per verb
-    
+
     // persistent data
     SkTDArray<SkPoint> fPts;
     SkTDArray<uint8_t> fVerbs;
@@ -739,8 +747,9 @@ struct ActiveEdge {
     
     void calcLeft(SkScalar y) {
         // OPTIMIZE: put a kDone_Verb at the end of the verb list?
-        if (done(y))
+        if (fDone || fBelow.fY > y) {
             return; // nothing to do; use last
+        }
         calcLeft();
     }
     
@@ -772,6 +781,7 @@ struct ActiveEdge {
     void init(const InEdge* edge) {
         fWorkEdge.init(edge);
         initT();
+        fBelow.fY = SK_ScalarMin;
         fDone = false;
         fYBottom = SK_ScalarMin;
     }
@@ -792,7 +802,7 @@ struct ActiveEdge {
     // t values, since the same t values could exist intersecting non-coincident
     // edges.
     bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
-        if (fAbove.fX != edge->fAbove.fX || fBelow.fX != edge->fBelow.fX) {
+        if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
             return false;
         }
         uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
@@ -803,14 +813,7 @@ struct ActiveEdge {
         }
         switch (verb) {
             case SkPath::kLine_Verb: {
-                int offset = fDone ? -1 : 1;
-                int edgeOffset = edge->fDone ? -1 : 1;
-                const SkPoint* pts = fWorkEdge.fPts;
-                const SkPoint* edgePts = edge->fWorkEdge.fPts;
-                return (pts->fX - pts[offset].fX)
-                        * (edgePts->fY - edgePts[edgeOffset].fY)
-                        == (pts->fY - pts[offset].fY)
-                        * (edgePts->fX - edgePts[edgeOffset].fX);
+                return true;
             }
             default:
                 // FIXME: add support for all curve types
@@ -886,7 +889,12 @@ static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge)
     active->init(edge);
 }
 
-    // find any intersections in the range of active edges
+// Find any intersections in the range of active edges. A pair of edges, on
+// either side of another edge, may change the winding contribution for part of
+// the edge. 
+// OPTIMIZATION: Another approach would be to keep horizontal edges just for
+// the purpose of computing when edges change their winding contribution, since
+// this is essentially computing the horizontal intersection. 
 static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
     InEdge** testPtr = currentPtr;
     InEdge* test = *testPtr;
@@ -901,8 +909,7 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
                     double wtTs[2];
                     int pts = LineIntersect(wt.fPts, bottom, wtTs);
                     if (pts) {
-                        test->fContainsIntercepts |= test->add(wtTs, pts,
-                                wt.verbIndex());
+                        test->add(wtTs, pts, wt.verbIndex());
                     }
                 } else {
                     // FIXME: add all curve types
@@ -936,10 +943,8 @@ static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
                     double wtTs[2], wnTs[2];
                     int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
                     if (pts) {
-                        test->fContainsIntercepts |= test->add(wtTs, pts,
-                                wt.verbIndex());
-                        next->fContainsIntercepts |= next->add(wnTs, pts,
-                                wn.verbIndex());
+                        test->add(wtTs, pts, wt.verbIndex());
+                        next->add(wnTs, pts, wn.verbIndex());
                     }
                 } else {
                     // FIXME: add all combinations of curve types
@@ -987,6 +992,18 @@ static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
         wt.init(test);
         do {
             const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
+            if (intercepts.fTopIntercepts > 1) {
+                SkScalar yTop = wt.fPts[0].fY;
+                if (yTop > y && bottom > yTop) {
+                    bottom = yTop;
+                }
+            }
+            if (intercepts.fBottomIntercepts > 1) {
+                SkScalar yBottom = wt.fPts[wt.verb()].fY;
+                if (yBottom > y && bottom > yBottom) {
+                    bottom = yBottom;
+                }
+            }
             const SkTDArray<double>& fTs = intercepts.fTs;
             size_t count = fTs.count();
             for (size_t index = 0; index < count; ++index) {
@@ -1099,8 +1116,10 @@ static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
             // and not all at once as is done here, fold this test into the
             // current less than test.
             if (activePtr->swapCoincident(nextPtr, bottom)) {
+                winding -= activePtr->fWorkEdge.winding();
                 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
                 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
+                winding += activePtr->fWorkEdge.winding();
             }
             if (!firstCoincident) {
                 firstCoincident = activePtr;
@@ -1154,7 +1173,8 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
         bool closer = (winding & windingMask) == 0;
         SkASSERT(!opener | !closer);
         bool inWinding = opener | closer;
-        const SkPoint* clipped;
+        SkPoint clippedPts[2];
+        const SkPoint* clipped = NULL;
         uint8_t verb = wt.verb();
         bool moreToDo, aboveBottom;
         do {
@@ -1165,7 +1185,6 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
             do {
                 nextT = activePtr->nextT();
                 if (verb == SkPath::kLine_Verb) {
-                    SkPoint clippedPts[2];
                     // FIXME: obtuse: want efficient way to say 
                     // !currentT && currentT != 1 || !nextT && nextT != 1
                     if (currentT * nextT != 0 || currentT + nextT != 1) {
@@ -1184,6 +1203,11 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
                         }
                         outBuilder.addLine(clipped);
                     }
+                    if (clipped[1].fY > activePtr->fBelow.fY
+                            && bottom >= activePtr->fBelow.fY ) {
+                        activePtr->fAbove = activePtr->fBelow;
+                        activePtr->fBelow = clipped[1];
+                    }
                     activePtr->fSkip = false;
                 } else {
                     // FIXME: add all curve types
@@ -1214,6 +1238,9 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
     InEdge edgeSentinel;
     makeEdgeList(edges, edgeSentinel, edgeList);
     InEdge** currentPtr = edgeList.begin();
+    if (!currentPtr) {
+        return;
+    }
     // walk the sorted edges from top to bottom, computing accumulated winding
     SkTDArray<ActiveEdge> activeEdges;
     OutEdgeBuilder outBuilder(asFill);
index 3ea0e0b..07ce6c8 100644 (file)
@@ -245,6 +245,91 @@ static void testSimplifyTriangle17() {
     comparePaths(path, out);
 }
     
+static void testSimplifyTriangle18() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 3);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplifyTriangle19() {
+    SkPath path, out;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplifyTriangle20() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(3, 2);
+    path.lineTo(0, 3);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplifyTriangle21() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(2, 1);
+    path.lineTo(0, 3);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplifyDegenerateTriangle1() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplifyDegenerateTriangle2() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 2);
+    path.lineTo(3, 3);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
 static void testSimplifyWindingParallelogram() {
     SkPath path, out;
     path.setFillType(SkPath::kWinding_FillType);
@@ -361,6 +446,67 @@ static void testSimplifyNondegenerate4x4Triangles() {
     }
 }
 
+static void testSimplifyDegenerate4x4Triangles() {
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    for (int a = 0; a < 16; ++a) {
+        int ax = a & 0x03;
+        int ay = a >> 2;
+        for (int b = a ; b < 16; ++b) {
+            int bx = b & 0x03;
+            int by = b >> 2;
+            for (int c = a ; c < 16; ++c) {
+                int cx = c & 0x03;
+                int cy = c >> 2;
+                bool abcIsATriangle = (bx - ax) * (cy - ay)
+                        != (by - ay) * (cx - ax);
+                for (int d = 0; d < 16; ++d) {
+                    int dx = d & 0x03;
+                    int dy = d >> 2;
+                    for (int e = d ; e < 16; ++e) {
+                        int ex = e & 0x03;
+                        int ey = e >> 2;
+                        for (int f = d ; f < 16; ++f) {
+                            int fx = f & 0x03;
+                            int fy = f >> 2;
+                            if (abcIsATriangle && (ex - dx) * (fy - dy)
+                                    != (ey - dy) * (fx - dx)) {
+                                continue;
+                            }
+                            SkPath path, out;
+                            path.setFillType(SkPath::kWinding_FillType);
+                            path.moveTo(ax, ay);
+                            path.lineTo(bx, by);
+                            path.lineTo(cx, cy);
+                            path.close();
+                            path.moveTo(dx, dy);
+                            path.lineTo(ex, ey);
+                            path.lineTo(fx, fy);
+                            path.close();
+                            if (1) {
+                                char* str = pathStr;
+                                str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+                                str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
+                                str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
+                                str += sprintf(str, "    path.close();\n");
+                                str += sprintf(str, "    path.moveTo(%d, %d);\n", dx, dy);
+                                str += sprintf(str, "    path.lineTo(%d, %d);\n", ex, ey);
+                                str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+                                str += sprintf(str, "    path.close();");
+                            }
+                            simplify(path, true, out);
+                            comparePaths(path, out);
+                            path.setFillType(SkPath::kEvenOdd_FillType);
+                            simplify(path, true, out);
+                            comparePaths(path, out);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
 static void testPathTriangleRendering() {
     SkPath one, two;
     one.moveTo(0, 0);
@@ -382,6 +528,12 @@ static void testPathTriangleRendering() {
 }
 
 static void (*simplifyTests[])() = {
+    testSimplifyDegenerateTriangle2,
+    testSimplifyDegenerateTriangle1,
+    testSimplifyTriangle21,
+    testSimplifyTriangle20,
+    testSimplifyTriangle19,
+    testSimplifyTriangle18,
     testSimplifyTriangle17,
     testSimplifyTriangle16,
     testSimplifyTriangle15,
@@ -401,6 +553,7 @@ static void (*simplifyTests[])() = {
     testSimplifyTriangle2,
     testSimplifyWindingParallelogram,
     testSimplifyXorParallelogram,
+    testSimplifyDegenerate4x4Triangles,
     testSimplifyNondegenerate4x4Triangles,
     testPathTriangleRendering,
 };
diff --git a/experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp b/experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp
new file mode 100644 (file)
index 0000000..b22e1a3
--- /dev/null
@@ -0,0 +1,108 @@
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+
+static void testSimplifyQuad1() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(3, 2);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 3);
+    path.lineTo(1, 3);
+    path.lineTo(1, 3);
+    path.close();
+    simplify(path, true, out);
+    comparePaths(path, out);
+}
+
+static void testSimplify4x4Quadralaterals() {
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    for (int a = 0; a < 16; ++a) {
+        int ax = a & 0x03;
+        int ay = a >> 2;
+        for (int b = a ; b < 16; ++b) {
+            int bx = b & 0x03;
+            int by = b >> 2;
+            for (int c = b ; c < 16; ++c) {
+                int cx = c & 0x03;
+                int cy = c >> 2;
+                for (int d = c; d < 16; ++d) {
+                    int dx = d & 0x03;
+                    int dy = d >> 2;
+                    for (int e = 0 ; e < 16; ++e) {
+                        int ex = e & 0x03;
+                        int ey = e >> 2;
+                        for (int f = e ; f < 16; ++f) {
+                            int fx = f & 0x03;
+                            int fy = f >> 2;
+                            for (int g = f ; g < 16; ++g) {
+                                int gx = g & 0x03;
+                                int gy = g >> 2;
+                                for (int h = g ; g < 16; ++g) {
+                                    int hx = h & 0x03;
+                                    int hy = h >> 2;
+        SkPath path, out;
+        path.setFillType(SkPath::kWinding_FillType);
+        path.moveTo(ax, ay);
+        path.lineTo(bx, by);
+        path.lineTo(cx, cy);
+        path.lineTo(dx, dy);
+        path.close();
+        path.moveTo(ex, ey);
+        path.lineTo(fx, fy);
+        path.lineTo(gx, gy);
+        path.lineTo(hx, hy);
+        path.close();
+        if (1) {
+            char* str = pathStr;
+            str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
+            str += sprintf(str, "    path.close();\n");
+            str += sprintf(str, "    path.moveTo(%d, %d);\n", ex, ey);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", gx, gy);
+            str += sprintf(str, "    path.lineTo(%d, %d);\n", hx, hy);
+            str += sprintf(str, "    path.close();");
+        }
+        simplify(path, true, out);
+        comparePaths(path, out);
+        path.setFillType(SkPath::kEvenOdd_FillType);
+        simplify(path, true, out);
+        comparePaths(path, out);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+
+static void (*simplifyTests[])() = {
+    testSimplifyQuad1,
+    testSimplify4x4Quadralaterals,
+};
+
+static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
+
+static void (*firstTest)() = 0;
+
+void SimplifyQuadralateralPaths_Test() {
+    size_t index = 0;
+    if (firstTest) {
+        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
+            ++index;
+        }
+    }
+    for ( ; index < simplifyTestsCount; ++index) {
+        (*simplifyTests[index])();
+    }
+}
index 42f4a26..2a1620e 100644 (file)
@@ -17,8 +17,9 @@ void Intersection_Tests() {
     LineQuadraticIntersection_Test();
     LineCubicIntersection_Test();
 
-    SimplifyPolygonPaths_Test();
     SimplifyRectangularPaths_Test();
+    SimplifyPolygonPaths_Test();
+    SimplifyQuadralateralPaths_Test();
 
     QuadraticCoincidence_Test();
     QuadraticReduceOrder_Test();
index 9def8b5..f007dda 100644 (file)
@@ -12,6 +12,7 @@ void LineIntersection_Test();
 void LineParameter_Test();
 void LineQuadraticIntersection_Test();
 void SimplifyPolygonPaths_Test();
+void SimplifyQuadralateralPaths_Test();
 void SimplifyRectangularPaths_Test();
 void QuadraticBezierClip_Test();
 void QuadraticCoincidence_Test();
index 99b9e82..4d80606 100644 (file)
@@ -59,7 +59,7 @@ int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]
                 bRange[0] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
                 bRange[1] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
             }
-            return 2;
+            return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
         }
     }
     double ab0y = a[0].y - b[0].y;
index 6180ae8..cf433c6 100644 (file)
@@ -58,6 +58,7 @@
                FECAA6C714BDCE9B00B35E2C /* QuadraticIntersection_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FECAA6C614BDCE9B00B35E2C /* QuadraticIntersection_Test.cpp */; };
                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 */; };
+               FED865F915056A79006F4508 /* EdgeWalkerQuadralaterals_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED865F815056A79006F4508 /* EdgeWalkerQuadralaterals_Test.cpp */; };
                FEED7245144DD2250059E97B /* SkEventNotifier.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEED723E144DD2250059E97B /* SkEventNotifier.mm */; };
                FEED7292144DD4610059E97B /* libexperimental.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED726E144DD4050059E97B /* libexperimental.a */; };
                FEED7293144DD4620059E97B /* libskgr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7276144DD4140059E97B /* libskgr.a */; };
                FECAAB7F14BDFAFD00B35E2C /* CubicParameterization_TestUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CubicParameterization_TestUtility.cpp; sourceTree = "<group>"; };
                FECAACA614BE1C6100B35E2C /* QuadraticParameterization_TestUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QuadraticParameterization_TestUtility.cpp; sourceTree = "<group>"; };
                FED53C381483CB9400F6359E /* Inline_Tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Inline_Tests.cpp; sourceTree = "<group>"; };
+               FED865F815056A79006F4508 /* EdgeWalkerQuadralaterals_Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalkerQuadralaterals_Test.cpp; sourceTree = "<group>"; };
                FEED723C144DD2250059E97B /* SampleApp.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = SampleApp.xib; path = ../../src/utils/mac/SampleApp.xib; sourceTree = SOURCE_ROOT; };
                FEED723D144DD2250059E97B /* SampleAppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SampleAppDelegate.mm; path = ../../src/utils/mac/SampleAppDelegate.mm; sourceTree = SOURCE_ROOT; };
                FEED723E144DD2250059E97B /* SkEventNotifier.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkEventNotifier.mm; path = ../../src/utils/mac/SkEventNotifier.mm; sourceTree = SOURCE_ROOT; };
                                FEED7673144F2D770059E97B /* TestUtilities.h */,
                                FEED764B144F29BD0059E97B /* TestUtilities.cpp */,
                                FE7413DB14F6926D00056D7B /* EdgeWalker_Test.h */,
+                               FED865F815056A79006F4508 /* EdgeWalkerQuadralaterals_Test.cpp */,
                        );
                        name = Tests;
                        sourceTree = "<group>";
                                FEA61B2C14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp in Sources */,
                                FE7413D414F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp in Sources */,
                                FE7413D814F691C200056D7B /* EdgeWalker_TestUtility.cpp in Sources */,
+                               FED865F915056A79006F4508 /* EdgeWalkerQuadralaterals_Test.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };