shape ops work in progress

overhaul coincident handling

git-svn-id: http://skia.googlecode.com/svn/trunk@6695 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
index e3ea52e..5489a8b 100644
--- a/experimental/Intersection/EdgeWalker_TestUtility.cpp
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -29,25 +29,133 @@
     "\n"
     "var testDivs = [\n";
 
+static const char* opStrs[] = {
+    "kDifference_Op",
+    "kIntersect_Op",
+    "kUnion_Op",
+    "kXor_Op",
+};
+
+static const char* opSuffixes[] = {
+    "d",
+    "i",
+    "u",
+    "x",
+};
+
 static const char preferredFilename[] = "/flash/debug/XX.txt";
 static const char backupFilename[] = "../../experimental/Intersection/debugXX.txt";
 
 static bool gShowPath = false;
 static bool gComparePaths = true;
-//static bool gDrawLastAsciiPaths = true;
-//static bool gDrawAllAsciiPaths = false;
 static bool gShowOutputProgress = false;
-static bool gShowAsciiPaths = true;
-static bool gComparePathsAssert = false;
+static bool gComparePathsAssert = true;
 static bool gPathStrAssert = true;
 static bool gUsePhysicalFiles = false;
 
-void showPath(const SkPath& path, const char* str) {
-    SkDebugf("%s\n", !str ? "original:" : str);
-    SkPath::Iter iter(path, true);
+static bool isRectContour(SkPath::Iter& iter, SkRect& rect, SkPath::Direction& direction) {
+    int corners = 0;
+    SkPoint first, last;
+    first.set(0, 0);
+    last.set(0, 0);
+    int firstDirection = 0;
+    int lastDirection = 0;
+    int nextDirection = 0;
+    bool closedOrMoved = false;
+    bool autoClose = false;
+    rect.setEmpty();
+    uint8_t verb;
+    SkPoint data[4];
+    SkTDArray<SkPoint> sides;
+    bool empty = true;
+    while ((verb = iter.next(data)) != SkPath::kDone_Verb && !autoClose) {
+        empty = false;
+        SkPoint* pts = &data[1];
+        switch (verb) {
+            case SkPath::kClose_Verb:
+                pts = &last;
+                autoClose = true;
+            case SkPath::kLine_Verb: {
+                SkScalar left = last.fX;
+                SkScalar top = last.fY;
+                SkScalar right = pts->fX;
+                SkScalar bottom = pts->fY;
+                *sides.append() = *pts;
+                ++pts;
+                if (left != right && top != bottom) {
+                    return false; // diagonal
+                }
+                if (left == right && top == bottom) {
+                    break; // single point on side OK
+                }
+                nextDirection = (left != right) << 0 |
+                    (left < right || top < bottom) << 1;
+                if (0 == corners) {
+                    firstDirection = nextDirection;
+                    first = last;
+                    last = pts[-1];
+                    corners = 1;
+                    closedOrMoved = false;
+                    break;
+                }
+                if (closedOrMoved) {
+                    return false; // closed followed by a line
+                }
+                if (autoClose && nextDirection == firstDirection) {
+                    break; // colinear with first
+                }
+                closedOrMoved = autoClose;
+                if (lastDirection != nextDirection) {
+                    if (++corners > 4) {
+                        return false; // too many direction changes
+                    }
+                }
+                last = pts[-1];
+                if (lastDirection == nextDirection) {
+                    break; // colinear segment
+                }
+                // Possible values for corners are 2, 3, and 4.
+                // When corners == 3, nextDirection opposes firstDirection.
+                // Otherwise, nextDirection at corner 2 opposes corner 4.
+                int turn = firstDirection ^ (corners - 1);
+                int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
+                if ((directionCycle ^ turn) != nextDirection) {
+                    return false; // direction didn't follow cycle
+                }
+                break;
+            }
+            case SkPath::kQuad_Verb:
+            case SkPath::kCubic_Verb:
+                return false; // quadratic, cubic not allowed
+            case SkPath::kMove_Verb:
+                last = *pts++;
+                *sides.append() = last;
+                closedOrMoved = true;
+                break;
+        }
+        lastDirection = nextDirection;
+    }
+    // Success if 4 corners and first point equals last
+    bool result = 4 == corners && (first == last || autoClose);
+    if (result) {
+        direction = firstDirection == (lastDirection + 1 & 3) ? SkPath::kCCW_Direction
+                : SkPath::kCW_Direction;
+        rect.set(&sides[0], sides.count());
+    } else {
+        rect.setEmpty();
+    }
+    return !empty;
+}
+
+static void showPathContour(SkPath::Iter& iter, bool skip) {
     uint8_t verb;
     SkPoint pts[4];
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        if (skip) {
+            if (verb == SkPath::kClose_Verb) {
+                return;
+            }
+        }
         switch (verb) {
             case SkPath::kMove_Verb:
                 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pts[0].fX, pts[0].fY);
@@ -66,7 +174,7 @@
                 break;
             case SkPath::kClose_Verb:
                 SkDebugf("path.close();\n");
-                continue;
+                return;
             default:
                 SkDEBUGFAIL("bad verb");
                 return;
@@ -74,8 +182,32 @@
     }
 }
 
-static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
-        SkBitmap& bits, int& error2x2) {
+void showPath(const SkPath& path, const char* str) {
+    SkDebugf("%s\n", !str ? "original:" : str);
+    SkPath::Iter iter(path, true);
+    SkTDArray<SkRect> rects;
+    SkTDArray<SkPath::Direction> directions;
+    SkRect rect;
+    SkPath::Direction direction;
+    while (isRectContour(iter, rect, direction)) {
+        *rects.append() = rect;
+        *directions.append() = direction;
+    }
+    iter.setPath(path, true);
+    for (int contour = 0; contour < rects.count(); ++contour) {
+        const SkRect& rect = rects[contour];
+        bool useRect = !rect.isEmpty();
+        showPathContour(iter, useRect);
+        if (useRect) {
+            SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
+                    rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
+                    ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
+        }
+    }
+}
+
+static int pathsDrawTheSame(const SkPath& one, const SkPath& two, 
+        SkBitmap& bits, SkPath& scaledOne, SkPath& scaledTwo, int& error2x2) {
     const int bitWidth = 64;
     const int bitHeight = 64;
     if (bits.width() == 0) {
@@ -98,7 +230,6 @@
     SkMatrix scale;
     scale.reset();
     scale.preScale(hScale, vScale);
-    SkPath scaledOne, scaledTwo;
     one.transform(scale, &scaledOne);
     two.transform(scale, &scaledTwo);
     const SkRect& bounds1 = scaledOne.getBounds();
@@ -134,9 +265,6 @@
     if (errors2 >= 6 || errors > 160) {
         SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
     }
-    if (errors2 >= 7) {
-        drawAsciiPaths(scaledOne, scaledTwo, true);
-    }
     error2x2 = errors2;
     return errors;
 }
@@ -145,10 +273,6 @@
     if (!drawPaths) {
         return true;
     }
-    if (gShowAsciiPaths) {
-        showPath(one, "one:");
-        showPath(two, "two:");
-    }
     const SkRect& bounds1 = one.getBounds();
     const SkRect& bounds2 = two.getBounds();
     SkRect larger = bounds1;
@@ -193,17 +317,58 @@
     return true;
 }
 
+static void showSimplifiedPath(const SkPath& one, const SkPath& two, 
+        const SkPath& scaledOne, const SkPath& scaledTwo) {
+    showPath(one, "original:");
+    showPath(two, "simplified:");
+    drawAsciiPaths(scaledOne, scaledTwo, true);
+}
+
 int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap) {
     int errors2x2;
-    int errors = pathsDrawTheSame(one, two, bitmap, errors2x2);
+    SkPath scaledOne, scaledTwo;
+    int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
     if (errors2x2 == 0) {
         return 0;
     }
     const int MAX_ERRORS = 8;
+    if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
+        showSimplifiedPath(one, two, scaledOne, scaledTwo);
+    }
     if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
         SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
-        showPath(one);
-        showPath(two, "simplified:");
+        showSimplifiedPath(one, two, scaledOne, scaledTwo);
+        SkASSERT(0);
+    }
+    return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
+}
+
+static void showShapeOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b, 
+        const SkPath& scaledOne, const SkPath& scaledTwo, const ShapeOp shapeOp) {
+    SkASSERT((unsigned) shapeOp < sizeof(opStrs) / sizeof(opStrs[0]));
+    showPath(a, "minuend:");
+    SkDebugf("op: %s\n", opStrs[shapeOp]);
+    showPath(b, "subtrahend:");
+    showPath(one, "region:");
+    showPath(two, "op result:");
+    drawAsciiPaths(scaledOne, scaledTwo, true);
+}
+
+int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
+        const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
+    int errors2x2;
+    SkPath scaledOne, scaledTwo;
+    int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
+    if (errors2x2 == 0) {
+        return 0;
+    }
+    const int MAX_ERRORS = 8;
+    if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
+        showShapeOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp);
+    }
+    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
+        SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
+        showShapeOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp);
         SkASSERT(0);
     }
     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
@@ -304,7 +469,7 @@
     rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
     rgnOut.getBoundaryPath(&pathOut);
     SkBitmap bitmap;
-    int result = comparePaths(pathOut, out, bitmap);
+    int result = comparePaths(pathOut, out, bitmap, a, b, shapeOp);
     if (result && gPathStrAssert) {
         SkASSERT(0);
     }
@@ -467,20 +632,6 @@
     outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, outRam);
 }
 
-static const char* opStrs[] = {
-    "kDifference_Op",
-    "kIntersect_Op",
-    "kUnion_Op",
-    "kXor_Op",
-};
-
-static const char* opSuffixes[] = {
-    "d",
-    "i",
-    "u",
-    "x",
-};
-
 void outputProgress(const State4& state, const char* pathStr, ShapeOp op) {
     SkString testFunc("testShapeOp(path, pathB, ");
     testFunc += opStrs[op];