aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-10-09 14:11:58 +0000
committerGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-10-09 14:11:58 +0000
commit6aea33f92c611d6fdc88bc2352c5c966168af83b (patch)
tree0afc954993f542c618413c659d21f1680065221a
parent9598f4256d729434a9e7273a7de1e4876eaacee9 (diff)
checkpoint for shape ops
at minimum, the unit tests in SimplyNew_Test pass git-svn-id: http://skia.googlecode.com/svn/trunk@5860 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--experimental/Intersection/DataTypes.h1
-rw-r--r--experimental/Intersection/EdgeDemo.cpp225
-rw-r--r--experimental/Intersection/EdgeDemoApp.mm3
-rw-r--r--experimental/Intersection/EdgeWalker_TestUtility.cpp16
-rw-r--r--experimental/Intersection/Intersection_Tests.cpp6
-rw-r--r--experimental/Intersection/Intersections.h2
-rw-r--r--experimental/Intersection/QuadraticImplicit.cpp53
-rw-r--r--experimental/Intersection/QuadraticIntersection_Test.cpp33
-rw-r--r--experimental/Intersection/QuarticRoot.cpp24
-rw-r--r--experimental/Intersection/Simplify.cpp501
-rw-r--r--experimental/Intersection/SimplifyAngle_Test.cpp115
-rw-r--r--experimental/Intersection/SimplifyNew_Test.cpp2
-rw-r--r--experimental/Intersection/bc.htm7
-rw-r--r--experimental/Intersection/op.htm552
-rw-r--r--gyp/shapeops_tool.gyp45
15 files changed, 1236 insertions, 349 deletions
diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h
index c87beb033b..6c2ac53595 100644
--- a/experimental/Intersection/DataTypes.h
+++ b/experimental/Intersection/DataTypes.h
@@ -82,6 +82,7 @@ inline bool approximately_equal(double x, double y) {
if (approximately_zero(x - y)) {
return true;
}
+ // FIXME: since no other function uses ULPS, this one shouldn't either
return AlmostEqualUlps((float) x, (float) y, UlpsEpsilon);
}
diff --git a/experimental/Intersection/EdgeDemo.cpp b/experimental/Intersection/EdgeDemo.cpp
index 33aae21750..14ede5ba3c 100644
--- a/experimental/Intersection/EdgeDemo.cpp
+++ b/experimental/Intersection/EdgeDemo.cpp
@@ -4,6 +4,35 @@
#import "SkCanvas.h"
#import "SkPaint.h"
+extern void showPath(const SkPath& path, const char* str);
+
+static bool drawPaths(SkCanvas* canvas, const SkPath& path, bool useOld)
+{
+ SkPath out;
+#define SHOW_PATH 0
+#if SHOW_PATH
+ showPath(path, "original:");
+#endif
+ if (useOld) {
+ simplify(path, true, out);
+ } else {
+ simplifyx(path, out);
+ }
+#if SHOW_PATH
+ showPath(out, "simplified:");
+#endif
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // paint.setStrokeWidth(6);
+ // paint.setColor(0x1F003f7f);
+ // canvas->drawPath(path, paint);
+ paint.setColor(0xFF305F00);
+ paint.setStrokeWidth(1);
+ canvas->drawPath(out, paint);
+ return true;
+}
+
// Three circles bounce inside a rectangle. The circles describe three, four
// or five points which in turn describe a polygon. The polygon points
// bounce inside the circles. The circles rotate and scale over time. The
@@ -26,7 +55,7 @@ static bool drawCircles(SkCanvas* canvas, int step, bool useOld)
pts[c * 8 + p * 2 + 1] = abs(110 - ((step + c * 223 + p * 17) % 230));
}
}
- SkPath path, out;
+ SkPath path;
for (c = 0; c < circles; ++c) {
for (p = 0; p < 4; ++p) {
SkScalar x = pts[c * 8 + p * 2];
@@ -49,23 +78,7 @@ static bool drawCircles(SkCanvas* canvas, int step, bool useOld)
}
path.close();
}
- showPath(path, "original:");
- if (useOld) {
- simplify(path, true, out);
- } else {
- simplifyx(path, out);
- }
- showPath(out, "simplified:");
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(3);
- paint.setColor(0x3F007fbF);
- canvas->drawPath(path, paint);
- paint.setColor(0xFF60FF00);
- paint.setStrokeWidth(1);
- canvas->drawPath(out, paint);
- return true;
+ return drawPaths(canvas, path, useOld);
}
static void createStar(SkPath& path, SkScalar innerRadius, SkScalar outerRadius,
@@ -89,7 +102,7 @@ static void createStar(SkPath& path, SkScalar innerRadius, SkScalar outerRadius,
static bool drawStars(SkCanvas* canvas, int step, bool useOld)
{
- SkPath path, out;
+ SkPath path;
const int stars = 25;
int pts[stars];
// static bool initialize = true;
@@ -134,43 +147,171 @@ static bool drawStars(SkCanvas* canvas, int step, bool useOld)
createStar(path, innerRadius[s] / 4.0f, outerRadius[s] / 4.0f,
angles[s], pts[s], locs[s]);
}
-#define SHOW_PATH 0
-#if SHOW_PATH
- showPath(path, "original:");
-#endif
-#define TEST_SIMPLIFY 01
-#if TEST_SIMPLIFY
- if (useOld) {
- simplify(path, true, out);
+ return drawPaths(canvas, path, useOld);
+}
+
+static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
+ // capture everything in a desired rectangle
+ SkPath tiny;
+ bool closed = true;
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ int count = 0;
+ SkPoint lastPt;
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ count = 0;
+ break;
+ case SkPath::kLine_Verb:
+ count = 1;
+ break;
+ case SkPath::kQuad_Verb:
+ count = 2;
+ break;
+ case SkPath::kCubic_Verb:
+ count = 3;
+ break;
+ case SkPath::kClose_Verb:
+ if (!closed) {
+ tiny.close();
+ closed = true;
+ }
+ count = 0;
+ break;
+ default:
+ SkDEBUGFAIL("bad verb");
+ }
+ if (!count) {
+ continue;
+ }
+ SkRect bounds;
+ bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
+ for (int i = 1; i <= count; ++i) {
+ bounds.growToInclude(pts[i].fX + 0.1f, pts[i].fY + 0.1f);
+ }
+ if (!SkRect::Intersects(target, bounds)) {
+ continue;
+ }
+ if (closed) {
+ tiny.moveTo(pts[0].fX, pts[0].fY);
+ closed = false;
+ } else if (pts[0] != lastPt) {
+ tiny.lineTo(pts[0].fX, pts[0].fY);
+ }
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ tiny.lineTo(pts[1].fX, pts[1].fY);
+ lastPt = pts[1];
+ break;
+ case SkPath::kQuad_Verb:
+ tiny.quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+ lastPt = pts[2];
+ break;
+ case SkPath::kCubic_Verb:
+ tiny.cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
+ lastPt = pts[3];
+ break;
+ default:
+ SkDEBUGFAIL("bad verb");
+ }
+ }
+ if (!closed) {
+ tiny.close();
+ }
+ if (show) {
+ showPath(tiny, NULL);
+ SkDebugf("simplified:\n");
+ }
+ SkPath out;
+ simplifyx(tiny, out);
+}
+
+static void tryRonco(const SkPath& path) {
+ const SkRect& overall = path.getBounds();
+ const int divs = 50;
+ SkScalar cellWidth = overall.width() / divs * 2;
+ SkScalar cellHeight = overall.height() / divs * 2;
+ SkRect target;
+ if (true) {
+ int xDiv = 21;
+ int yDiv = 9;
+ target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
+ overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
+ cellWidth, cellHeight);
+ tryRoncoOnce(path, target, true);
} else {
+ for (int xDiv = 0; xDiv < divs; ++xDiv) {
+ for (int yDiv = 0; yDiv < divs; ++yDiv) {
+ target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
+ overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
+ cellWidth, cellHeight);
+ tryRoncoOnce(path, target, false);
+ }
+ }
+ }
+}
+
+static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
+{
+ SkPath path;
+ const int width = 640;
+ const int height = 480;
+ const char testStr[] = "Merge";
+ const int testStrLen = sizeof(testStr) - 1;
+ SkPoint textPos[testStrLen];
+ SkScalar widths[testStrLen];
+ SkPaint paint;
+ paint.setTextSize(40);
+ paint.setAntiAlias(true);
+ paint.getTextWidths(testStr, testStrLen, widths, NULL);
+ SkScalar running = 0;
+ for (int x = 0; x < testStrLen; ++x) {
+ SkScalar width = widths[x];
+ widths[x] = running;
+ running += width;
+ }
+ SkScalar bias = (width - widths[testStrLen - 1]) / 2;
+ for (int x = 0; x < testStrLen; ++x) {
+ textPos[x].fX = bias + widths[x];
+ textPos[x].fY = height / 2;
+ }
+ paint.setTextSize(40 + step / 100.0f);
+#if 0
+ for (int mask = 0; mask < 1 << testStrLen; ++mask) {
+ char maskStr[testStrLen];
+ // mask = 26;
+ for (int letter = 0; letter < testStrLen; ++letter) {
+ maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
+ }
+ paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
+ showPath(path, NULL);
+ SkDebugf("%d simplified:\n", mask);
+ SkPath out;
simplifyx(path, out);
}
#endif
-#if SHOW_PATH
- showPath(out, "simplified:");
+ paint.getPosTextPath(testStr, testStrLen, textPos, &path);
+#if 1
+ tryRonco(path);
#endif
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(6);
- paint.setColor(0x1F003f7f);
- canvas->drawPath(path, paint);
- paint.setColor(0xFF305F00);
- paint.setStrokeWidth(1);
-#if TEST_SIMPLIFY
- canvas->drawPath(out, paint);
+#if 1
+ showPath(path, NULL);
+ SkDebugf("simplified:\n");
#endif
- return true;
+ return drawPaths(canvas, path, false);
}
static bool (*drawDemos[])(SkCanvas* , int , bool ) = {
drawStars,
- drawCircles
+ drawCircles,
+ drawLetters,
};
static size_t drawDemosCount = sizeof(drawDemos) / sizeof(drawDemos[0]);
-static bool (*firstTest)(SkCanvas* , int , bool) = 0;
+static bool (*firstTest)(SkCanvas* , int , bool) = drawLetters;
bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld) {
diff --git a/experimental/Intersection/EdgeDemoApp.mm b/experimental/Intersection/EdgeDemoApp.mm
index 23d8c5a01f..340ded56c2 100644
--- a/experimental/Intersection/EdgeDemoApp.mm
+++ b/experimental/Intersection/EdgeDemoApp.mm
@@ -16,7 +16,8 @@ public:
};
protected:
virtual void onDraw(SkCanvas* canvas) {
- static int step = 0; // useNew triggers error at 23275
+ static int step = 15064; // drawLetters first error
+ // drawStars triggers error at 23275
// error is not easy to debug in its current state
static double seconds;
if (step == -1) {
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
index 397bfcacbc..e236cca455 100644
--- a/experimental/Intersection/EdgeWalker_TestUtility.cpp
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -40,6 +40,7 @@ static bool gShowOutputProgress = false;
static bool gShowAsciiPaths = true;
static bool gComparePathsAssert = false;
static bool gPathStrAssert = true;
+static bool gUsePhysicalFiles = false;
void showPath(const SkPath& path, const char* str) {
SkDebugf("%s\n", !str ? "original:" : str);
@@ -291,7 +292,7 @@ static int threadIndex;
State4 threadState[maxThreadsAllocated];
static int testNumber;
static const char* testName;
-static bool debugThreads = true;
+static bool debugThreads = false;
State4* State4::queue = NULL;
pthread_mutex_t State4::addQueue = PTHREAD_MUTEX_INITIALIZER;
@@ -421,12 +422,17 @@ void outputProgress(const State4& state, const char* pathStr, SkPath::FillType p
}
SkDebugf("%s\n", pathStr);
}
- SkFILEWStream outFile(state.filename);
- if (!outFile.isValid()) {
- SkASSERT(0);
+ if (gUsePhysicalFiles) {
+ SkFILEWStream outFile(state.filename);
+ if (!outFile.isValid()) {
+ SkASSERT(0);
+ return;
+ }
+ outputToStream(state, pathStr, pathFillType, outFile);
return;
}
- outputToStream(state, pathStr, pathFillType, outFile);
+ SkFILEWStream outRam(state.filename);
+ outputToStream(state, pathStr, pathFillType, outRam);
}
static void writeTestName(SkPath::FillType pathFillType, SkWStream& outFile) {
diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp
index 36582fe317..b3dcb06cc5 100644
--- a/experimental/Intersection/Intersection_Tests.cpp
+++ b/experimental/Intersection/Intersection_Tests.cpp
@@ -14,9 +14,11 @@ void cubecode_test(int test);
void Intersection_Tests() {
int testsRun = 0;
+ SimplifyNew_Test();
+ QuadraticIntersection_Test();
+ SimplifyAngle_Test();
QuarticRoot_Test();
// QuadraticIntersection_Test();
- SimplifyNew_Test();
Simplify4x4QuadraticsThreaded_Test(testsRun);
QuadLineIntersectThreaded_Test(testsRun);
LineQuadraticIntersection_Test();
@@ -25,12 +27,10 @@ void Intersection_Tests() {
SimplifyDegenerate4x4TrianglesThreaded_Test(testsRun);
Simplify4x4QuadralateralsThreaded_Test(testsRun);
SkDebugf("%s total testsRun=%d\n", __FUNCTION__, testsRun);
- SimplifyAngle_Test();
QuadraticBezierClip_Test();
SimplifyFindNext_Test();
SimplifyFindTop_Test();
QuadraticReduceOrder_Test();
- QuadraticIntersection_Test();
SimplifyAddIntersectingTs_Test();
cubecode_test(1);
diff --git a/experimental/Intersection/Intersections.h b/experimental/Intersection/Intersections.h
index a09cfcda9c..c1421e3603 100644
--- a/experimental/Intersection/Intersections.h
+++ b/experimental/Intersection/Intersections.h
@@ -16,6 +16,7 @@ public:
, fUsed2(0)
, fCoincidentUsed(0)
, fSwap(0)
+ , fFlip(0)
{
// OPTIMIZE: don't need to be initialized in release
bzero(fT, sizeof(fT));
@@ -188,6 +189,7 @@ public:
int fUsed;
int fUsed2;
int fCoincidentUsed;
+ int fFlip;
private:
int fSwap;
};
diff --git a/experimental/Intersection/QuadraticImplicit.cpp b/experimental/Intersection/QuadraticImplicit.cpp
index 835b3bf71f..268d7d3f62 100644
--- a/experimental/Intersection/QuadraticImplicit.cpp
+++ b/experimental/Intersection/QuadraticImplicit.cpp
@@ -64,7 +64,55 @@ static void addValidRoots(const double roots[4], const int count, const int side
}
}
+static bool onlyEndPtsInCommon(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+ for (int oddMan = 0; oddMan < 3; ++oddMan) {
+ const _Point* endPt[2];
+ for (int opp = 1; opp < 3; ++opp) {
+ int end = oddMan ^ opp;
+ if (end == 3) {
+ end = opp;
+ }
+ endPt[opp - 1] = &q1[end];
+ }
+ double origX = endPt[0]->x;
+ double origY = endPt[0]->y;
+ double adj = endPt[1]->x - origX;
+ double opp = endPt[1]->y - origY;
+ double sign = (q1[oddMan].y - origY) * adj - (q1[oddMan].x - origX) * opp;
+ assert(!approximately_zero(sign));
+ for (int n = 0; n < 3; ++n) {
+ double test = (q2[n].y - origY) * adj - (q2[n].x - origX) * opp;
+ if (test * sign > 0) {
+ goto tryNextHalfPlane;
+ }
+ }
+ for (int i1 = 0; i1 < 3; i1 += 2) {
+ for (int i2 = 0; i2 < 3; i2 += 2) {
+ if (q1[i1] == q2[i2]) {
+ i.insertOne(i1 >> 1, 0);
+ i.insertOne(i2 >> 1, 1);
+ }
+ }
+ }
+ assert(i.fUsed < 3);
+ return true;
+tryNextHalfPlane:
+ ;
+ }
+ return false;
+}
+
bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+ // if the quads share an end point, check to see if they overlap
+
+ if (onlyEndPtsInCommon(q1, q2, i)) {
+ assert(i.insertBalanced());
+ return i.intersected();
+ }
QuadImplicitForm i1(q1);
QuadImplicitForm i2(q2);
if (i1.implicit_match(i2)) {
@@ -100,7 +148,9 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
addValidRoots(roots2, rootCount, 1, i);
_Point pts[4];
bool matches[4];
+ int flipCheck[4];
int index, ndex2;
+ int flipIndex = 0;
for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
xy_at_t(q2, i.fT[1][ndex2], pts[ndex2].x, pts[ndex2].y);
matches[ndex2] = false;
@@ -110,6 +160,8 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
xy_at_t(q1, i.fT[0][index], xy.x, xy.y);
for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
if (approximately_equal(pts[ndex2].x, xy.x) && approximately_equal(pts[ndex2].y, xy.y)) {
+ assert(flipIndex < 4);
+ flipCheck[flipIndex++] = ndex2;
matches[ndex2] = true;
goto next;
}
@@ -131,6 +183,7 @@ bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
}
++ndex2;
}
+ i.fFlip = i.fUsed >= 2 && flipCheck[0] > flipCheck[1];
assert(i.insertBalanced());
return i.intersected();
}
diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp
index 08736cc948..e077bd5f93 100644
--- a/experimental/Intersection/QuadraticIntersection_Test.cpp
+++ b/experimental/Intersection/QuadraticIntersection_Test.cpp
@@ -59,6 +59,10 @@ static void standardTestCases() {
}
static const Quadratic testSet[] = {
+ {{400.121704, 149.468719}, {391.949493, 161.037186}, {391.949493, 181.202423}},
+ {{391.946747, 181.839218}, {391.946747, 155.62442}, {406.115479, 138.855438}},
+ {{360.048828125, 229.2578125}, {360.048828125, 224.4140625}, {362.607421875, 221.3671875}},
+ {{362.607421875, 221.3671875}, {365.166015625, 218.3203125}, {369.228515625, 218.3203125}},
{{8, 8}, {10, 10}, {8, -10}},
{{8, 8}, {12, 12}, {14, 4}},
{{8, 8}, {9, 9}, {10, 8}}
@@ -71,12 +75,13 @@ static void oneOffTest() {
for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
const Quadratic& quad1 = testSet[outer];
const Quadratic& quad2 = testSet[inner];
+ double tt1, tt2;
+ #if 0 // enable to test bezier clip style intersection
Intersections intersections;
intersect(quad1, quad2, intersections);
if (!intersections.intersected()) {
SkDebugf("%s no intersection!\n", __FUNCTION__);
}
- double tt1, tt2;
for (int pt = 0; pt < intersections.used(); ++pt) {
tt1 = intersections.fT[0][pt];
double tx1, ty1;
@@ -97,8 +102,10 @@ static void oneOffTest() {
SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
outer, inner, tt1, tx1, tx2, tt2);
}
+ #endif
Intersections intersections2;
intersect2(quad1, quad2, intersections2);
+ #if 0
SkASSERT(intersections.used() == intersections2.used());
for (int pt = 0; pt < intersections2.used(); ++pt) {
tt1 = intersections2.fT[0][pt];
@@ -106,6 +113,28 @@ static void oneOffTest() {
tt2 = intersections2.fT[1][pt];
SkASSERT(approximately_equal(intersections.fT[1][pt], tt2));
}
+ #endif
+ for (int pt = 0; pt < intersections2.used(); ++pt) {
+ tt1 = intersections2.fT[0][pt];
+ double tx1, ty1;
+ xy_at_t(quad1, tt1, tx1, ty1);
+ int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
+ tt2 = intersections2.fT[1][pt2];
+ double tx2, ty2;
+ xy_at_t(quad2, tt2, tx2, ty2);
+ if (!approximately_equal(tx1, tx2)) {
+ SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+ __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+ SkASSERT(0);
+ }
+ if (!approximately_equal(ty1, ty2)) {
+ SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+ __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+ SkASSERT(0);
+ }
+ SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+ outer, inner, tt1, tx1, tx2, tt2);
+ }
}
}
}
@@ -148,7 +177,7 @@ static void coincidentTest() {
}
void QuadraticIntersection_Test() {
- coincidentTest();
oneOffTest();
+ coincidentTest();
standardTestCases();
}
diff --git a/experimental/Intersection/QuarticRoot.cpp b/experimental/Intersection/QuarticRoot.cpp
index 839c3b973b..b2e73cba50 100644
--- a/experimental/Intersection/QuarticRoot.cpp
+++ b/experimental/Intersection/QuarticRoot.cpp
@@ -120,7 +120,7 @@ static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
if (approximately_zero(A)) { // we're just a quadratic
return quadraticRootsX(B, C, D, s);
}
- if (approximately_zero(D)) {
+ if (approximately_zero(D)) { // 0 is one root
int num = quadraticRootsX(A, B, C, s);
for (int i = 0; i < num; ++i) {
if (approximately_zero(s[i])) {
@@ -130,6 +130,16 @@ static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
s[num++] = 0;
return num;
}
+ if (approximately_zero(A + B + C + D)) { // 1 is one root
+ int num = quadraticRootsX(A, A + B, -D, s);
+ for (int i = 0; i < num; ++i) {
+ if (approximately_equal(s[i], 1)) {
+ return num;
+ }
+ }
+ s[num++] = 1;
+ return num;
+ }
double a, b, c;
{
double invA = 1 / A;
@@ -197,7 +207,7 @@ int quarticRoots(const double A, const double B, const double C, const double D,
}
int num;
int i;
- if (approximately_zero(E)) {
+ if (approximately_zero(E)) { // 0 is one root
num = cubicRootsX(A, B, C, D, s);
for (i = 0; i < num; ++i) {
if (approximately_zero(s[i])) {
@@ -207,6 +217,16 @@ int quarticRoots(const double A, const double B, const double C, const double D,
s[num++] = 0;
return num;
}
+ if (approximately_zero(A + B + C + D + E)) { // 1 is one root
+ num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
+ for (i = 0; i < num; ++i) {
+ if (approximately_equal(s[i], 1)) {
+ return num;
+ }
+ }
+ s[num++] = 1;
+ return num;
+ }
double u, v;
/* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
const double invA = 1 / A;
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index 2a65ad1219..2ce83d686c 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -25,17 +25,9 @@ int gDebugMaxWindSum = SK_MaxS32;
int gDebugMaxWindValue = SK_MaxS32;
#endif
-#define HIGH_DEF_ANGLES 1
-
-#if HIGH_DEF_ANGLES
-typedef double AngleValue;
-#else
-typedef SkScalar AngleValue;
-#endif
-
#define DEBUG_UNUSED 0 // set to expose unused functions
-#if 1 // set to 1 for multiple thread -- no debugging
+#if 0 // set to 1 for multiple thread -- no debugging
const bool gRunTestsInOneThread = false;
@@ -45,9 +37,8 @@ const bool gRunTestsInOneThread = false;
#define DEBUG_ANGLE 0
#define DEBUG_CONCIDENT 0
#define DEBUG_CROSS 0
-#define DEBUG_DUMP 0
#define DEBUG_MARK_DONE 0
-#define DEBUG_PATH_CONSTRUCTION 0
+#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_SORT 0
#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 0
@@ -57,12 +48,11 @@ const bool gRunTestsInOneThread = false;
const bool gRunTestsInOneThread = true;
#define DEBUG_ACTIVE_SPANS 1
-#define DEBUG_ADD_INTERSECTING_TS 0
-#define DEBUG_ADD_T_PAIR 0
+#define DEBUG_ADD_INTERSECTING_TS 1
+#define DEBUG_ADD_T_PAIR 1
#define DEBUG_ANGLE 1
#define DEBUG_CONCIDENT 1
#define DEBUG_CROSS 0
-#define DEBUG_DUMP 1
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_SORT 1
@@ -71,10 +61,7 @@ const bool gRunTestsInOneThread = true;
#endif
-#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !DEBUG_DUMP
-#undef DEBUG_DUMP
-#define DEBUG_DUMP 1
-#endif
+#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
#if DEBUG_DUMP
static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
@@ -198,6 +185,11 @@ static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
out->fY = SkDoubleToScalar(y);
}
+static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
+ MAKE_CONST_QUAD(quad, a);
+ xy_at_t(quad, t, out->x, out->y);
+}
+
static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
MAKE_CONST_CUBIC(cubic, a);
double x, y;
@@ -460,15 +452,35 @@ static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) =
CubicLeftMost
};
+#if 0 // currently unused
static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
Intersections& intersections) {
MAKE_CONST_QUAD(aQuad, a);
MAKE_CONST_LINE(bLine, b);
return intersectRay(aQuad, bLine, intersections);
}
+#endif
+
+static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
+ Intersections& intersections) {
+ MAKE_CONST_QUAD(aQuad, a);
+ return intersectRay(aQuad, bLine, intersections);
+}
class Segment;
+struct Span {
+ Segment* fOther;
+ mutable SkPoint fPt; // lazily computed as needed
+ double fT;
+ double fOtherT; // value at fOther[fOtherIndex].fT
+ int fOtherIndex; // can't be used during intersection
+ int fWindSum; // accumulated from contours surrounding this one
+ int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
+ int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
+ bool fDone; // if set, this span to next higher T has been processed
+};
+
// sorting angles
// given angles of {dx dy ddx ddy dddx dddy} sort them
class Angle {
@@ -490,7 +502,7 @@ public:
the shorter tangent. If the tangents are equal, choose the better second
tangent angle
- maybe I set up LineParameters lazily
+ maybe I could set up LineParameters lazily
*/
bool operator<(const Angle& rh) const {
double y = dy();
@@ -503,9 +515,9 @@ public:
if (y == 0 && ry == 0 && x * rx < 0) {
return x < rx;
}
- AngleValue x_ry = x * ry;
- AngleValue rx_y = rx * y;
- AngleValue cmp = x_ry - rx_y;
+ double x_ry = x * ry;
+ double rx_y = rx * y;
+ double cmp = x_ry - rx_y;
if (!approximately_zero(cmp)) {
return cmp < 0;
}
@@ -513,55 +525,23 @@ public:
&& !approximately_zero_squared(cmp)) {
return cmp < 0;
}
- // at this point, the initial tangent line is coincident
- #if !HIGH_DEF_ANGLES // old way
- AngleValue dy = approximately_pin(fDy + fDDy);
- AngleValue rdy = approximately_pin(rh.fDy + rh.fDDy);
- if (dy * rdy < 0) {
- return dy < 0;
- }
- AngleValue dx = approximately_pin(fDx + fDDx);
- AngleValue rdx = approximately_pin(rh.fDx + rh.fDDx);
- if (dy == 0 && rdy == 0 && dx * rdx < 0) {
- return dx < rdx;
- }
- cmp = dx * rdy - rdx * dy;
- if (!approximately_zero(cmp)) {
- return cmp < 0;
- }
- dy = approximately_pin(dy + fDDDy);
- rdy = approximately_pin(rdy + rh.fDDDy);
- if (dy * rdy < 0) {
- return dy < 0;
- }
- dx = approximately_pin(dx + fDDDx);
- rdx = approximately_pin(rdx + rh.fDDDx);
- if (dy == 0 && rdy == 0 && dx * rdx < 0) {
- return dx < rdx;
+ // see if either curve can be lengthened and try the tangent compare again
+ if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
+ && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
+ Angle longer = *this;
+ Angle rhLonger = rh;
+ if (longer.lengthen() | rhLonger.lengthen()) {
+ return longer < rhLonger;
+ }
}
- return dx * rdy < rdx * dy;
- #else // new way
+ // at this point, the initial tangent line is coincident
if (fSide * rh.fSide <= 0) {
// FIXME: running demo will trigger this assertion
// (don't know if commenting out will trigger further assertion or not)
// commenting it out allows demo to run in release, though
- SkASSERT(fSide != rh.fSide);
+ // SkASSERT(fSide != rh.fSide);
return fSide < rh.fSide;
}
- #if 0 // the following code is a bust. In particular, tangent length doesn't provide a sort
- if (y != ry) {
- return (fabs(y) < fabs(ry)) ^ (fSide > 0);
- }
- if (x != rx) {
- return (fabs(x) < fabs(rx)) ^ (fSide > 0);
- }
- // sort by second tangent angle
- cmp = fD2x * rh.fD2y - rh.fD2x * fD2y;
- if (!approximately_zero(cmp)) {
- return cmp < 0;
- }
- SkASSERT(0); // add code for cubic case
- #else
SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
// FIXME: until I can think of something better, project a ray from the
@@ -570,7 +550,7 @@ public:
// FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
double len = fTangent1.normalSquared();
double rlen = rh.fTangent1.normalSquared();
- SkPoint ray[2];
+ _Line ray;
Intersections i, ri;
int roots, rroots;
bool flip = false;
@@ -578,30 +558,23 @@ public:
const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
double midX = (q[0].x + q[2].x) / 2;
double midY = (q[0].y + q[2].y) / 2;
- // FIXME: Ugh! this feels like a huge hack, not sure what else to do
- while (approximately_equal(q[1].x, midX) && approximately_equal(q[1].y, midY)) {
- SkASSERT(midX - q[1].x || midY - q[1].y);
- midX += midX - q[1].x;
- midY += midY - q[1].y;
- }
- ray[0].fX = SkDoubleToScalar(q[1].x);
- ray[0].fY = SkDoubleToScalar(q[1].y);
- ray[1].fX = SkDoubleToScalar(midX);
- ray[1].fY = SkDoubleToScalar(midY);
+ ray[0] = q[1];
+ ray[1].x = midX;
+ ray[1].y = midY;
SkASSERT(ray[0] != ray[1]);
roots = QuadRayIntersect(fPts, ray, i);
rroots = QuadRayIntersect(rh.fPts, ray, ri);
} while ((roots == 0 || rroots == 0) && (flip ^= true));
SkASSERT(roots > 0);
SkASSERT(rroots > 0);
- SkPoint loc;
- SkScalar best = SK_ScalarInfinity;
- SkScalar dx, dy, dist;
+ _Point loc;
+ double best = SK_ScalarInfinity;
+ double dx, dy, dist;
int index;
for (index = 0; index < roots; ++index) {
QuadXYAtT(fPts, i.fT[0][index], &loc);
- dx = loc.fX - ray[0].fX;
- dy = loc.fY - ray[0].fY;
+ dx = loc.x - ray[0].x;
+ dy = loc.y - ray[0].y;
dist = dx * dx + dy * dy;
if (best > dist) {
best = dist;
@@ -609,32 +582,22 @@ public:
}
for (index = 0; index < rroots; ++index) {
QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
- dx = loc.fX - ray[0].fX;
- dy = loc.fY - ray[0].fY;
+ dx = loc.x - ray[0].x;
+ dy = loc.y - ray[0].y;
dist = dx * dx + dy * dy;
if (best > dist) {
return fSide < 0;
}
}
return fSide > 0;
- #endif
- #endif
}
double dx() const {
-#if HIGH_DEF_ANGLES
return fTangent1.dx();
-#else
- return fDx;
-#endif
}
double dy() const {
-#if HIGH_DEF_ANGLES
return fTangent1.dy();
-#else
- return fDy;
-#endif
}
int end() const {
@@ -642,122 +605,57 @@ public:
}
bool isHorizontal() const {
-#if HIGH_DEF_ANGLES
return dy() == 0 && fVerb == SkPath::kLine_Verb;
-#else
- return fDy == 0 && fDDy == 0 && fDDDy == 0;
-#endif
}
- // high precision version
-#if HIGH_DEF_ANGLES
+ bool lengthen() {
+ int newEnd = fEnd;
+ if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
+ fEnd = newEnd;
+ setSpans();
+ return true;
+ }
+ return false;
+ }
+
void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
- int start, int end, double startT, double endT) {
+ int start, int end, const SkTDArray<Span>& spans) {
fSegment = segment;
fStart = start;
fEnd = end;
fPts = orig;
fVerb = verb;
- switch (verb) {
+ fSpans = &spans;
+ setSpans();
+ }
+
+ void setSpans() {
+ double startT = (*fSpans)[fStart].fT;
+ double endT = (*fSpans)[fEnd].fT;
+ switch (fVerb) {
case SkPath::kLine_Verb:
_Line l;
- LineSubDivideHD(orig, startT, endT, l);
+ LineSubDivideHD(fPts, startT, endT, l);
// OPTIMIZATION: for pure line compares, we never need fTangent1.c
fTangent1.lineEndPoints(l);
fSide = 0;
break;
case SkPath::kQuad_Verb:
- QuadSubDivideHD(orig, startT, endT, fQ);
+ QuadSubDivideHD(fPts, startT, endT, fQ);
fTangent1.quadEndPoints(fQ, 0, 1);
fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
break;
case SkPath::kCubic_Verb:
Cubic c;
- CubicSubDivideHD(orig, startT, endT, c);
+ CubicSubDivideHD(fPts, startT, endT, c);
fTangent1.cubicEndPoints(c, 0, 1);
fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
break;
default:
SkASSERT(0);
}
- // OPTIMIZATION: don't need these, access fTangent1 directly
}
-#else
- // since all angles share a point, this needs to know which point
- // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
- // practically, this should only be called by addAngle
- void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
- int start, int end) {
- SkASSERT(start != end);
- fSegment = segment;
- fStart = start;
- fEnd = end;
- fDx = approximately_pin(pts[1].fX - pts[0].fX); // b - a
- fDy = approximately_pin(pts[1].fY - pts[0].fY);
- if (verb == SkPath::kLine_Verb) {
- fDDx = fDDy = fDDDx = fDDDy = 0;
- return;
- }
- fDDx = approximately_pin(pts[2].fX - pts[1].fX - fDx); // a - 2b + c
- fDDy = approximately_pin(pts[2].fY - pts[1].fY - fDy);
- if (verb == SkPath::kQuad_Verb) {
- fDDDx = fDDDy = 0;
- return;
- }
- fDDDx = approximately_pin(pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX);
- fDDDy = approximately_pin(pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY);
- }
-
- // noncoincident quads/cubics may have the same initial angle
- // as lines, so must sort by derivatives as well
- // if flatness turns out to be a reasonable way to sort, use the below:
- void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
- int start, int end) {
- fSegment = segment;
- fStart = start;
- fEnd = end;
- fDx = pts[1].fX - pts[0].fX; // b - a
- fDy = pts[1].fY - pts[0].fY;
- if (verb == SkPath::kLine_Verb) {
- fDDx = fDDy = fDDDx = fDDDy = 0;
- return;
- }
- if (verb == SkPath::kQuad_Verb) {
- int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
- int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
- int larger = std::max(abs(uplsX), abs(uplsY));
- int shift = 0;
- double flatT;
- SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
- LineParameters implicitLine;
- MAKE_CONST_LINE(tangent, pts);
- implicitLine.lineEndPoints(tangent);
- implicitLine.normalize();
- while (larger > UlpsEpsilon * 1024) {
- larger >>= 2;
- ++shift;
- flatT = 0.5 / (1 << shift);
- QuadXYAtT(pts, flatT, &ddPt);
- _Point _pt = {ddPt.fX, ddPt.fY};
- double distance = implicitLine.pointDistance(_pt);
- if (approximately_zero(distance)) {
- SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
- break;
- }
- }
- flatT = 0.5 / (1 << shift);
- QuadXYAtT(pts, flatT, &ddPt);
- fDDx = ddPt.fX - pts[0].fX;
- fDDy = ddPt.fY - pts[0].fY;
- SkASSERT(fDDx != 0 || fDDy != 0);
- fDDDx = fDDDy = 0;
- return;
- }
- SkASSERT(0); // FIXME: add cubic case
- }
-#endif
-
Segment* segment() const {
return const_cast<Segment*>(fSegment);
}
@@ -771,56 +669,30 @@ public:
}
#if DEBUG_ANGLE
+ const SkPoint* pts() const {
+ return fPts;
+ }
+
+ const SkTDArray<Span>* spans() const {
+ return fSpans;
+ }
+
+ SkPath::Verb verb() const {
+ return fVerb;
+ }
+
void debugShow(const SkPoint& a) const {
-#if HIGH_DEF_ANGLES
- SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n",
- dx(), dy(), fSide);
-#else
- SkDebugf(" d=(%1.9g,%1.9g) dd=(%1.9g,%1.9g) ddd=(%1.9g,%1.9g)\n",
- fDx, fDy, fDDx, fDDy, fDDDx, fDDDy);
- AngleValue ax = (AngleValue) a.fX;
- AngleValue ay = (AngleValue) a.fY;
- AngleValue bx, by, cx, cy, dx, dy;
- bx = ax + fDx; // add b - a
- by = ay + fDy;
- cx = ax + 2 * fDx + fDDx; // add a + 2(b - a) to a - 2b + c
- cy = ay + 2 * fDy + fDDy;
- if (fDDDx == 0 && fDDDy == 0) {
- if (fDDx == 0 && fDDy == 0) {
- SkDebugf(
-" {SkPath::kLine_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g} }},\n",
- ax, ay, bx, by);
- } else {
- SkDebugf(
-" {SkPath::kQuad_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
- ax, ay, bx, by, cx, cy);
- }
- } else {
- dx = fDDDx - ax - 3 * (cx - bx);
- dy = fDDDy - ay - 3 * (cy - by);
- SkDebugf(
-" {SkPath::kCubic_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
- ax, ay, bx, by, cx, cy, dx, dy);
- }
-#endif
+ SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
}
#endif
private:
-#if HIGH_DEF_ANGLES
const SkPoint* fPts;
Quadratic fQ;
SkPath::Verb fVerb;
double fSide;
LineParameters fTangent1;
-#else
- AngleValue fDx;
- AngleValue fDy;
- AngleValue fDDx;
- AngleValue fDDy;
- AngleValue fDDDx;
- AngleValue fDDDy;
-#endif
+ const SkTDArray<Span>* fSpans;
const Segment* fSegment;
int fStart;
int fEnd;
@@ -900,18 +772,6 @@ static bool useInnerWinding(int outerWinding, int innerWinding) {
return result;
}
-struct Span {
- Segment* fOther;
- mutable SkPoint fPt; // lazily computed as needed
- double fT;
- double fOtherT; // value at fOther[fOtherIndex].fT
- int fOtherIndex; // can't be used during intersection
- int fWindSum; // accumulated from contours surrounding this one
- int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
- int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
- bool fDone; // if set, this span to next higher T has been processed
-};
-
static const bool opLookup[][2][2] = {
// ==0 !=0
// b a b a
@@ -1010,13 +870,17 @@ public:
void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
SkASSERT(start != end);
Angle* angle = angles.append();
-#if HIGH_DEF_ANGLES==0 // old way
- SkPoint edge[4];
- (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
- angle->set(edge, fVerb, this, start, end);
-#else // new way : compute temp edge in higher precision
- angle->set(fPts, fVerb, this, start, end, fTs[start].fT, fTs[end].fT);
+#if DEBUG_ANGLE
+ if (angles.count() > 1) {
+ SkPoint angle0Pt, newPt;
+ (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
+ (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
+ (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
+ SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
+ SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
+ }
#endif
+ angle->set(fPts, fVerb, this, start, end, fTs);
}
void addCancelOutsides(double tStart, double oStart, Segment& other,
@@ -1140,15 +1004,15 @@ public:
(*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
if (active) {
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
+ SkDebugf("path.%sTo(%1.9g,%1.9g",
kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
if (fVerb > 1) {
- SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
+ SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
}
if (fVerb > 2) {
- SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
+ SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
}
- SkDebugf("\n");
+ SkDebugf(");\n");
#endif
switch (fVerb) {
case SkPath::kLine_Verb:
@@ -1177,7 +1041,7 @@ public:
const SkPoint& pt = xyAtT(tIndex);
if (active) {
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
+ SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
#endif
path.moveTo(pt.fX, pt.fY);
}
@@ -1440,7 +1304,8 @@ public:
if (!approximately_negative(span.fT - t)) {
break;
}
- if (approximately_negative(span.fT - t) && span.fOther == &other && span.fOtherT == otherT) {
+ if (approximately_negative(span.fT - t) && span.fOther == &other
+ && approximately_equal(span.fOtherT, otherT)) {
#if DEBUG_ADD_T_PAIR
SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
__FUNCTION__, fID, t, other.fID, otherT);
@@ -1518,6 +1383,61 @@ public:
return false;
}
+ // FIXME may not need this at all
+ // FIXME once I figure out the logic, merge this and too close to call
+ // NOTE not sure if tiny triangles can ever form at the edge, so until we
+ // see one, only worry about triangles that happen away from 0 and 1
+ void collapseTriangles(bool isXor) {
+ if (fTs.count() < 3) { // require t=0, x, 1 at minimum
+ return;
+ }
+ int lastIndex = 1;
+ double lastT;
+ while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
+ ++lastIndex;
+ }
+ if (approximately_greater_than_one(lastT)) {
+ return;
+ }
+ int matchIndex = lastIndex;
+ do {
+ Span& match = fTs[++matchIndex];
+ double matchT = match.fT;
+ if (approximately_greater_than_one(matchT)) {
+ return;
+ }
+ if (matchT == lastT) {
+ goto nextSpan;
+ }
+ if (approximately_negative(matchT - lastT)) {
+ Span& last = fTs[lastIndex];
+ Segment* lOther = last.fOther;
+ double lT = last.fOtherT;
+ if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
+ goto nextSpan;
+ }
+ Segment* mOther = match.fOther;
+ double mT = match.fOtherT;
+ if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
+ goto nextSpan;
+ }
+ // add new point to connect adjacent spurs
+ int count = lOther->fTs.count();
+ for (int index = 0; index < count; ++index) {
+ Span& span = lOther->fTs[index];
+ if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
+ goto nextSpan;
+ }
+ }
+ mOther->addTPair(mT, *lOther, lT, false);
+ // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
+ }
+ nextSpan:
+ lastIndex = matchIndex;
+ lastT = matchT;
+ } while (true);
+ }
+
int computeSum(int startIndex, int endIndex) {
SkTDArray<Angle> angles;
addTwoAngles(startIndex, endIndex, angles);
@@ -2638,6 +2558,23 @@ public:
}
return -1;
}
+
+ // FIXME
+ // this returns at any difference in T, vs. a preset minimum. It may be
+ // that all callers to nextSpan should use this instead.
+ int nextExactSpan(int from, int step) const {
+ const Span& fromSpan = fTs[from];
+ int count = fTs.count();
+ int to = from;
+ while (step > 0 ? ++to < count : --to >= 0) {
+ const Span& span = fTs[to];
+ if (span.fT == fromSpan.fT) {
+ continue;
+ }
+ return to;
+ }
+ return -1;
+ }
bool operand() const {
return fOperand;
@@ -2865,6 +2802,40 @@ public:
SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
}
}
+
+ // This isn't useful yet -- but leaving it in for now in case i think of something
+ // to use it for
+ void validateActiveSpans() const {
+ if (done()) {
+ return;
+ }
+ int tCount = fTs.count();
+ for (int index = 0; index < tCount; ++index) {
+ if (fTs[index].fDone) {
+ continue;
+ }
+ // count number of connections which are not done
+ int first = index;
+ double baseT = fTs[index].fT;
+ while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
+ --first;
+ }
+ int last = index;
+ while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
+ ++last;
+ }
+ int connections = 0;
+ connections += first > 0 && !fTs[first - 1].fDone;
+ for (int test = first; test <= last; ++test) {
+ connections += !fTs[test].fDone;
+ const Segment* other = fTs[test].fOther;
+ int oIndex = fTs[test].fOtherIndex;
+ connections += !other->fTs[oIndex].fDone;
+ connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
+ }
+ // SkASSERT(!(connections & 1));
+ }
+ }
#endif
#if DEBUG_MARK_DONE
@@ -2917,7 +2888,7 @@ public:
segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
lastSum, windSum, useInnerWinding(lastSum, windSum)
? windSum : lastSum, mSpan.fDone);
-#if DEBUG_ANGLE
+#if false && DEBUG_ANGLE
angle.debugShow(segment.xyAtT(&sSpan));
#endif
++index;
@@ -3045,6 +3016,13 @@ public:
return fBounds;
}
+ void collapseTriangles() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ fSegments[sIndex].collapseTriangles(fXor);
+ }
+ }
+
void complete() {
setBounds();
fContainsIntercepts = false;
@@ -3290,6 +3268,12 @@ public:
fSegments[index].debugShowActiveSpans();
}
}
+
+ void validateActiveSpans() {
+ for (int index = 0; index < fSegments.count(); ++index) {
+ fSegments[index].validateActiveSpans();
+ }
+ }
#endif
protected:
@@ -3854,13 +3838,20 @@ static bool addIntersectTs(Contour* test, Contour* next) {
}
}
+ int pt2 = 0;
+ int pt2inc = 1;
+ if (ts.fFlip) {
+ pt2 = pts - 1;
+ pt2inc = -1;
+ }
for (int pt = 0; pt < pts; ++pt) {
SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
int testTAt = wt.addT(ts.fT[swap][pt], wn);
int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
- wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
- wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
+ wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
+ wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
+ pt2 += pt2inc;
}
} while (wn.advance());
} while (wt.advance());
@@ -3879,6 +3870,13 @@ static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
Contour* contour = contourList[cIndex];
contour->findTooCloseToCall();
}
+#if 0
+ // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ Contour* contour = contourList[cIndex];
+ contour->collapseTriangles();
+ }
+#endif
}
// project a ray from the top of the contour up and see if it hits anything
@@ -4183,9 +4181,13 @@ static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
#if DEBUG_ACTIVE_SPANS
static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
- for (int index = 0; index < contourList.count(); ++ index) {
+ int index;
+ for (index = 0; index < contourList.count(); ++ index) {
contourList[index]->debugShowActiveSpans();
}
+ for (index = 0; index < contourList.count(); ++ index) {
+ contourList[index]->validateActiveSpans();
+ }
}
#endif
@@ -4286,7 +4288,7 @@ static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
} while (*firstPt != lastPt && (active || !current->done()));
if (firstPt && active) {
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s close\n", __FUNCTION__);
+ SkDebugf("path.close();\n");
#endif
simple.close();
}
@@ -4349,6 +4351,9 @@ static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
#endif
simple.close();
}
+ #if DEBUG_ACTIVE_SPANS
+ debugShowActiveSpans(contourList);
+ #endif
}
}
diff --git a/experimental/Intersection/SimplifyAngle_Test.cpp b/experimental/Intersection/SimplifyAngle_Test.cpp
index 4bf22fd44f..9b25851eee 100644
--- a/experimental/Intersection/SimplifyAngle_Test.cpp
+++ b/experimental/Intersection/SimplifyAngle_Test.cpp
@@ -93,27 +93,12 @@ static void testSegments(bool testFlat) {
int index = 0;
do {
int next = index + 1;
- #if HIGH_DEF_ANGLES==0
- if (testFlat) {
- lesser.setFlat(segPtr[index].pts, segPtr[index].verb, 0, index, next);
- } else {
- lesser.set(segPtr[index].pts, segPtr[index].verb, 0, index, next);
- }
- #else
- lesser.set(segPtr[index].pts, segPtr[index].verb, 0, index, next, 0, 1);
- #endif
+ SkTDArray<SimplifyAngleTest::Span> dummy;
+ lesser.set(segPtr[index].pts, segPtr[index].verb, NULL, index, next, dummy);
if (segPtr[next].verb == SkPath::kMove_Verb) {
break;
}
- #if HIGH_DEF_ANGLES==0
- if (testFlat) {
- greater.setFlat(segPtr[next].pts, segPtr[next].verb, 0, index, next);
- } else {
- greater.set(segPtr[next].pts, segPtr[next].verb, 0, index, next);
- }
- #else
- greater.set(segPtr[next].pts, segPtr[next].verb, 0, index, next, 0, 1);
- #endif
+ greater.set(segPtr[next].pts, segPtr[next].verb, NULL, index, next, dummy);
bool result = lesser < greater;
SkASSERT(result);
index = next;
@@ -129,15 +114,8 @@ static void testLines(bool testFlat) {
size_t x;
for (x = 0; x < lineCount; ++x) {
SimplifyAngleTest::Angle* angle = angles.append();
- #if HIGH_DEF_ANGLES==0
- if (testFlat) {
- angle->setFlat(lines[x], SkPath::kLine_Verb, 0, x, x + 1);
- } else {
- angle->set(lines[x], SkPath::kLine_Verb, 0, x, x + 1);
- }
- #else
- angle->set(lines[x], SkPath::kLine_Verb, 0, x, x + 1, 0, 1);
- #endif
+ SkTDArray<SimplifyAngleTest::Span> dummy;
+ angle->set(lines[x], SkPath::kLine_Verb, 0, x, x + 1, dummy);
double arcTan = atan2(lines[x][0].fX - lines[x][1].fX,
lines[x][0].fY - lines[x][1].fY);
arcTans.push(arcTan);
@@ -184,15 +162,8 @@ static void testQuads(bool testFlat) {
size_t x;
for (x = 0; x < quadCount; ++x) {
SimplifyAngleTest::Angle* angle = angles.append();
- #if HIGH_DEF_ANGLES==0
- if (testFlat) {
- angle->setFlat(quads[x], SkPath::kQuad_Verb, 0, x, x + 1);
- } else {
- angle->set(quads[x], SkPath::kQuad_Verb, 0, x, x + 1);
- }
- #else
- angle->set(quads[x], SkPath::kQuad_Verb, 0, x, x + 1, 0, 1);
- #endif
+ SkTDArray<SimplifyAngleTest::Span> dummy;
+ angle->set(quads[x], SkPath::kQuad_Verb, 0, x, x + 1, dummy);
}
for (x = 0; x < quadCount; ++x) {
angleList.push(&angles[x]);
@@ -214,15 +185,8 @@ static void testCubics(bool testFlat) {
SkTDArray<SimplifyAngleTest::Angle* > angleList;
for (size_t x = 0; x < cubicCount; ++x) {
SimplifyAngleTest::Angle* angle = angles.append();
- #if HIGH_DEF_ANGLES==0
- if (testFlat) {
- angle->setFlat(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1);
- } else {
- angle->set(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1);
- }
- #else
- angle->set(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1, 0, 1);
- #endif
+ SkTDArray<SimplifyAngleTest::Span> dummy;
+ angle->set(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1, dummy);
angleList.push(angle);
}
QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
@@ -235,7 +199,68 @@ static void testCubics(bool testFlat) {
}
}
+struct segmentWithT {
+ SkPath::Verb verb;
+ SkPoint pts[4];
+ double ts[2];
+};
+
+
+static const segmentWithT oneOffTest1[] = {
+ {SkPath::kQuad_Verb, {{391.653534f, 183.286819f}, {391.653534f, 157.724487f}, {405.469604f, 141.372879f}},
+ {0.62346946335026932, 0.62344389027237135}},
+ {SkPath::kQuad_Verb, {{399.365234f, 171.695801f}, {399.365234f, 140.337967f}, {375.976227f, 140.337967f}},
+ {0.31638302676995866, 0.31637992418411398}},
+ {SkPath::kMove_Verb }
+};
+
+static const segmentWithT oneOffTest2[] = {
+ {SkPath::kQuad_Verb, {{399.070374f, 151.722f}, {391.101532f, 163.002533f}, {391.101532f, 182.665863f}},
+ {0.13793711854916513, 0.13790171160614006}},
+ {SkPath::kQuad_Verb, {{391.653534f, 183.286819f}, {391.653534f, 157.724487f}, {405.469604f, 141.372879f}},
+ {0.62344389027237135, 0.62346946335026932}},
+ {SkPath::kMove_Verb }
+};
+
+static const segmentWithT oneOffTest3[] = {
+ {SkPath::kQuad_Verb, {{399.365234f, 171.695801f}, {399.365234f, 140.337967f}, {375.976227f, 140.337967f}},
+ {0.31637992418411398, 0.31638302676995866, }},
+ {SkPath::kQuad_Verb, {{399.070374f, 151.722f}, {391.101532f, 163.002533f}, {391.101532f, 182.665863f}},
+ {0.13790171160614006, 0.13793711854916513}},
+ {SkPath::kMove_Verb }
+};
+
+static const segmentWithT* oneOffTests[] = {
+ oneOffTest1,
+ oneOffTest2,
+ oneOffTest3,
+};
+
+static const size_t oneOffTestCount = sizeof(oneOffTests) / sizeof(oneOffTests[0]);
+
+static void oneOffTest(bool testFlat) {
+ for (size_t testIndex = 0; testIndex < oneOffTestCount; ++testIndex) {
+ const segmentWithT* segPtr = oneOffTests[testIndex];
+ SimplifyAngleTest::Angle lesser, greater;
+ int index = 0;
+ do {
+ int next = index + 1;
+ SkTDArray<SimplifyAngleTest::Span> dummy; // FIXME
+ lesser.set(segPtr[index].pts, segPtr[index].verb, 0, index, next, dummy); // FIXME: segPtr[index].ts[0], segPtr[index].ts[1]);
+ if (segPtr[next].verb == SkPath::kMove_Verb) {
+ break;
+ }
+ greater.set(segPtr[next].pts, segPtr[next].verb, 0, index, next, dummy); // FIXME: segPtr[next].ts[0], segPtr[next].ts[1]);
+ bool result = lesser < greater;
+ SkASSERT(result);
+ index = next;
+ } while (true);
+ }
+ SkDebugf("%s finished\n", __FUNCTION__);
+}
+
static void (*tests[])(bool) = {
+ oneOffTest,
testSegments,
testLines,
testQuads,
diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp
index d317abab85..d2f8b2c950 100644
--- a/experimental/Intersection/SimplifyNew_Test.cpp
+++ b/experimental/Intersection/SimplifyNew_Test.cpp
@@ -2828,7 +2828,7 @@ static void testQuadratic38() {
testSimplifyx(path);
}
-static void (*firstTest)() = testQuadratic38;
+static void (*firstTest)() = testLine73x;
static struct {
void (*fun)();
diff --git a/experimental/Intersection/bc.htm b/experimental/Intersection/bc.htm
index 95a8d754ab..e0784bdbbf 100644
--- a/experimental/Intersection/bc.htm
+++ b/experimental/Intersection/bc.htm
@@ -109,11 +109,18 @@ $3 = {{
(gdb)
</div>
+<div id="quad37">
+ {{x = 360.048828125, y = 229.2578125}, {x = 360.048828125, y = 224.4140625}, {x = 362.607421875, y = 221.3671875}}
+ {{x = 362.607421875, y = 221.3671875}, {x = 365.166015625, y = 218.3203125}, {x = 369.228515625, y = 218.3203125}}
+</div>
+
+
</div>
<script type="text/javascript">
var testDivs = [
+ quad37,
quad36,
quad21g,
quad21a,
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
index 2063152c7a..fabd0ef9cc 100644
--- a/experimental/Intersection/op.htm
+++ b/experimental/Intersection/op.htm
@@ -1556,11 +1556,555 @@ path.quadTo(40.8951759, 17.8140678, 40.5937271, 17.4687576);
path.close();
</div>
+<div id="testQuadratic40x">
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(2, 0);
+ path.quadTo(3, 0, 2, 2);
+ path.lineTo(3, 2);
+ path.close();
+ path.moveTo(3, 1);
+ path.lineTo(0, 2);
+ path.quadTo(0, 2, 1, 2);
+ path.close();
+</div>
+
+<div id="testQuadratic40xa">
+path.moveTo(31, 0);
+path.quadTo(41.3333359, 0, 37.8888893, 13.7777777);
+path.lineTo(31, 0);
+path.close();
+path.moveTo(37.8888893, 13.7777777);
+path.quadTo(37.2993202, 16.1360455, 36.3061028, 18.8979664);
+path.lineTo(0, 31);
+path.lineTo(15.5, 31);
+path.lineTo(35.5182915, 20.9908543);
+path.quadTo(33.7454262, 25.5091457, 31, 31);
+path.lineTo(46.5, 31);
+path.lineTo(40.2999992, 18.6000004);
+path.lineTo(46.5, 15.5);
+path.lineTo(39.8571434, 17.7142868);
+path.lineTo(37.8888893, 13.7777777);
+path.close();
+path.moveTo(36.3061028, 18.8979664);
+path.quadTo(35.9396667, 19.9169388, 35.5182915, 20.9908543);
+path.lineTo(40.2999992, 18.6000004);
+path.lineTo(39.8571434, 17.7142868);
+path.lineTo(36.3061028, 18.8979664);
+</div>
+
+<div id="testQuadratic40xb">
+path.moveTo(31, 0);
+path.quadTo(46.5, 0, 31, 31);
+path.lineTo(46.5, 31);
+path.lineTo(31, 0);
+path.close();
+path.moveTo(46.5, 15.5);
+path.lineTo(0, 31);
+path.quadTo(0, 31, 15.5, 31);
+path.lineTo(46.5, 15.5);
+path.close();
+</div>
+
+<div id="testQuadratic41o">
+path.moveTo(419.33905, 236.377808);
+path.quadTo(398.847778, 242.58728, 384.255524, 242.58728);
+path.quadTo(359.417633, 242.58728, 343.738708, 226.080429);
+path.quadTo(328.059784, 209.573578, 328.059784, 183.286819);
+path.quadTo(328.059784, 157.724487, 341.875854, 141.372879);
+path.quadTo(355.691956, 125.021263, 377.218109, 125.021263);
+path.quadTo(397.605896, 125.021263, 408.731201, 139.51004);
+path.quadTo(419.856506, 153.99881, 419.856506, 180.699539);
+path.lineTo(419.752991, 187.012497);
+path.lineTo(348.861511, 187.012497);
+path.quadTo(353.311646, 227.063599, 388.084686, 227.063599);
+path.quadTo(400.814117, 227.063599, 419.33905, 220.233185);
+path.lineTo(419.33905, 236.377808);
+path.close();
+path.moveTo(349.792938, 171.695801);
+path.lineTo(399.365234, 171.695801);
+path.quadTo(399.365234, 140.337967, 375.976227, 140.337967);
+path.quadTo(352.483704, 140.337967, 349.792938, 171.695801);
+path.close();
+path.moveTo(378.682587, 277.360321);
+path.lineTo(381.062897, 259.66333);
+path.quadTo(398.759888, 268.046112, 415.939423, 268.046112);
+path.quadTo(450.402008, 268.046112, 450.402008, 231.513718);
+path.lineTo(450.402008, 213.816727);
+path.quadTo(439.12146, 237.41272, 413.352142, 237.41272);
+path.quadTo(393.171356, 237.41272, 381.269867, 222.716965);
+path.quadTo(369.368378, 208.02121, 369.368378, 183.079834);
+path.quadTo(369.368378, 157.414017, 382.92572, 141.269379);
+path.quadTo(396.483093, 125.124756, 418.009247, 125.124756);
+path.quadTo(436.844666, 125.124756, 450.402008, 140.441467);
+path.lineTo(450.402008, 127.608543);
+path.lineTo(470.89325, 127.608543);
+path.lineTo(470.89325, 209.366608);
+path.quadTo(470.89325, 235.756866, 468.150757, 248.43454);
+path.quadTo(465.408234, 261.112213, 457.853363, 269.184509);
+path.quadTo(444.502991, 283.362823, 416.353394, 283.362823);
+path.quadTo(396.690063, 283.362823, 378.682587, 277.360321);
+path.close();
+path.moveTo(450.402008, 201.087311);
+path.lineTo(450.402008, 154.412781);
+path.quadTo(436.948151, 140.441467, 421.113983, 140.441467);
+path.quadTo(407.039185, 140.441467, 399.070374, 151.722);
+path.quadTo(391.101532, 163.002533, 391.101532, 182.665863);
+path.quadTo(391.101532, 219.612228, 417.07782, 219.612228);
+path.quadTo(434.774841, 219.612228, 450.402008, 201.087311);
+path.close();
+path.moveTo(482.9328, 236.377808);
+path.quadTo(462.441528, 242.58728, 447.849274, 242.58728);
+path.quadTo(423.011383, 242.58728, 407.332458, 226.080429);
+path.quadTo(391.653534, 209.573578, 391.653534, 183.286819);
+path.quadTo(391.653534, 157.724487, 405.469604, 141.372879);
+path.quadTo(419.285706, 125.021263, 440.811859, 125.021263);
+path.quadTo(461.199646, 125.021263, 472.324951, 139.51004);
+path.quadTo(483.450256, 153.99881, 483.450256, 180.699539);
+path.lineTo(483.346741, 187.012497);
+path.lineTo(412.455261, 187.012497);
+path.quadTo(416.905396, 227.063599, 451.678436, 227.063599);
+path.quadTo(464.407867, 227.063599, 482.9328, 220.233185);
+path.lineTo(482.9328, 236.377808);
+path.close();
+path.moveTo(413.386688, 171.695801);
+path.lineTo(462.958984, 171.695801);
+path.quadTo(462.958984, 140.337967, 439.569977, 140.337967);
+path.quadTo(416.077454, 140.337967, 413.386688, 171.695801);
+path.close();
+</div>
+
+<div id="testQuadratic41s">
+path.moveTo(341.875854, 141.372879);
+path.quadTo(355.691956,125.021263, 377.218109,125.021263);
+path.quadTo(388.787811,125.021263, 397.374664,129.687164);
+path.quadTo(406.565979,125.124756, 418.009247,125.124756);
+path.quadTo(423.583374,125.124756, 428.695251,126.466187);
+path.quadTo(434.412903,125.021263, 440.811859,125.021263);
+path.quadTo(449.427277,125.021263, 456.388672,127.608543);
+path.lineTo(470.89325,127.608543);
+path.lineTo(470.89325,137.749908);
+path.quadTo(471.627319,138.601486, 472.324951,139.51004);
+path.quadTo(483.450256,153.99881, 483.450256,180.699539);
+path.lineTo(483.346741,187.012497);
+path.lineTo(470.89325,187.012497);
+path.lineTo(470.89325,209.366608);
+path.quadTo(470.89325,217.414856, 470.638184,224.187729);
+path.quadTo(476.428223,222.631516, 482.9328,220.233185);
+path.lineTo(482.9328,236.377808);
+path.quadTo(475.87207,238.517426, 469.511749,239.919785);
+path.quadTo(468.946777,244.754791, 468.150757,248.43454);
+path.quadTo(465.408234,261.112213, 457.853363,269.184509);
+path.quadTo(444.502991,283.362823, 416.353394,283.362823);
+path.quadTo(396.690063,283.362823, 378.682587,277.360321);
+path.lineTo(381.062897,259.66333);
+path.quadTo(398.759888,268.046112, 415.939423,268.046112);
+path.quadTo(444.719147,268.046112, 449.464905,242.568665);
+path.quadTo(448.648254,242.58728, 447.849274,242.58728);
+path.quadTo(433.084625,242.58728, 421.556366,236.754425);
+path.quadTo(418.89566,237.203537, 416.046783,237.346252);
+path.quadTo(397.661652,242.58728, 384.255524,242.58728);
+path.quadTo(359.417633,242.58728, 343.738708,226.080429);
+path.quadTo(328.059784,209.573578, 328.059784,183.286819);
+path.quadTo(328.059784,157.724487, 341.875854,141.372879);
+path.close();
+path.moveTo(442.014923, 226.179474);
+path.quadTo(445.951935,226.953491, 450.402008,227.049881);
+path.lineTo(450.402008,213.816727);
+path.quadTo(446.904755,221.132065, 442.014923,226.179474);
+path.close();
+path.moveTo(395.347717, 206.501785);
+path.quadTo(392.200165,197.593536, 391.734406,187.012497);
+path.lineTo(391.197113,187.012497);
+path.quadTo(391.738647,198.938644, 395.347717,206.501785);
+path.close();
+path.moveTo(391.808533, 171.695801);
+path.lineTo(392.428436,171.695801);
+path.quadTo(393.693451,162.656265, 397.02359,154.9935);
+path.quadTo(397.023804,154.992996, 397.024048,154.992493);
+path.quadTo(393.175995,143.845093, 383.003235,141.177292);
+path.quadTo(382.964447,141.223267, 382.92572,141.269379);
+</div>
+
+<div id="testQuadratic42o">
+path.moveTo(421.962158, 236.285355);
+path.quadTo(400.947845, 242.65332, 385.983124, 242.65332);
+path.quadTo(360.511261, 242.65332, 344.432129, 225.725143);
+path.quadTo(328.352997, 208.796951, 328.352997, 181.839218);
+path.quadTo(328.352997, 155.62442, 342.521729, 138.855438);
+path.quadTo(356.69046, 122.086449, 378.766083, 122.086449);
+path.quadTo(399.674255, 122.086449, 411.083527, 136.945038);
+path.quadTo(422.492798, 151.803635, 422.492798, 179.185898);
+path.lineTo(422.386688, 185.660004);
+path.lineTo(349.685699, 185.660004);
+path.quadTo(354.24942, 226.733398, 389.910034, 226.733398);
+path.quadTo(402.964386, 226.733398, 421.962158, 219.728638);
+path.lineTo(421.962158, 236.285355);
+path.close();
+path.moveTo(350.6409, 169.952347);
+path.lineTo(401.478516, 169.952347);
+path.quadTo(401.478516, 137.794098, 377.492493, 137.794098);
+path.quadTo(353.40036, 137.794098, 350.6409, 169.952347);
+path.close();
+path.moveTo(379.213562, 278.313934);
+path.lineTo(381.654602, 260.165222);
+path.quadTo(399.803314, 268.761993, 417.421356, 268.761993);
+path.quadTo(452.763611, 268.761993, 452.763611, 231.297104);
+path.lineTo(452.763611, 213.148392);
+path.quadTo(441.195129, 237.34668, 414.768036, 237.34668);
+path.quadTo(394.072144, 237.34668, 381.866882, 222.275818);
+path.quadTo(369.661591, 207.204956, 369.661591, 181.626953);
+path.quadTo(369.661591, 155.306015, 383.565002, 138.749298);
+path.quadTo(397.468384, 122.192581, 419.544037, 122.192581);
+path.quadTo(438.860199, 122.192581, 452.763611, 137.900238);
+path.lineTo(452.763611, 124.739769);
+path.lineTo(473.777893, 124.739769);
+path.lineTo(473.777893, 208.584686);
+path.quadTo(473.777893, 235.64856, 470.965363, 248.649826);
+path.quadTo(468.152863, 261.651093, 460.405151, 269.929443);
+path.quadTo(446.71402, 284.469666, 417.845886, 284.469666);
+path.quadTo(397.680664, 284.469666, 379.213562, 278.313934);
+path.close();
+path.moveTo(452.763611, 200.094055);
+path.lineTo(452.763611, 152.228165);
+path.quadTo(438.966339, 137.900238, 422.727997, 137.900238);
+path.quadTo(408.293945, 137.900238, 400.121704, 149.468719);
+path.quadTo(391.949493, 161.037186, 391.949493, 181.202423);
+path.quadTo(391.949493, 219.091827, 418.588837, 219.091827);
+path.quadTo(436.737549, 219.091827, 452.763611, 200.094055);
+path.close();
+path.moveTo(485.555908, 236.285355);
+path.quadTo(464.541595, 242.65332, 449.576874, 242.65332);
+path.quadTo(424.105011, 242.65332, 408.025879, 225.725143);
+path.quadTo(391.946747, 208.796951, 391.946747, 181.839218);
+path.quadTo(391.946747, 155.62442, 406.115479, 138.855438);
+path.quadTo(420.28421, 122.086449, 442.359833, 122.086449);
+path.quadTo(463.268005, 122.086449, 474.677277, 136.945038);
+path.quadTo(486.086548, 151.803635, 486.086548, 179.185898);
+path.lineTo(485.980438, 185.660004);
+path.lineTo(413.279449, 185.660004);
+path.quadTo(417.84317, 226.733398, 453.503784, 226.733398);
+path.quadTo(466.558136, 226.733398, 485.555908, 219.728638);
+path.lineTo(485.555908, 236.285355);
+path.close();
+path.moveTo(414.23465, 169.952347);
+path.lineTo(465.072266, 169.952347);
+path.quadTo(465.072266, 137.794098, 441.086243, 137.794098);
+path.quadTo(416.99411, 137.794098, 414.23465, 169.952347);
+path.close();
+</div>
+
+<div id="testQuadratic42s">
+path.moveTo(342.521729, 138.855438);
+path.quadTo(356.69046,122.086449, 378.766083,122.086449);
+path.quadTo(390.293488,122.086449, 398.933502,126.603004);
+path.quadTo(408.150299,122.192581, 419.544037,122.192581);
+path.quadTo(425.108429,122.192581, 430.223633,123.496056);
+path.quadTo(435.959412,122.086449, 442.359833,122.086449);
+path.quadTo(451.19516,122.086449, 458.334229,124.739769);
+path.lineTo(473.777893,124.739769);
+path.lineTo(473.777893,135.814713);
+path.quadTo(474.234741,136.368698, 474.677277,136.945038);
+path.quadTo(486.086548,151.803635, 486.086548,179.185898);
+path.lineTo(485.980438,185.660004);
+path.lineTo(473.777893,185.660004);
+path.lineTo(473.777893,208.584686);
+path.quadTo(473.777893,216.745773, 473.522156,223.628113);
+path.quadTo(479.207153,222.069519, 485.555908,219.728638);
+path.lineTo(485.555908,236.285355);
+path.quadTo(478.638306,238.381592, 472.376221,239.787796);
+path.quadTo(471.792389,244.826782, 470.965363,248.649826);
+path.quadTo(468.152863,261.651093, 460.405151,269.929443);
+path.quadTo(446.71402,284.469666, 417.845886,284.469666);
+path.quadTo(397.680664,284.469666, 379.213562,278.313934);
+path.lineTo(381.654602,260.165222);
+path.quadTo(399.803314,268.761993, 417.421356,268.761993);
+path.quadTo(446.944275,268.761993, 451.80542,242.619034);
+path.quadTo(450.674866,242.65332, 449.576874,242.65332);
+path.quadTo(434.524628,242.65332, 422.75238,236.741913);
+path.quadTo(420.864227,237.041901, 418.884674,237.193085);
+path.quadTo(399.840271,242.65332, 385.983124,242.65332);
+path.quadTo(360.511261,242.65332, 344.432129,225.725143);
+path.quadTo(328.352997,208.796951, 328.352997,181.839218);
+path.quadTo(328.352997,155.62442, 342.521729,138.855438);
+path.close();
+path.moveTo(383.823944, 138.443222);
+path.quadTo(380.900299,137.794098, 377.492493,137.794098);
+path.quadTo(353.40036,137.794098, 350.6409,169.952347);
+path.lineTo(370.408203,169.952347);
+path.quadTo(372.883484,151.469254, 383.565002,138.749298);
+path.quadTo(383.694122,138.595551, 383.823944,138.443222);
+path.close();
+path.moveTo(369.740021, 185.660004);
+path.lineTo(349.685699,185.660004);
+path.quadTo(353.983734,224.342361, 385.863525,226.594208);
+path.quadTo(383.762756,224.616837, 381.866882,222.275818);
+path.quadTo(370.639954,208.41301, 369.740021,185.660004);
+path.close();
+path.moveTo(413.279449, 185.660004);
+path.quadTo(415.737305,207.780716, 427.214905,217.987976);
+path.quadTo(440.600586,214.512451, 452.763611,200.094055);
+path.lineTo(452.763611,185.660004);
+</div>
+
+<div id="testQuadratic43o">
+path.moveTo(288.755981, 240);
+path.lineTo(288.755981, 102.232819);
+path.lineTo(315.843994, 102.232819);
+path.lineTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(417.493835, 102.232819);
+path.lineTo(417.493835, 240);
+path.lineTo(399.248962, 240);
+path.lineTo(399.248962, 127.92453);
+path.lineTo(361.269928, 230.784485);
+path.lineTo(342.373474, 230.784485);
+path.lineTo(305.511444, 127.645271);
+path.lineTo(305.511444, 240);
+path.lineTo(288.755981, 240);
+path.close();
+path.moveTo(397.864014, 236.741989);
+path.quadTo(379.433014, 242.327148, 366.307892, 242.327148);
+path.quadTo(343.967255, 242.327148, 329.864746, 227.479935);
+path.quadTo(315.762238, 212.632736, 315.762238, 188.988907);
+path.quadTo(315.762238, 165.996674, 328.189209, 151.289093);
+path.quadTo(340.61618, 136.581512, 359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.quadTo(398.329468, 162.645584, 398.329468, 186.661758);
+path.lineTo(398.236359, 192.339996);
+path.lineTo(334.472504, 192.339996);
+path.quadTo(338.475189, 228.364258, 369.752075, 228.364258);
+path.quadTo(381.20163, 228.364258, 397.864014, 222.220581);
+path.lineTo(397.864014, 236.741989);
+path.close();
+path.moveTo(335.310272, 178.563278);
+path.lineTo(379.898438, 178.563278);
+path.quadTo(379.898438, 150.358246, 358.861023, 150.358246);
+path.quadTo(337.730499, 150.358246, 335.310272, 178.563278);
+path.close();
+path.moveTo(346.052765, 240);
+path.lineTo(346.052765, 138.908661);
+path.lineTo(364.390686, 138.908661);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.quadTo(398.832489, 136.674606, 401.904327, 137.140045);
+path.lineTo(401.904327, 154.267853);
+path.quadTo(397.156952, 152.685394, 393.526611, 152.685394);
+path.quadTo(376.119537, 152.685394, 364.390686, 173.350464);
+path.lineTo(364.390686, 240);
+path.lineTo(346.052765, 240);
+path.close();
+path.moveTo(362.792297, 273.604034);
+path.lineTo(364.933289, 257.68634);
+path.quadTo(380.850983, 265.226288, 396.303253, 265.226288);
+path.quadTo(427.300842, 265.226288, 427.300842, 232.366959);
+path.lineTo(427.300842, 216.449265);
+path.quadTo(417.15448, 237.672852, 393.976105, 237.672852);
+path.quadTo(375.824341, 237.672852, 365.119446, 224.454651);
+path.quadTo(354.414581, 211.23645, 354.414581, 188.802734);
+path.quadTo(354.414581, 165.717422, 366.608826, 151.196014);
+path.quadTo(378.803101, 136.674606, 398.164948, 136.674606);
+path.quadTo(415.106598, 136.674606, 427.300842, 150.451324);
+path.lineTo(427.300842, 138.908661);
+path.lineTo(445.731873, 138.908661);
+path.lineTo(445.731873, 212.446564);
+path.quadTo(445.731873, 236.183472, 443.265106, 247.586502);
+path.quadTo(440.798309, 258.989532, 434.003052, 266.250244);
+path.quadTo(421.994965, 279.002991, 396.675598, 279.002991);
+path.quadTo(378.989258, 279.002991, 362.792297, 273.604034);
+path.close();
+path.moveTo(427.300842, 204.999695);
+path.lineTo(427.300842, 163.017929);
+path.quadTo(415.199677, 150.451324, 400.95755, 150.451324);
+path.quadTo(388.297852, 150.451324, 381.130249, 160.597687);
+path.quadTo(373.962616, 170.744064, 373.962616, 188.430389);
+path.quadTo(373.962616, 221.662079, 397.327179, 221.662079);
+path.quadTo(413.244873, 221.662079, 427.300842, 204.999695);
+path.close();
+path.moveTo(461.457764, 236.741989);
+path.quadTo(443.026764, 242.327148, 429.901642, 242.327148);
+path.quadTo(407.561005, 242.327148, 393.458496, 227.479935);
+path.quadTo(379.355988, 212.632736, 379.355988, 188.988907);
+path.quadTo(379.355988, 165.996674, 391.782959, 151.289093);
+path.quadTo(404.20993, 136.581512, 423.571808, 136.581512);
+path.quadTo(441.909729, 136.581512, 451.916473, 149.613556);
+path.quadTo(461.923218, 162.645584, 461.923218, 186.661758);
+path.lineTo(461.830109, 192.339996);
+path.lineTo(398.066254, 192.339996);
+path.quadTo(402.068939, 228.364258, 433.345825, 228.364258);
+path.quadTo(444.79538, 228.364258, 461.457764, 222.220581);
+path.lineTo(461.457764, 236.741989);
+path.close();
+path.moveTo(398.904022, 178.563278);
+path.lineTo(443.492188, 178.563278);
+path.quadTo(443.492188, 150.358246, 422.454773, 150.358246);
+path.quadTo(401.324249, 150.358246, 398.904022, 178.563278);
+path.close();
+</div>
+
+<div id="testQuadratic43s">
+path.moveTo(288.755981, 240);
+path.lineTo(288.755981,102.232819);
+path.lineTo(315.843994,102.232819);
+path.lineTo(331.979736,147.294876);
+path.quadTo(343.453125,136.581512, 359.978058,136.581512);
+path.quadTo(370.869446,136.581512, 378.822021,141.178574);
+path.quadTo(378.893585,141.140915, 378.965302,141.103577);
+path.lineTo(393.291473,102.232819);
+path.lineTo(417.493835,102.232819);
+path.lineTo(417.493835,136.965759);
+path.quadTo(420.44223,136.581512, 423.571808,136.581512);
+path.quadTo(431.320984,136.581512, 437.582458,138.908661);
+path.lineTo(445.731873,138.908661);
+path.lineTo(445.731873,143.392502);
+path.quadTo(449.143951,146.002823, 451.916473,149.613556);
+path.quadTo(461.923218,162.645584, 461.923218,186.661758);
+path.lineTo(461.830109,192.339996);
+path.lineTo(445.731873,192.339996);
+path.lineTo(445.731873,212.446564);
+path.quadTo(445.731873,220.39856, 445.455017,226.966339);
+path.quadTo(452.7435,225.43367, 461.457764,222.220581);
+path.lineTo(461.457764,236.741989);
+path.quadTo(452.250824,239.531982, 444.367889,240.928268);
+path.quadTo(443.897583,244.662796, 443.265106,247.586502);
+path.quadTo(440.798309,258.989532, 434.003052,266.250244);
+path.quadTo(421.994965,279.002991, 396.675598,279.002991);
+path.quadTo(378.989258,279.002991, 362.792297,273.604034);
+path.lineTo(364.933289,257.68634);
+path.quadTo(380.850983,265.226288, 396.303253,265.226288);
+path.quadTo(422.230743,265.226288, 426.471558,242.237076);
+path.quadTo(419.570892,241.869324, 413.503387,240);
+path.lineTo(399.248962,240);
+path.lineTo(399.248962,237.37915);
+path.quadTo(397.047638,237.633072, 394.711517,237.667465);
+path.quadTo(378.296356,242.327148, 366.307892,242.327148);
+path.quadTo(357.463165,242.327148, 349.909637,240);
+path.lineTo(346.052765,240);
+path.lineTo(346.052765,238.625916);
+path.quadTo(336.926056,234.914124, 329.864746,227.479935);
+path.quadTo(315.762238,212.632736, 315.762238,188.988907);
+path.quadTo(315.762238,176.540054, 319.405273,166.519882);
+path.lineTo(305.511444,127.645271);
+path.lineTo(305.511444,240);
+path.lineTo(288.755981,240);
+path.close();
+path.moveTo(375.464813, 192.339996);
+path.lineTo(374.267029,195.583939);
+path.quadTo(375.987579,214.575378, 387.432068,219.736267);
+path.quadTo(380.122528,208.101486, 379.428741,192.339996);
+path.lineTo(375.464813,192.339996);
+path.close();
+path.moveTo(427.300842, 178.563278);
+path.lineTo(427.300842,163.017929);
+path.quadTo(422.561523,158.096329, 417.493835,155.102234);
+path.lineTo(417.493835,178.563278);
+path.lineTo(427.300842,178.563278);
+path.close();
+path.moveTo(427.300842, 192.339996);
+path.lineTo(417.493835,192.339996);
+path.lineTo(417.493835,214.429169);
+path.quadTo(422.505676,210.684036, 427.300842,204.999695);
+path.lineTo(427.300842,192.339996);
+path.close();
+path.moveTo(420.700134, 226.556015);
+path.quadTo(423.794342,227.54834, 427.300842,227.996094);
+path.lineTo(427.300842,216.449265);
+path.quadTo(424.497772,222.312531, 420.700134,226.556015);
+path.close();
+path.moveTo(368.744965, 228.354782);
+path.quadTo(366.836426,226.574738, 365.119446,224.454651);
+path.quadTo(364.748657,223.996796, 364.390686,223.527878);
+path.lineTo(364.390686,228.077774);
+path.quadTo(366.495239,228.312164, 368.744965,228.354782);
+path.close();
+path.moveTo(399.248962, 199.701065);
+path.lineTo(399.248962,192.339996);
+path.lineTo(398.236359,192.339996);
+path.lineTo(398.066254,192.339996);
+path.quadTo(398.498535,196.230621, 399.248962,199.701065);
+path.close();
+path.moveTo(399.248962, 178.563278);
+path.lineTo(399.248962,175.376892);
+path.quadTo(399.04483,176.922348, 398.904022,178.563278);
+path.lineTo(399.248962,178.563278);
+path.close();
+path.moveTo(399.248962, 136.688828);
+path.lineTo(399.248962,127.92453);
+path.lineTo(396.018158,136.674606);
+path.quadTo(396.029053,136.674606, 396.039917,136.674606);
+path.quadTo(396.513672,136.674606, 396.995453,136.688004);
+path.quadTo(397.576904,136.674606, 398.164948,136.674606);
+path.quadTo(398.709412,136.674606, 399.248962,136.688828);
+path.close();
+path.moveTo(346.052765, 178.563278);
+path.lineTo(346.052765,154.02713);
+path.quadTo(340.97113,157.621338, 338.22525,164.736588);
+path.lineTo(343.1763,178.563278);
+path.lineTo(346.052765,178.563278);
+path.close();
+path.moveTo(364.390686, 150.922379);
+path.lineTo(364.390686,154.048065);
+path.quadTo(365.340851,152.726639, 366.38147,151.468765);
+path.quadTo(365.420258,151.14975, 364.390686,150.922379);
+path.close();
+path.moveTo(367.863586, 152.032623);
+path.quadTo(367.144043,151.721848, 366.38147,151.468765);
+</div>
+
+<div id="testQuadratic44o">
+path.moveTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(399.248962, 127.92453);
+path.lineTo(361.269928, 230.784485);
+path.lineTo(354.009216, 208.816208);
+path.close();
+path.moveTo(328.189209, 151.289093);
+path.quadTo(340.61618, 136.581512, 359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.lineTo(328.189209, 151.289093);
+path.close();
+path.moveTo(346.052765, 138.908661);
+path.lineTo(364.390686, 138.908661);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.lineTo(346.052765, 138.908661);
+path.close();
+</div>
+
+<div id="testQuadratic44s">
+path.moveTo(380.33902, 137.376312);
+path.lineTo(393.291473,102.232819);
+path.lineTo(399.248962,127.92453);
+path.lineTo(396.018158,136.674606);
+path.quadTo(396.029053,136.674606, 396.039917,136.674606);
+path.lineTo(396.017792,136.675598);
+path.lineTo(361.269928,230.784485);
+path.lineTo(354.009216,208.816208);
+path.lineTo(375.699249,149.965286);
+path.lineTo(369.22699,150.14563);
+path.quadTo(373.524384,144.511566, 378.917297,141.233871);
+path.lineTo(380.33902,137.376312);
+path.close();
+path.moveTo(392.55661, 136.830276);
+path.quadTo(385.032623,137.51709, 378.917297,141.233856);
+path.quadTo(378.917297,141.233856, 378.917297,141.233856);
+</div>
+
</div>
<script type="text/javascript">
var testDivs = [
+ testQuadratic44o,
+ testQuadratic44s,
+ testQuadratic43o,
+ testQuadratic43s,
+ testQuadratic42o,
+ testQuadratic42s,
+ testQuadratic41o,
+ testQuadratic41s,
+ testQuadratic40xb,
+ testQuadratic40xa,
+ testQuadratic40x,
testQuadratic39,
testQuadratic39a,
testQuadratic38,
@@ -1729,6 +2273,7 @@ var tests = [];
var testTitles = [];
var testIndex = 0;
var hasXor = false;
+var draw_labels = true;
var ctx;
@@ -1939,6 +2484,9 @@ function draw(test, title, _at_x, _at_y, scale) {
ctx.fillStyle="rgba(192,192,255, 0.3)";
ctx.fill();
+ if (!draw_labels) {
+ return;
+ }
ctx.fillStyle="blue";
for (contours in test) {
var contour = test[contours];
@@ -2025,6 +2573,10 @@ function doKeyPress(evt) {
case '+':
drawTop();
break;
+ case 'x':
+ draw_labels ^= true;
+ drawTop();
+ break;
}
}
diff --git a/gyp/shapeops_tool.gyp b/gyp/shapeops_tool.gyp
new file mode 100644
index 0000000000..3b1408a5ff
--- /dev/null
+++ b/gyp/shapeops_tool.gyp
@@ -0,0 +1,45 @@
+# GYP file to build unit tests.
+{
+ 'includes': [
+ 'apptype_console.gypi',
+ 'common.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'addTest',
+ 'type': 'executable',
+ 'include_dirs' : [
+ '../src/core',
+ ],
+ 'sources': [
+ '../experimental/Intersection/AddTestOutput/main.cpp',
+ ],
+ 'dependencies': [
+ 'core.gyp:core',
+ 'effects.gyp:effects',
+ 'experimental.gyp:experimental',
+ 'images.gyp:images',
+ 'ports.gyp:ports',
+ 'pdf.gyp:pdf',
+ 'utils.gyp:utils',
+ ],
+ 'conditions': [
+ [ 'skia_gpu == 1', {
+ 'include_dirs': [
+ '../src/gpu',
+ ],
+ 'dependencies': [
+ 'gpu.gyp:gr',
+ 'gpu.gyp:skgr',
+ ],
+ }],
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2: