aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2016-07-18 10:01:36 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-07-18 10:01:36 -0700
commit55888e44171ffd48b591d19256884a969fe4da17 (patch)
treee3aed151b6074bba5357263199a5915ec31a0b99
parent6451a0cea6007aff54565ec82e2f86cb1d32ecc7 (diff)
pathops coincidence and security rewrite
Most changes stem from working on an examples bracketed by #if DEBUG_UNDER_DEVELOPMENT // tiger These exposed many problems with coincident curves, as well as errors throughout the code. Fixing these errors also fixed a number of fuzzer-inspired bug reports. * Line/Curve Intersections Check to see if the end of the line nearly intersects the curve. This was a FIXME in the old code. * Performance Use a central chunk allocator. Plumb the allocator into the global variable state so that it can be shared. (Note that 'SkGlobalState' is allocated on the stack and is visible to children functions but not other threads.) * Refactor Let SkOpAngle grow up from a structure to a class. Let SkCoincidentSpans grow up from a structure to a class. Rename enum Alias to AliasMatch. * Coincidence Rewrite Add more debugging to coincidence detection. Parallel debugging routines have read-only logic to report the current coincidence state so that steps through the logic can expose whether things got better or worse. More functions can error-out and cause the pathops engine to non-destructively exit. * Accuracy Remove code that adjusted point locations. Instead, offset the curve part so that sorted curves all use the same origin. Reduce the size (and influence) of magic numbers. * Testing The debug suite with verify and the full release suite ./out/Debug/pathops_unittest -v -V ./out/Release/pathops_unittest -v -V -x expose one error. That error is captured as cubics_d3. This error exists in the checked in code as well. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 Review-Url: https://codereview.chromium.org/2128633003
-rw-r--r--src/pathops/SkAddIntersections.cpp17
-rw-r--r--src/pathops/SkAddIntersections.h3
-rw-r--r--src/pathops/SkDConicLineIntersection.cpp22
-rw-r--r--src/pathops/SkDCubicLineIntersection.cpp21
-rw-r--r--src/pathops/SkDQuadLineIntersection.cpp26
-rw-r--r--src/pathops/SkIntersections.h8
-rw-r--r--src/pathops/SkOpAngle.cpp56
-rw-r--r--src/pathops/SkOpAngle.h67
-rw-r--r--src/pathops/SkOpBuilder.cpp12
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp1495
-rw-r--r--src/pathops/SkOpCoincidence.h270
-rw-r--r--src/pathops/SkOpContour.cpp10
-rw-r--r--src/pathops/SkOpContour.h82
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp22
-rw-r--r--src/pathops/SkOpEdgeBuilder.h17
-rw-r--r--src/pathops/SkOpSegment.cpp519
-rw-r--r--src/pathops/SkOpSegment.h124
-rwxr-xr-xsrc/pathops/SkOpSpan.cpp301
-rw-r--r--src/pathops/SkOpSpan.h119
-rw-r--r--src/pathops/SkPathOpsBounds.h14
-rw-r--r--src/pathops/SkPathOpsCommon.cpp192
-rw-r--r--src/pathops/SkPathOpsCommon.h7
-rw-r--r--src/pathops/SkPathOpsCubic.cpp2
-rw-r--r--src/pathops/SkPathOpsCurve.cpp53
-rw-r--r--src/pathops/SkPathOpsCurve.h79
-rw-r--r--src/pathops/SkPathOpsDebug.cpp1704
-rw-r--r--src/pathops/SkPathOpsDebug.h29
-rw-r--r--src/pathops/SkPathOpsOp.cpp57
-rw-r--r--src/pathops/SkPathOpsPoint.h16
-rw-r--r--src/pathops/SkPathOpsQuad.cpp13
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp64
-rw-r--r--src/pathops/SkPathOpsTSect.h19
-rw-r--r--src/pathops/SkPathOpsTightBounds.cpp15
-rw-r--r--src/pathops/SkPathOpsTypes.cpp26
-rw-r--r--src/pathops/SkPathOpsTypes.h37
-rw-r--r--src/pathops/SkPathOpsWinding.cpp15
-rw-r--r--src/pathops/SkReduceOrder.cpp8
-rwxr-xr-xtests/PathOpsAngleIdeas.cpp15
-rw-r--r--tests/PathOpsAngleTest.cpp64
-rwxr-xr-xtests/PathOpsDebug.cpp76
-rw-r--r--tests/PathOpsExtendedTest.cpp97
-rw-r--r--tests/PathOpsExtendedTest.h14
-rwxr-xr-xtests/PathOpsFuzz763Test.cpp16
-rw-r--r--tests/PathOpsIssue3651.cpp8
-rw-r--r--tests/PathOpsOpTest.cpp474
-rw-r--r--tests/PathOpsSimplifyFailTest.cpp40
-rw-r--r--tests/PathOpsSimplifyTest.cpp712
-rw-r--r--tools/pathops_sorter.htm91
-rw-r--r--tools/pathops_visualizer.htm1771
49 files changed, 5996 insertions, 2923 deletions
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 07666e796d..1ca56b8f23 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -254,8 +254,7 @@ static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
}
#endif
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence) {
if (test != next) {
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
return false;
@@ -506,12 +505,14 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
wt.segment()->debugValidate();
- SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
- allocator);
+ SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAliasMatch,
+ nullptr);
wn.segment()->debugValidate();
- SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
- allocator);
- testTAt->addOpp(nextTAt);
+ SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAliasMatch,
+ nullptr);
+ if (testTAt->addOpp(nextTAt)) {
+ testTAt->span()->checkForCollapsedCoincidence();
+ }
if (testTAt->fPt != nextTAt->fPt) {
testTAt->span()->unaligned();
nextTAt->span()->unaligned();
@@ -540,7 +541,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
SkTSwap(testTAt, nextTAt);
}
SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
- coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
+ coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt);
wt.segment()->debugValidate();
wn.segment()->debugValidate();
coinIndex = -1;
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 23654e5e91..ca409800cd 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -12,7 +12,6 @@
class SkOpCoincidence;
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator);
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence);
#endif
diff --git a/src/pathops/SkDConicLineIntersection.cpp b/src/pathops/SkDConicLineIntersection.cpp
index 710647236c..620e99c8ae 100644
--- a/src/pathops/SkDConicLineIntersection.cpp
+++ b/src/pathops/SkDConicLineIntersection.cpp
@@ -6,6 +6,7 @@
*/
#include "SkIntersections.h"
#include "SkPathOpsConic.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
class LineConicIntersections {
@@ -199,7 +200,22 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double conicT = ((SkDCurve*) &fConic)->nearPoint(SkPath::kConic_Verb,
+ (*fLine)[lIndex], (*fLine)[!lIndex]);
+ if (conicT < 0) {
+ continue;
+ }
+ fIntersections->insert(conicT, lineT, (*fLine)[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -225,7 +241,7 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -251,7 +267,7 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index cbdce7789a..045acd9e4f 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -6,6 +6,7 @@
*/
#include "SkIntersections.h"
#include "SkPathOpsCubic.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
/*
@@ -291,6 +292,22 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double cubicT = ((SkDCurve*) &fCubic)->nearPoint(SkPath::kCubic_Verb,
+ fLine[lIndex], fLine[!lIndex]);
+ if (cubicT < 0) {
+ continue;
+ }
+ fIntersections->insert(cubicT, lineT, fLine[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -316,7 +333,7 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
- // FIXME: see if line end is nearly on cubic
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -342,7 +359,7 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
- // FIXME: see if line end is nearly on cubic
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp
index a3d10bc9ff..dc237f57a5 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkIntersections.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
@@ -98,11 +99,11 @@ public:
, fLine(&l)
, fIntersections(i)
, fAllowNear(true) {
- i->setMax(3); // allow short partial coincidence plus discrete intersection
+ i->setMax(4); // allow short partial coincidence plus discrete intersections
}
LineQuadraticIntersections(const SkDQuad& q)
- : fQuad(q)
+ : fQuad(q)
SkDEBUGPARAMS(fLine(nullptr))
SkDEBUGPARAMS(fIntersections(nullptr))
SkDEBUGPARAMS(fAllowNear(false)) {
@@ -297,7 +298,22 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double quadT = ((SkDCurve*) &fQuad)->nearPoint(SkPath::kQuad_Verb,
+ (*fLine)[lIndex], (*fLine)[!lIndex]);
+ if (quadT < 0) {
+ continue;
+ }
+ fIntersections->insert(quadT, lineT, (*fLine)[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -323,7 +339,7 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -349,7 +365,7 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index 474142b269..abc10e19dd 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -112,6 +112,11 @@ public:
return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
}
+ bool hasOppT(double t) const {
+ SkASSERT(t == 0 || t == 1);
+ return fUsed > 0 && (fT[1][0] == t || fT[1][fUsed - 1] == t);
+ }
+
int insertSwap(double one, double two, const SkDPoint& pt) {
if (fSwap) {
return insert(two, one, pt);
@@ -180,7 +185,6 @@ public:
quad.set(a);
SkDLine line;
line.set(b);
- fMax = 3; // 2; permit small coincident segment + non-coincident intersection
return intersect(quad, line);
}
@@ -207,7 +211,7 @@ public:
bool swapped() const {
return fSwap;
}
-
+
int used() const {
return fUsed;
}
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index c2eb0c9326..479a6f673d 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -62,6 +62,12 @@ bool SkOpAngle::after(SkOpAngle* test) {
SkOpAngle* lh = test;
SkOpAngle* rh = lh->fNext;
SkASSERT(lh != rh);
+ fCurvePart = fOriginalCurvePart;
+ lh->fCurvePart = lh->fOriginalCurvePart;
+ lh->fCurvePart.offset(lh->segment()->verb(), fCurvePart[0] - lh->fCurvePart[0]);
+ rh->fCurvePart = rh->fOriginalCurvePart;
+ rh->fCurvePart.offset(rh->segment()->verb(), fCurvePart[0] - rh->fCurvePart[0]);
+
#if DEBUG_ANGLE
SkString bugOut;
bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
@@ -150,9 +156,7 @@ bool SkOpAngle::after(SkOpAngle* test) {
return COMPARE_RESULT(8, ltOpposite);
} else if (ltOrder == 1 && trOrder == 0) {
SkASSERT(lrOrder < 0);
- SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
bool trOpposite = oppositePlanes(rh);
- SkASSERT(ltOpposite != trOpposite);
return COMPARE_RESULT(9, trOpposite);
} else if (lrOrder == 1 && trOrder == 1) {
SkASSERT(ltOrder < 0);
@@ -175,15 +179,8 @@ bool SkOpAngle::after(SkOpAngle* test) {
int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
SkASSERT(!fIsCurve);
SkASSERT(test->fIsCurve);
- const SkDPoint& origin = test->fCurvePart[0];
- SkVector line;
- if (segment()->verb() == SkPath::kLine_Verb) {
- const SkPoint* linePts = segment()->pts();
- int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
- line = linePts[lineStart ^ 1] - linePts[lineStart];
- } else {
- line = (fCurvePart[1] - fCurvePart[0]).asSkVector();
- }
+ SkDPoint origin = fCurvePart[0];
+ SkDVector line = fCurvePart[1] - origin;
float crosses[3];
SkPath::Verb testVerb = test->segment()->verb();
int iMax = SkPathOpsVerbToPoints(testVerb);
@@ -244,8 +241,7 @@ bool SkOpAngle::checkParallel(SkOpAngle* rh) {
// compute the perpendicular to the endpoints and see where it intersects the opposite curve
// if the intersections within the t range, do a cross check on those
bool inside;
- if (!fCurvePart[SkPathOpsVerbToPoints(this->segment()->verb())].approximatelyEqual(
- rh->fCurvePart[SkPathOpsVerbToPoints(rh->segment()->verb())])) {
+ if (!fEnd->contains(rh->fEnd)) {
if (this->endToSide(rh, &inside)) {
return inside;
}
@@ -280,7 +276,7 @@ bool SkOpAngle::computeSector() {
}
fComputedSector = true;
bool stepUp = fStart->t() < fEnd->t();
- const SkOpSpanBase* checkEnd = fEnd;
+ SkOpSpanBase* checkEnd = fEnd;
if (checkEnd->final() && stepUp) {
fUnorderable = true;
return false;
@@ -306,7 +302,7 @@ bool SkOpAngle::computeSector() {
: checkEnd->prev();
} while (checkEnd);
recomputeSector:
- SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
+ SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
: checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
fUnorderable = true;
@@ -398,7 +394,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
int rPts = SkPathOpsVerbToPoints(rVerb);
SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
{{this->fCurvePart[0], this->fCurvePart[lPts]}}};
- if (rays[0][1] == rays[1][1]) {
+ if (this->fEnd->contains(rh->fEnd)) {
return checkParallel(rh);
}
double smallTs[2] = {-1, -1};
@@ -538,14 +534,14 @@ bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
}
double maxWidth = SkTMax(maxX - minX, maxY - minY);
endDist /= maxWidth;
- if (endDist < 5e-11) { // empirically found
+ if (endDist < 5e-12) { // empirically found
return false;
}
const SkDPoint* endPt = &rayEnd[0];
SkDPoint oppPt = iEnd.pt(closestEnd);
SkDVector vLeft = *endPt - start;
SkDVector vRight = oppPt - start;
- double dir = vLeft.crossCheck(vRight);
+ double dir = vLeft.crossNoNormalCheck(vRight);
if (!dir) {
return false;
}
@@ -785,7 +781,7 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- worth finding earlier
return x_ry < rx_y;
}
- if ((result = allOnOneSide(rh)) >= 0) {
+ if ((result = this->allOnOneSide(rh)) >= 0) {
return result;
}
if (fUnorderable || approximately_zero(rh->fSide)) {
@@ -798,11 +794,10 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
if (rh->fUnorderable || approximately_zero(fSide)) {
goto unorderable;
}
- }
- if ((result = convexHullOverlaps(rh)) >= 0) {
+ } else if ((result = this->convexHullOverlaps(rh)) >= 0) {
return result;
}
- return endsIntersect(rh);
+ return this->endsIntersect(rh);
unorderable:
fUnorderable = true;
rh->fUnorderable = true;
@@ -846,8 +841,17 @@ void SkOpAngle::setCurveHullSweep() {
return;
}
fSweep[1] = fCurvePart[2] - fCurvePart[0];
+ // OPTIMIZE: I do the following float check a lot -- probably need a
+ // central place for this val-is-small-compared-to-curve check
+ double maxVal = 0;
+ for (int index = 0; index < SkPathOpsVerbToPoints(segment->verb()); ++index) {
+ maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurvePart[index].fX),
+ SkTAbs(fCurvePart[index].fY)));
+ }
+
if (SkPath::kCubic_Verb != segment->verb()) {
- if (!fSweep[0].fX && !fSweep[0].fY) {
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
fSweep[0] = fSweep[1];
}
return;
@@ -856,7 +860,8 @@ void SkOpAngle::setCurveHullSweep() {
if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
fSweep[0] = fSweep[1];
fSweep[1] = thirdSweep;
- if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
fSweep[0] = fSweep[1];
fCurvePart[1] = fCurvePart[3];
fIsCurve = false;
@@ -894,6 +899,7 @@ void SkOpAngle::setSpans() {
= SK_ScalarNaN);
SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
segment->subDivide(fStart, fEnd, &fCurvePart);
+ fOriginalCurvePart = fCurvePart;
setCurveHullSweep();
const SkPath::Verb verb = segment->verb();
if (verb != SkPath::kLine_Verb
@@ -1049,5 +1055,5 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
double tDist = tweep[0].length() * m;
bool useS = fabs(sDist) < fabs(tDist);
double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
- return mFactor < 2400; // empirically found limit
+ return mFactor < 50; // empirically found limit
}
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 4b209de32d..a1ead1ecfd 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -19,7 +19,8 @@ class SkOpSegment;
class SkOpSpanBase;
class SkOpSpan;
-struct SkOpAngle {
+class SkOpAngle {
+public:
enum IncludeType {
kUnaryWinding,
kUnaryXor,
@@ -27,14 +28,8 @@ struct SkOpAngle {
kBinaryOpp,
};
- bool after(SkOpAngle* test);
- int allOnOneSide(const SkOpAngle* test);
- bool checkCrossesZero() const;
- bool checkParallel(SkOpAngle* );
- bool computeSector();
- int convexHullOverlaps(const SkOpAngle* ) const;
-
const SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
int debugID() const {
@@ -46,6 +41,7 @@ struct SkOpAngle {
#endif
#if DEBUG_ANGLE
+ bool debugCheckCoincidence() const { return fCheckCoincidence; }
void debugCheckNearCoincidence() const;
SkString debugPart() const;
#endif
@@ -53,7 +49,7 @@ struct SkOpAngle {
const SkOpSegment* debugSegment(int id) const;
int debugSign() const;
const SkOpSpanBase* debugSpan(int id) const;
- void debugValidate() const;
+ void debugValidate() const;
void debugValidateNext() const; // in debug builds, verify that angle loop is uncorrupted
double distEndRatio(double dist) const;
// available to testing only
@@ -68,62 +64,56 @@ struct SkOpAngle {
return fEnd;
}
- bool endsIntersect(SkOpAngle* );
- bool endToSide(const SkOpAngle* rh, bool* inside) const;
- int findSector(SkPath::Verb verb, double x, double y) const;
- SkOpGlobalState* globalState() const;
void insert(SkOpAngle* );
SkOpSpanBase* lastMarked() const;
bool loopContains(const SkOpAngle* ) const;
int loopCount() const;
- bool merge(SkOpAngle* );
- double midT() const;
- bool midToSide(const SkOpAngle* rh, bool* inside) const;
SkOpAngle* next() const {
return fNext;
}
- bool oppositePlanes(const SkOpAngle* rh) const;
- bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
SkOpAngle* previous() const;
-
- int sectorEnd() const {
- return fSectorEnd;
- }
-
- int sectorStart() const {
- return fSectorStart;
- }
-
SkOpSegment* segment() const;
-
void set(SkOpSpanBase* start, SkOpSpanBase* end);
- void setCurveHullSweep();
-
- void setID(int id) {
- SkDEBUGCODE(fID = id);
- }
void setLastMarked(SkOpSpanBase* marked) {
fLastMarked = marked;
}
- void setSector();
- void setSpans();
-
SkOpSpanBase* start() const {
return fStart;
}
SkOpSpan* starter();
- bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
bool unorderable() const {
return fUnorderable;
}
- SkDCurve fCurvePart; // the curve from start to end
+private:
+ bool after(SkOpAngle* test);
+ int allOnOneSide(const SkOpAngle* test);
+ bool checkCrossesZero() const;
+ bool checkParallel(SkOpAngle* );
+ bool computeSector();
+ int convexHullOverlaps(const SkOpAngle* ) const;
+ bool endToSide(const SkOpAngle* rh, bool* inside) const;
+ bool endsIntersect(SkOpAngle* );
+ int findSector(SkPath::Verb verb, double x, double y) const;
+ SkOpGlobalState* globalState() const;
+ bool merge(SkOpAngle* );
+ double midT() const;
+ bool midToSide(const SkOpAngle* rh, bool* inside) const;
+ bool oppositePlanes(const SkOpAngle* rh) const;
+ bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
+ void setCurveHullSweep();
+ void setSector();
+ void setSpans();
+ bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+
+ SkDCurve fOriginalCurvePart; // the curve from start to end
+ SkDCurve fCurvePart; // the curve from start to end offset as needed
double fSide;
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
SkOpAngle* fNext;
@@ -143,6 +133,7 @@ struct SkOpAngle {
bool fCheckCoincidence;
SkDEBUGCODE(int fID);
+ friend class PathOpsAngleTester;
};
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 67aa92fe26..6652c10d52 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -44,13 +44,11 @@ bool FixWinding(SkPath* path) {
}
SkChunkAlloc allocator(4096);
SkOpContourHead contourHead;
- SkOpGlobalState globalState(nullptr, &contourHead SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
- SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
- builder.finish(&allocator);
- if (!contourHead.next()) {
- return false;
- }
+ SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
+ builder.finish();
+ SkASSERT(contourHead.next());
contourHead.resetReverse();
bool writePath = false;
SkOpSpan* topSpan;
@@ -67,7 +65,7 @@ bool FixWinding(SkPath* path) {
topContour->setReverse();
writePath = true;
}
- topContour->markDone();
+ topContour->markAllDone();
globalState.clearNested();
}
if (!writePath) {
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index 4b46e685ee..af330dfe1e 100755
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -8,237 +8,725 @@
#include "SkOpSegment.h"
#include "SkPathOpsTSect.h"
-bool SkOpCoincidence::extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd) {
- // if there is an existing pair that overlaps the addition, extend it
+#if DEBUG_COINCIDENCE
+#define FAIL_IF(cond) SkASSERT(!(cond))
+#else
+#define FAIL_IF(cond) do { if (cond) return false; } while (false)
+#endif
+
+// returns true if coincident span's start and end are the same
+bool SkCoincidentSpans::collapsed(const SkOpPtT* test) const {
+ return (fCoinPtTStart == test && fCoinPtTEnd->contains(test))
+ || (fCoinPtTEnd == test && fCoinPtTStart->contains(test))
+ || (fOppPtTStart == test && fOppPtTEnd->contains(test))
+ || (fOppPtTEnd == test && fOppPtTStart->contains(test));
+}
+
+// sets the span's end to the ptT referenced by the previous-next
+void SkCoincidentSpans::correctOneEnd(
+ const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
+ void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) ) {
+ const SkOpPtT* origPtT = (this->*getEnd)();
+ const SkOpSpanBase* origSpan = origPtT->span();
+ const SkOpSpan* prev = origSpan->prev();
+ const SkOpPtT* testPtT = prev ? prev->next()->ptT()
+ : origSpan->upCast()->next()->prev()->ptT();
+ if (origPtT != testPtT) {
+ (this->*setEnd)(testPtT);
+ }
+}
+
+// FIXME: member pointers have fallen out of favor and can be replaced with
+// an alternative approach.
+// makes all span ends agree with the segment's spans that define them
+void SkCoincidentSpans::correctEnds() {
+ this->correctOneEnd(&SkCoincidentSpans::coinPtTStart, &SkCoincidentSpans::setCoinPtTStart);
+ this->correctOneEnd(&SkCoincidentSpans::coinPtTEnd, &SkCoincidentSpans::setCoinPtTEnd);
+ this->correctOneEnd(&SkCoincidentSpans::oppPtTStart, &SkCoincidentSpans::setOppPtTStart);
+ this->correctOneEnd(&SkCoincidentSpans::oppPtTEnd, &SkCoincidentSpans::setOppPtTEnd);
+}
+
+/* Please keep this in sync with debugExpand */
+// expand the range by checking adjacent spans for coincidence
+bool SkCoincidentSpans::expand() {
+ bool expanded = false;
+ const SkOpSegment* segment = coinPtTStart()->segment();
+ const SkOpSegment* oppSegment = oppPtTStart()->segment();
+ do {
+ const SkOpSpan* start = coinPtTStart()->span()->upCast();
+ const SkOpSpan* prev = start->prev();
+ const SkOpPtT* oppPtT;
+ if (!prev || !(oppPtT = prev->contains(oppSegment))) {
+ break;
+ }
+ double midT = (prev->t() + start->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ setStarts(prev->ptT(), oppPtT);
+ expanded = true;
+ } while (true);
+ do {
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
+ const SkOpPtT* oppPtT;
+ if (!next || !(oppPtT = next->contains(oppSegment))) {
+ break;
+ }
+ double midT = (end->t() + next->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ setEnds(next->ptT(), oppPtT);
+ expanded = true;
+ } while (true);
+ return expanded;
+}
+
+// increase the range of this span
+bool SkCoincidentSpans::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ bool result = false;
+ if (fCoinPtTStart->fT > coinPtTStart->fT || (this->flipped()
+ ? fOppPtTStart->fT < oppPtTStart->fT : fOppPtTStart->fT > oppPtTStart->fT)) {
+ this->setStarts(coinPtTStart, oppPtTStart);
+ result = true;
+ }
+ if (fCoinPtTEnd->fT < coinPtTEnd->fT || (this->flipped()
+ ? fOppPtTEnd->fT > oppPtTEnd->fT : fOppPtTEnd->fT < oppPtTEnd->fT)) {
+ this->setEnds(coinPtTEnd, oppPtTEnd);
+ result = true;
+ }
+ return result;
+}
+
+// set the range of this span
+void SkCoincidentSpans::set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart,
+ const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd
+ SkDEBUGPARAMS(int id)) {
+ SkASSERT(SkOpCoincidence::Ordered(coinPtTStart, oppPtTStart));
+ fNext = next;
+ this->setStarts(coinPtTStart, oppPtTStart);
+ this->setEnds(coinPtTEnd, oppPtTEnd);
+ SkDEBUGCODE(fID = id);
+}
+
+// returns true if both points are inside this
+bool SkCoincidentSpans::contains(const SkOpPtT* s, const SkOpPtT* e) const {
+ if (s->fT > e->fT) {
+ SkTSwap(s, e);
+ }
+ if (s->segment() == fCoinPtTStart->segment()) {
+ return fCoinPtTStart->fT <= s->fT && e->fT <= fCoinPtTEnd->fT;
+ } else {
+ SkASSERT(s->segment() == fOppPtTStart->segment());
+ double oppTs = fOppPtTStart->fT;
+ double oppTe = fOppPtTEnd->fT;
+ if (oppTs > oppTe) {
+ SkTSwap(oppTs, oppTe);
+ }
+ return oppTs <= s->fT && e->fT <= oppTe;
+ }
+}
+
+// returns the number of segment span's contained by this, or -1 if inconsistent
+int SkCoincidentSpans::spanCount() const {
+ // most commonly, concidence are one span long; check for that first
+ const SkOpSpanBase* start = coinPtTStart()->span();
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ int coinIntervals = 0;
+ while (start != end) {
+ coinIntervals++;
+ start = start->upCast()->next();
+ }
+ const SkOpSpanBase* oppStart = (flipped() ? oppPtTEnd() : oppPtTStart())->span();
+ const SkOpSpanBase* oppEnd = (flipped() ? oppPtTStart() : oppPtTEnd())->span();
+ int oppIntervals = 0;
+ while (oppStart != oppEnd) {
+ oppIntervals++;
+ oppStart = oppStart->upCast()->next();
+ }
+ return coinIntervals == oppIntervals ? coinIntervals : -1;
+}
+
+// returns true if the point is on a coincident edge, and if it is the start of that edge
+bool SkOpCoincidence::edge(const SkOpPtT* test, bool* start) const {
SkCoincidentSpans* coinRec = fHead;
- if (coinRec) {
- do {
- if (coinRec->fCoinPtTStart->segment() != coinPtTStart->segment()) {
- continue;
- }
- if (coinRec->fOppPtTStart->segment() != oppPtTStart->segment()) {
+ if (!coinRec) {
+ return false;
+ }
+ do {
+ if (coinRec->coinPtTStart() == test) {
+ *start = true;
+ return true;
+ }
+ if (coinRec->coinPtTEnd() == test) {
+ *start = false;
+ return true;
+ }
+ if (coinRec->oppPtTStart() == test) {
+ *start = !coinRec->flipped();
+ return true;
+ }
+ if (coinRec->coinPtTEnd() == test) {
+ *start = coinRec->flipped();
+ return true;
+ }
+ } while ((coinRec = coinRec->next()));
+ return false;
+}
+
+// if there is an existing pair that overlaps the addition, extend it
+bool SkOpCoincidence::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ SkCoincidentSpans* test = fHead;
+ if (!test) {
+ return false;
+ }
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ SkTSwap(coinSeg, oppSeg);
+ SkTSwap(coinPtTStart, oppPtTStart);
+ SkTSwap(coinPtTEnd, oppPtTEnd);
+ if (coinPtTStart->fT > coinPtTEnd->fT) {
+ SkTSwap(coinPtTStart, coinPtTEnd);
+ SkTSwap(oppPtTStart, oppPtTEnd);
+ }
+ }
+ double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
+ SkDEBUGCODE(double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT));
+ do {
+ if (coinSeg != test->coinPtTStart()->segment()) {
+ continue;
+ }
+ if (oppSeg != test->oppPtTStart()->segment()) {
+ continue;
+ }
+ double oTestMinT = SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
+ double oTestMaxT = SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
+ // if debug check triggers, caller failed to check if extended already exists
+ SkASSERT(test->coinPtTStart()->fT > coinPtTStart->fT
+ || coinPtTEnd->fT > test->coinPtTEnd()->fT
+ || oTestMinT > oppMinT || oppMaxT > oTestMaxT);
+ if ((test->coinPtTStart()->fT <= coinPtTEnd->fT
+ && coinPtTStart->fT <= test->coinPtTEnd()->fT)
+ || (oTestMinT <= oTestMaxT && oppMinT <= oTestMaxT)) {
+ test->extend(coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ return true;
+ }
+ } while ((test = test->next()));
+ return false;
+}
+
+// verifies that the coincidence hasn't already been added
+static void DebugCheckAdd(const SkCoincidentSpans* check, const SkOpPtT* coinPtTStart,
+ const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+#if DEBUG_COINCIDENCE
+ while (check) {
+ SkASSERT(check->coinPtTStart() != coinPtTStart || check->coinPtTEnd() != coinPtTEnd
+ || check->oppPtTStart() != oppPtTStart || check->oppPtTEnd() != oppPtTEnd);
+ SkASSERT(check->coinPtTStart() != oppPtTStart || check->coinPtTEnd() != oppPtTEnd
+ || check->oppPtTStart() != coinPtTStart || check->oppPtTEnd() != coinPtTEnd);
+ check = check->next();
+ }
+#endif
+}
+
+// adds a new coincident pair
+void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+ SkOpPtT* oppPtTEnd) {
+ // OPTIMIZE: caller should have already sorted
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ if (oppPtTStart->fT < oppPtTEnd->fT) {
+ this->add(oppPtTStart, oppPtTEnd, coinPtTStart, coinPtTEnd);
+ } else {
+ this->add(oppPtTEnd, oppPtTStart, coinPtTEnd, coinPtTStart);
+ }
+ return;
+ }
+ SkASSERT(Ordered(coinPtTStart, oppPtTStart));
+ // choose the ptT at the front of the list to track
+ coinPtTStart = coinPtTStart->span()->ptT();
+ coinPtTEnd = coinPtTEnd->span()->ptT();
+ oppPtTStart = oppPtTStart->span()->ptT();
+ oppPtTEnd = oppPtTEnd->span()->ptT();
+ SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
+ SkASSERT(oppPtTStart->fT != oppPtTEnd->fT);
+ SkASSERT(!coinPtTStart->deleted());
+ SkASSERT(!coinPtTEnd->deleted());
+ SkASSERT(!oppPtTStart->deleted());
+ SkASSERT(!oppPtTEnd->deleted());
+ DebugCheckAdd(fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ DebugCheckAdd(fTop, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(
+ this->globalState()->allocator());
+ coinRec->init();
+ coinRec->set(this->fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd
+ SkDEBUGPARAMS(fGlobalState->nextCoinID()));
+ fHead = coinRec;
+}
+
+// description below
+void SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan) {
+ const SkOpPtT* testPtT = testSpan->ptT();
+ const SkOpPtT* stopPtT = testPtT;
+ const SkOpSegment* baseSeg = base->segment();
+ while ((testPtT = testPtT->next()) != stopPtT) {
+ const SkOpSegment* testSeg = testPtT->segment();
+ if (testPtT->deleted()) {
+ continue;
+ }
+ if (testSeg == baseSeg) {
+ continue;
+ }
+ if (this->contains(baseSeg, testSeg, testPtT->fT)) {
+ continue;
+ }
+ // intersect perp with base->ptT() with testPtT->segment()
+ SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
+ const SkPoint& pt = base->pt();
+ SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
+ SkIntersections i;
+ (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
+ for (int index = 0; index < i.used(); ++index) {
+ double t = i[0][index];
+ if (!between(0, t, 1)) {
continue;
}
- if (coinRec->fCoinPtTStart->fT > coinPtTEnd->fT) {
+ SkDPoint oppPt = i.pt(index);
+ if (!oppPt.approximatelyEqual(pt)) {
continue;
}
- if (coinRec->fCoinPtTEnd->fT < coinPtTStart->fT) {
+ SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
+ SkOpPtT* oppStart = writableSeg->addT(t, SkOpSegment::kAllowAliasMatch, nullptr);
+ SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
+ oppStart->span()->addOppAndMerge(writableBase);
+ if (oppStart->deleted()) {
continue;
}
- if (coinRec->fCoinPtTStart->fT > coinPtTStart->fT) {
- coinRec->fCoinPtTStart = coinPtTStart;
- coinRec->fOppPtTStart = oppPtTStart;
+ SkOpSegment* coinSeg = base->segment();
+ SkOpSegment* oppSeg = oppStart->segment();
+ double coinTs, coinTe, oppTs, oppTe;
+ if (coinSeg < oppSeg) {
+ coinTs = base->t();
+ coinTe = testSpan->t();
+ oppTs = oppStart->fT;
+ oppTe = testPtT->fT;
+ } else {
+ SkTSwap(coinSeg, oppSeg);
+ coinTs = oppStart->fT;
+ coinTe = testPtT->fT;
+ oppTs = base->t();
+ oppTe = testSpan->t();
}
- if (coinRec->fCoinPtTEnd->fT < coinPtTEnd->fT) {
- coinRec->fCoinPtTEnd = coinPtTEnd;
- coinRec->fOppPtTEnd = oppPtTEnd;
+ if (coinTs > coinTe) {
+ SkTSwap(coinTs, coinTe);
+ SkTSwap(oppTs, oppTe);
}
- return true;
- } while ((coinRec = coinRec->fNext));
+ (void) this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe);
+ }
}
- return false;
}
-void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
- SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
- bool flipped = oppPtTStart->fT > oppPtTEnd->fT;
- SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
- coinRec->fNext = this->fHead;
- coinRec->fCoinPtTStart = coinPtTStart;
- coinRec->fCoinPtTEnd = coinPtTEnd;
- coinRec->fOppPtTStart = oppPtTStart;
- coinRec->fOppPtTEnd = oppPtTEnd;
- coinRec->fFlipped = flipped;
- SkDEBUGCODE(coinRec->fID = fDebugState->nextCoinID());
-
- this->fHead = coinRec;
+// description below
+bool SkOpCoincidence::addEndMovedSpans(const SkOpPtT* ptT) {
+ const SkOpSpan* base = ptT->span()->upCast();
+ const SkOpSpan* prev = base->prev();
+ if (!prev) {
+ return false;
+ }
+ if (!prev->isCanceled()) {
+ this->addEndMovedSpans(base, base->prev());
+ }
+ if (!base->isCanceled()) {
+ this->addEndMovedSpans(base, base->next());
+ }
+ return true;
}
-static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
- const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
- double denom = overE->fT - overS->fT;
- double start = 0 < denom ? tStart : tEnd;
- double end = 0 < denom ? tEnd : tStart;
- double sRatio = (start - overS->fT) / denom;
- double eRatio = (end - overS->fT) / denom;
- *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
- *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
+/* If A is coincident with B and B includes an endpoint, and A's matching point
+ is not the endpoint (i.e., there's an implied line connecting B-end and A)
+ then assume that the same implied line may intersect another curve close to B.
+ Since we only care about coincidence that was undetected, look at the
+ ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
+ next door) and see if the A matching point is close enough to form another
+ coincident pair. If so, check for a new coincident span between B-end/A ptT loop
+ and the adjacent ptT loop.
+*/
+bool SkOpCoincidence::addEndMovedSpans() {
+ SkCoincidentSpans* span = fHead;
+ if (!span) {
+ return true;
+ }
+ fTop = span;
+ fHead = nullptr;
+ do {
+ if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
+ if (1 == span->coinPtTStart()->fT) {
+ return false;
+ }
+ bool onEnd = span->coinPtTStart()->fT == 0;
+ bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
+ if (onEnd) {
+ if (!oOnEnd) { // if both are on end, any nearby intersect was already found
+ if (!this->addEndMovedSpans(span->oppPtTStart())) {
+ return false;
+ }
+ }
+ } else if (oOnEnd) {
+ if (!this->addEndMovedSpans(span->coinPtTStart())) {
+ return false;
+ }
+ }
+ }
+ if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
+ bool onEnd = span->coinPtTEnd()->fT == 1;
+ bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
+ if (onEnd) {
+ if (!oOnEnd) {
+ if (!this->addEndMovedSpans(span->oppPtTEnd())) {
+ return false;
+ }
+ }
+ } else if (oOnEnd) {
+ if (!this->addEndMovedSpans(span->coinPtTEnd())) {
+ return false;
+ }
+ }
+ }
+ } while ((span = span->next()));
+ this->restoreHead();
+ return true;
}
-bool SkOpCoincidence::addExpanded(SkChunkAlloc* allocator
- PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* globalState)) {
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kIntersecting);
-#endif
- // for each coincident pair, match the spans
- // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
+/* Please keep this in sync with debugAddExpanded */
+// for each coincident pair, match the spans
+// if the spans don't match, add the missing pt to the segment and loop it in the opposite span
+bool SkOpCoincidence::addExpanded() {
SkCoincidentSpans* coin = this->fHead;
- SkASSERT(coin);
+ if (!coin) {
+ return true;
+ }
do {
- SkOpPtT* startPtT = coin->fCoinPtTStart;
- SkOpPtT* oStartPtT = coin->fOppPtTStart;
+ const SkOpPtT* startPtT = coin->coinPtTStart();
+ const SkOpPtT* oStartPtT = coin->oppPtTStart();
SkASSERT(startPtT->contains(oStartPtT));
- SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
- SkOpSpanBase* start = startPtT->span();
- SkOpSpanBase* oStart = oStartPtT->span();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- if (oEnd->deleted()) {
- return false;
- }
- SkOpSpanBase* test = start->upCast()->next();
- SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
+ SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
+ const SkOpSpanBase* start = startPtT->span();
+ const SkOpSpanBase* oStart = oStartPtT->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
+ const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
+ FAIL_IF(oEnd->deleted());
+ const SkOpSpanBase* test = start->upCast()->next();
+ const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
if (!oTest) {
return false;
}
while (test != end || oTest != oEnd) {
- if (!test->ptT()->contains(oTest->ptT())) {
+ if (!test->ptT()->contains(oStart->segment())
+ || !oTest->ptT()->contains(start->segment())) {
// use t ranges to guess which one is missing
- double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
- if (!startRange) {
- return false;
- }
+ double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
+ FAIL_IF(!startRange);
double startPart = (test->t() - startPtT->fT) / startRange;
- double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
- if (!oStartRange) {
- return false;
- }
+ double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
+ FAIL_IF(!oStartRange);
double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
- if (startPart == oStartPart) {
+ FAIL_IF(startPart == oStartPart);
+ bool startOver = false;
+ bool success = startPart < oStartPart
+ ? oStart->segment()->addExpanded(
+ oStartPtT->fT + oStartRange * startPart, test, &startOver)
+ : start->segment()->addExpanded(
+ startPtT->fT + startRange * oStartPart, oTest, &startOver);
+ if (!success) {
+ SkASSERT(0);
return false;
}
- SkOpPtT* newPt;
- if (startPart < oStartPart) {
- double newT = oStartPtT->fT + oStartRange * startPart;
- newPt = oStart->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
- if (!newPt) {
- return false;
- }
- newPt->fPt = test->pt();
- test->ptT()->addOpp(newPt);
- } else {
- double newT = startPtT->fT + startRange * oStartPart;
- newPt = start->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
- if (!newPt) {
- return false;
- }
- newPt->fPt = oTest->pt();
- oTest->ptT()->addOpp(newPt);
+ if (startOver) {
+ test = start;
+ oTest = oStart;
}
- // start over
- test = start;
- oTest = oStart;
}
if (test != end) {
test = test->upCast()->next();
}
if (oTest != oEnd) {
- oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
+ oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
if (!oTest) {
return false;
}
}
}
- } while ((coin = coin->fNext));
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kWalking);
-#endif
+ } while ((coin = coin->next()));
return true;
}
+// checks to see if coincidence has already been found
+bool SkOpCoincidence::alreadyAdded(const SkCoincidentSpans* check, const SkCoincidentSpans* outer,
+ const SkOpPtT* over1s, const SkOpPtT* over1e) const {
+ do {
+ if (check->oppPtTStart() == outer->coinPtTStart() && check->coinPtTStart() == over1s
+ && check->oppPtTEnd() == outer->coinPtTEnd() && check->coinPtTEnd() == over1e) {
+ return true;
+ }
+ if (check->coinPtTStart() == outer->coinPtTStart() && check->oppPtTStart() == over1s
+ && check->coinPtTEnd() == outer->coinPtTEnd() && check->oppPtTEnd() == over1e) {
+ return true;
+ }
+ if (check->startEquals(outer->oppPtTStart()->span(), over1s->span())) {
+ SkDEBUGCODE(check->debugStartCheck(outer->oppPtTEnd()->span(), over1e->span(),
+ fGlobalState));
+ return true;
+ }
+ if (check->startEquals(over1s->span(), outer->coinPtTStart()->span())) {
+ SkDEBUGCODE(check->debugStartCheck(over1e->span(), outer->oppPtTEnd()->span(),
+ fGlobalState));
+ return true;
+ }
+ } while ((check = check->next()));
+ return false;
+}
+
+ /* Please keep this in sync with debugAddIfMissing() */
bool SkOpCoincidence::addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s,
- SkOpPtT* over1e, SkChunkAlloc* allocator) {
- SkCoincidentSpans* check = this->fTop;
+ SkOpPtT* over1e) {
+ SkASSERT(fTop);
+ if (this->alreadyAdded(fTop, outer, over1s, over1e)) {
+ return false;
+ }
+ if (fHead && this->alreadyAdded(fHead, outer, over1s, over1e)) {
+ return false;
+ }
+ this->add(outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e);
+ this->debugValidate();
+ return true;
+}
+
+// given a t span, map the same range on the coincident span
+void SkOpCoincidence::TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
+ double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs,
+ double* coinTe) {
+ double denom = overE->fT - overS->fT;
+ double start = 0 < denom ? tStart : tEnd;
+ double end = 0 < denom ? tEnd : tStart;
+ double sRatio = (start - overS->fT) / denom;
+ double eRatio = (end - overS->fT) / denom;
+ *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
+ *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
+}
+
+// return true if span overlaps existing and needs to adjust the coincident list
+bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check,
+ const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ SkTDArray<SkCoincidentSpans*>* overlaps) const {
+ if (!Ordered(coinSeg, oppSeg)) {
+ if (oppTs < oppTe) {
+ return this->checkOverlap(check, oppSeg, coinSeg, oppTs, oppTe, coinTs, coinTe,
+ overlaps);
+ }
+ return this->checkOverlap(check, oppSeg, coinSeg, oppTe, oppTs, coinTe, coinTs, overlaps);
+ }
+ bool swapOpp = oppTs > oppTe;
+ if (swapOpp) {
+ SkTSwap(oppTs, oppTe);
+ }
do {
- if (check->fCoinPtTStart->span() == over1s->span()
- && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
- || !fDebugState->debugRunFail());
- return false;
+ if (check->coinPtTStart()->segment() != coinSeg) {
+ continue;
}
- if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
- && check->fOppPtTStart->span() == over1s->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- return false;
+ if (check->oppPtTStart()->segment() != oppSeg) {
+ continue;
}
- } while ((check = check->fNext));
- this->add(outer->fCoinPtTStart, outer->fCoinPtTEnd, over1s, over1e, allocator);
-#if 0
- // FIXME: up to four flavors could be added -- do we need only one?
-#endif
+ double checkTs = check->coinPtTStart()->fT;
+ double checkTe = check->coinPtTEnd()->fT;
+ bool coinOutside = coinTe < checkTs || coinTs > checkTe;
+ double oCheckTs = check->oppPtTStart()->fT;
+ double oCheckTe = check->oppPtTEnd()->fT;
+ if (swapOpp) {
+ if (oCheckTs <= oCheckTe) {
+ return false;
+ }
+ SkTSwap(oCheckTs, oCheckTe);
+ }
+ bool oppOutside = oppTe < oCheckTs || oppTs > oCheckTe;
+ if (coinOutside && oppOutside) {
+ continue;
+ }
+ bool coinInside = coinTe <= checkTe && coinTs >= checkTs;
+ bool oppInside = oppTe <= oCheckTe && oppTs >= oCheckTs;
+ if (coinInside && oppInside) {
+ return false; // complete overlap, already included, do nothing
+ }
+ *overlaps->append() = check; // partial overlap, extend existing entry
+ } while ((check = check->next()));
return true;
}
+/* Please keep this in sync with debugAddIfMissing() */
bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
- const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+ const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
+ SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
double coinTs, coinTe, oppTs, oppTe;
- t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
- t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
+ TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ bool swap = coinTs > coinTe;
+ if (swap) {
+ SkTSwap(coinTs, coinTe);
+ }
+ if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
+ SkTSwap(oppTs, oppTe);
+ }
+ if (swap) {
+ SkTSwap(oppTs, oppTe);
+ }
SkOpSegment* coinSeg = coinPtTStart->segment();
SkOpSegment* oppSeg = oppPtTStart->segment();
- SkASSERT(coinSeg != oppSeg);
- SkCoincidentSpans* check = this->fTop;
- do {
- const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
- if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
- continue;
+ if (coinSeg == oppSeg) {
+ return false;
+ }
+ return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe);
+}
+
+/* Please keep this in sync with debugAddOrOverlap() */
+bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe) {
+ SkTDArray<SkCoincidentSpans*> overlaps;
+ SkASSERT(fTop);
+ if (!this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
+ return false;
+ }
+ if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
+ coinTe, oppTs, oppTe, &overlaps)) {
+ return false;
+ }
+ SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
+ for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
+ SkCoincidentSpans* test = overlaps[index];
+ if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
+ overlap->setCoinPtTStart(test->coinPtTStart());
}
- const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
- if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
- continue;
+ if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
+ overlap->setCoinPtTEnd(test->coinPtTEnd());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
+ : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
+ overlap->setOppPtTStart(test->oppPtTStart());
}
- int cTs = coinTs;
- int cTe = coinTe;
- int oTs = oppTs;
- int oTe = oppTe;
- if (checkCoinSeg != coinSeg) {
- SkASSERT(checkOppSeg != oppSeg);
- SkTSwap(cTs, oTs);
- SkTSwap(cTe, oTe);
- }
- int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
- + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
-// SkASSERT(tweenCount == 0 || tweenCount == 4);
- if (tweenCount) {
+ if (overlap->flipped()
+ ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
+ : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
+ overlap->setOppPtTEnd(test->oppPtTEnd());
+ }
+ if (!fHead || !this->release(fHead, test)) {
+ SkAssertResult(this->release(fTop, test));
+ }
+ }
+ const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
+ const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
+ if (overlap && cs && ce && overlap->contains(cs, ce)) {
+ return false;
+ }
+ SkASSERT(cs != ce || !cs);
+ const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
+ const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
+ if (overlap && os && oe && overlap->contains(os, oe)) {
+ return false;
+ }
+ SkASSERT(!cs || !cs->deleted());
+ SkASSERT(!os || !os->deleted());
+ SkASSERT(!ce || !ce->deleted());
+ SkASSERT(!oe || !oe->deleted());
+ const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
+ const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
+ if (csExisting && csExisting == ceExisting) {
+ return false;
+ }
+ if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
+ return false;
+ }
+ if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
+ return false;
+ }
+ const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
+ const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
+ if (osExisting && osExisting == oeExisting) {
+ return false;
+ }
+ if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
+ return false;
+ }
+ if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
+ return false;
+ }
+ // extra line in debug code
+ this->debugValidate();
+ if (!cs || !os) {
+ SkOpPtT* csWritable = cs ? const_cast<SkOpPtT*>(cs)
+ : coinSeg->addT(coinTs, SkOpSegment::kNoAliasMatch, nullptr);
+ SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os)
+ : oppSeg->addT(oppTs, SkOpSegment::kNoAliasMatch, nullptr);
+ csWritable->span()->addOppAndMerge(osWritable->span());
+ cs = csWritable;
+ os = osWritable;
+ if ((ce && ce->deleted()) || (oe && oe->deleted())) {
return false;
}
- } while ((check = check->fNext));
- if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
- SkTSwap(oppTs, oppTe);
}
- if (coinTs > coinTe) {
- SkTSwap(coinTs, coinTe);
- SkTSwap(oppTs, oppTe);
+ if (!ce || !oe) {
+ SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce)
+ : coinSeg->addT(coinTe, SkOpSegment::kNoAliasMatch, nullptr);
+ SkOpPtT* oeWritable = oe ? const_cast<SkOpPtT*>(oe)
+ : oppSeg->addT(oppTe, SkOpSegment::kNoAliasMatch, nullptr);
+ ceWritable->span()->addOppAndMerge(oeWritable->span());
+ ce = ceWritable;
+ oe = oeWritable;
}
- SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
- SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
- SkASSERT(cs != ce);
- SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator);
- SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator);
-// SkASSERT(os != oe);
- cs->addOpp(os);
- ce->addOpp(oe);
- this->add(cs, ce, os, oe, allocator);
- return true;
+ this->debugValidate();
+ if (cs->deleted() || os->deleted() || ce->deleted() || oe->deleted()) {
+ return false;
+ }
+ if (cs->contains(ce) || os->contains(oe)) {
+ return false;
+ }
+ bool result = true;
+ if (overlap) {
+ if (overlap->coinPtTStart()->segment() == coinSeg) {
+ result = overlap->extend(cs, ce, os, oe);
+ } else {
+ if (os->fT > oe->fT) {
+ SkTSwap(cs, ce);
+ SkTSwap(os, oe);
+ }
+ result = overlap->extend(os, oe, cs, ce);
+ }
+#if DEBUG_COINCIDENCE_VERBOSE
+ if (result) {
+ overlaps[0]->debugShow();
+ }
+#endif
+ } else {
+ this->add(cs, ce, os, oe);
+#if DEBUG_COINCIDENCE_VERBOSE
+ fHead->debugShow();
+#endif
+ }
+ this->debugValidate();
+ return result;
}
+// Please keep this in sync with debugAddMissing()
/* detects overlaps of different coincident runs on same segment */
/* does not detect overlaps for pairs without any segments in common */
-bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
+// returns true if caller should loop again
+bool SkOpCoincidence::addMissing() {
SkCoincidentSpans* outer = fHead;
if (!outer) {
- return true;
+ return false;
}
bool added = false;
fTop = outer;
@@ -247,107 +735,116 @@ bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
// addifmissing can modify the list that this is walking
// save head so that walker can iterate over old data unperturbed
// addifmissing adds to head freely then add saved head in the end
- const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
- const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
- SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
+ while ((inner = inner->next())) {
+ this->debugValidate();
double overS, overE;
- const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
- SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
- const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
- if (outerCoin == innerCoin
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator);
- } else if (outerCoin == innerOpp
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator);
- } else if (outerOpp == innerCoin
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator);
- } else if (outerOpp == innerOpp
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator);
- } else if (outerCoin != innerCoin) {
- // check to see if outer span overlaps the inner span
- // look for inner segment in pt-t list
- // if present, and if t values are in coincident range
- // add two pairs of new coincidence
- SkOpPtT* testS = outer->fCoinPtTStart->contains(innerCoin);
- SkOpPtT* testE = outer->fCoinPtTEnd->contains(innerCoin);
- if (testS && testS->fT >= inner->fCoinPtTStart->fT
- && testE && testE->fT <= inner->fCoinPtTEnd->fT
- && this->testForCoincidence(outer, testS, testE)) {
- added |= this->addIfMissing(outer, testS, testE, allocator);
- } else {
- testS = inner->fCoinPtTStart->contains(outerCoin);
- testE = inner->fCoinPtTEnd->contains(outerCoin);
- if (testS && testS->fT >= outer->fCoinPtTStart->fT
- && testE && testE->fT <= outer->fCoinPtTEnd->fT
- && this->testForCoincidence(inner, testS, testE)) {
- added |= this->addIfMissing(inner, testS, testE, allocator);
- }
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ if (outerCoin == innerCoin) {
+ if (outerOpp != innerOpp
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd());
+ }
+ } else if (outerCoin == innerOpp) {
+ if (outerOpp != innerCoin
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd());
+ }
+ } else if (outerOpp == innerCoin) {
+ SkASSERT(outerCoin != innerOpp);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd());
+ }
+ } else if (outerOpp == innerOpp) {
+ SkASSERT(outerCoin != innerCoin);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd());
}
}
-#if 0 && DEBUG_COINCIDENCE
- SkString miss;
- miss.printf("addMissing inner=%d outer=%d", inner->debugID(), outer->debugID());
- DEBUG_COINCIDENCE_HEALTH(fDebugState->contourHead(), miss.c_str());
-#endif
+ this->debugValidate();
}
- } while ((outer = outer->fNext));
- SkCoincidentSpans** headPtr = &fHead;
- while (*headPtr) {
- SkCoincidentSpans** headNext = &(*headPtr)->fNext;
- if (*headNext) {
- break;
- }
- headPtr = headNext;
- }
- *headPtr = fTop;
+ } while ((outer = outer->next()));
+ this->restoreHead();
return added;
}
-bool SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2,
- SkOpSegment* seg2o, SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* allocator) {
- SkOpPtT* s1 = overS->find(seg1);
- SkOpPtT* e1 = overE->find(seg1);
+bool SkOpCoincidence::addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
+ const SkOpSegment* seg2, const SkOpSegment* seg2o,
+ const SkOpPtT* overS, const SkOpPtT* overE) {
+ const SkOpPtT* s1, * e1, * s2, * e2;
+ if (!(s1 = overS->find(seg1))) {
+ return true;
+ }
+ if (!(e1 = overE->find(seg1))) {
+ return true;
+ }
+ if (s1 == e1) {
+ return true;
+ }
if (approximately_equal_half(s1->fT, e1->fT)) {
return false;
}
if (!s1->starter(e1)->span()->upCast()->windValue()) {
- s1 = overS->find(seg1o);
- e1 = overE->find(seg1o);
+ if (!(s1 = overS->find(seg1o))) {
+ return true;
+ }
+ if (!(e1 = overE->find(seg1o))) {
+ return true;
+ }
+ if (s1 == e1) {
+ return true;
+ }
if (!s1->starter(e1)->span()->upCast()->windValue()) {
return true;
}
}
- SkOpPtT* s2 = overS->find(seg2);
- SkOpPtT* e2 = overE->find(seg2);
+ if (!(s2 = overS->find(seg2))) {
+ return true;
+ }
+ if (!(e2 = overE->find(seg2))) {
+ return true;
+ }
+ if (s2 == e2) {
+ return true;
+ }
if (approximately_equal_half(s2->fT, e2->fT)) {
return false;
}
if (!s2->starter(e2)->span()->upCast()->windValue()) {
- s2 = overS->find(seg2o);
- e2 = overE->find(seg2o);
+ if (!(s2 = overS->find(seg2o))) {
+ return true;
+ }
+ if (!(e2 = overE->find(seg2o))) {
+ return true;
+ }
+ if (s2 == e2) {
+ return true;
+ }
if (!s2->starter(e2)->span()->upCast()->windValue()) {
return true;
}
@@ -359,26 +856,144 @@ bool SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegm
SkTSwap(s1, e1);
SkTSwap(s2, e2);
}
- this->add(s1, e1, s2, e2, allocator);
+ this->add(s1, e1, s2, e2);
return true;
}
-bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, bool flipped) const {
- const SkCoincidentSpans* coin = fHead;
- if (!coin) {
+/* look for pairs of coincidence with no common segments
+ if there's no existing coincidence found that matches up the segments, and
+ if the pt-t list for one contains the other, create coincident pairs for what's left */
+bool SkOpCoincidence::addUncommon() {
+ SkCoincidentSpans* outer = fHead;
+ if (!outer) {
return false;
}
+ bool added = false;
+ fTop = outer;
+ fHead = nullptr;
do {
- if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd
- && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
- && coin->fFlipped == flipped) {
+ // addifmissing can modify the list that this is walking
+ // save head so that walker can iterate over old data unperturbed
+ // addifmissing adds to head freely then add saved head in the end
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
+ SkCoincidentSpans* inner = outer;
+ while ((inner = inner->next())) {
+ this->debugValidate();
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ // check to see if outer span overlaps the inner span
+ // look for inner segment in pt-t list
+ // if present, and if t values are in coincident range
+ // add two pairs of new coincidence
+ const SkOpPtT* testS = outer->coinPtTStart()->contains(innerCoin);
+ const SkOpPtT* testE = outer->coinPtTEnd()->contains(innerCoin);
+ if (testS && testS->fT >= inner->coinPtTStart()->fT
+ && testE && testE->fT <= inner->coinPtTEnd()->fT
+ && this->testForCoincidence(outer, testS, testE)) {
+ added |= this->addIfMissing(outer, testS, testE);
+ } else {
+ testS = inner->coinPtTStart()->contains(outerCoin);
+ testE = inner->coinPtTEnd()->contains(outerCoin);
+ if (testS && testS->fT >= outer->coinPtTStart()->fT
+ && testE && testE->fT <= outer->coinPtTEnd()->fT
+ && this->testForCoincidence(inner, testS, testE)) {
+ added |= this->addIfMissing(inner, testS, testE);
+ }
+ }
+ }
+ } while ((outer = outer->next()));
+ this->restoreHead();
+ return added;
+}
+
+bool SkOpCoincidence::contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const {
+ if (this->contains(fHead, seg, opp, oppT)) {
+ return true;
+ }
+ if (this->contains(fTop, seg, opp, oppT)) {
+ return true;
+ }
+ return false;
+}
+
+bool SkOpCoincidence::contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
+ const SkOpSegment* opp, double oppT) const {
+ if (!coin) {
+ return false;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == seg && coin->oppPtTStart()->segment() == opp
+ && between(coin->oppPtTStart()->fT, oppT, coin->oppPtTEnd()->fT)) {
return true;
}
- } while ((coin = coin->fNext));
+ if (coin->oppPtTStart()->segment() == seg && coin->coinPtTStart()->segment() == opp
+ && between(coin->coinPtTStart()->fT, oppT, coin->coinPtTEnd()->fT)) {
+ return true;
+ }
+ } while ((coin = coin->next()));
+ return false;
+}
+
+bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
+ const SkCoincidentSpans* test = fHead;
+ if (!test) {
+ return false;
+ }
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ SkTSwap(coinSeg, oppSeg);
+ SkTSwap(coinPtTStart, oppPtTStart);
+ SkTSwap(coinPtTEnd, oppPtTEnd);
+ if (coinPtTStart->fT > coinPtTEnd->fT) {
+ SkTSwap(coinPtTStart, coinPtTEnd);
+ SkTSwap(oppPtTStart, oppPtTEnd);
+ }
+ }
+ double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
+ double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT);
+ do {
+ if (coinSeg != test->coinPtTStart()->segment()) {
+ continue;
+ }
+ if (coinPtTStart->fT < test->coinPtTStart()->fT) {
+ continue;
+ }
+ if (coinPtTEnd->fT > test->coinPtTEnd()->fT) {
+ continue;
+ }
+ if (oppSeg != test->oppPtTStart()->segment()) {
+ continue;
+ }
+ if (oppMinT < SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
+ continue;
+ }
+ if (oppMaxT > SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
+ continue;
+ }
+ return true;
+ } while ((test = test->next()));
return false;
}
+void SkOpCoincidence::correctEnds() {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ coin->correctEnds();
+ } while ((coin = coin->next()));
+}
+
// walk span sets in parallel, moving winding from one to the other
bool SkOpCoincidence::apply() {
SkCoincidentSpans* coin = fHead;
@@ -386,18 +1001,19 @@ bool SkOpCoincidence::apply() {
return true;
}
do {
- SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
+ SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
if (start->deleted()) {
continue;
}
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
SkASSERT(start == start->starter(end));
- bool flipped = coin->fFlipped;
- SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
+ bool flipped = coin->flipped();
+ SkOpSpan* oStart = (flipped ? coin->oppPtTEndWritable()
+ : coin->oppPtTStartWritable())->span()->upCast();
if (oStart->deleted()) {
continue;
}
- SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
+ const SkOpSpanBase* oEnd = (flipped ? coin->oppPtTStart() : coin->oppPtTEnd())->span();
SkASSERT(oStart == oStart->starter(oEnd));
SkOpSegment* segment = start->segment();
SkOpSegment* oSegment = oStart->segment();
@@ -427,8 +1043,12 @@ bool SkOpCoincidence::apply() {
windDiff = -windDiff;
oWindDiff = -oWindDiff;
}
- if (windValue && (windValue > windDiff || (windValue == windDiff
- && oWindValue <= oWindDiff))) {
+ bool addToStart = windValue && (windValue > windDiff || (windValue == windDiff
+ && oWindValue <= oWindDiff));
+ if (addToStart ? start->done() : oStart->done()) {
+ addToStart ^= true;
+ }
+ if (addToStart) {
if (operandSwap) {
SkTSwap(oWindValue, oOppValue);
}
@@ -465,6 +1085,12 @@ bool SkOpCoincidence::apply() {
}
windValue = oppValue = 0;
}
+#if DEBUG_COINCIDENCE
+ SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", segment->debugID(),
+ start->debugID(), windValue, oppValue);
+ SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", oSegment->debugID(),
+ oStart->debugID(), oWindValue, oOppValue);
+#endif
start->setWindValue(windValue);
start->setOppValue(oppValue);
oStart->setWindValue(oWindValue);
@@ -490,29 +1116,87 @@ bool SkOpCoincidence::apply() {
}
oStart = oNext->upCast();
} while (true);
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return true;
}
-void SkOpCoincidence::release(SkCoincidentSpans* remove) {
- SkCoincidentSpans* coin = fHead;
+// Please keep this in sync with debugRelease()
+bool SkOpCoincidence::release(SkCoincidentSpans* coin, SkCoincidentSpans* remove) {
+ SkCoincidentSpans* head = coin;
SkCoincidentSpans* prev = nullptr;
SkCoincidentSpans* next;
do {
- next = coin->fNext;
+ next = coin->next();
if (coin == remove) {
if (prev) {
- prev->fNext = next;
- } else {
+ prev->setNext(next);
+ } else if (head == fHead) {
fHead = next;
+ } else {
+ fTop = next;
}
break;
}
prev = coin;
} while ((coin = next));
- SkASSERT(coin);
+ return coin != nullptr;
+}
+
+// Please keep this in sync with debugReorder()
+// iterate through all coincident pairs, looking for ranges greater than 1
+// if found, see if the opposite pair can match it -- which may require
+// reordering the ptT pairs
+bool SkOpCoincidence::reorder() {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return true;
+ }
+ do {
+ // most commonly, concidence are one span long; check for that first
+ int intervals = coin->spanCount();
+ if (intervals <= 0) {
+ return false;
+ }
+ if (1 == intervals) {
+#if DEBUG_COINCIDENCE_VERBOSE
+ SkASSERT(!coin->debugExpand(nullptr, nullptr));
+#endif
+ continue;
+ }
+ coin->expand(); // be all that you can be
+ if (coin->spanCount() <= 0) {
+ return false;
+ }
+ // check to see if every span in coin has a mate in opp
+ const SkOpSpan* start = coin->coinPtTStart()->span()->upCast();
+ bool flipped = coin->flipped();
+ const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span();
+ const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast();
+ SkDebugf("", start, oppStart);
+ } while ((coin = coin->next()));
+ return true;
}
+void SkOpCoincidence::restoreHead() {
+ SkCoincidentSpans** headPtr = &fHead;
+ while (*headPtr) {
+ headPtr = (*headPtr)->nextPtr();
+ }
+ *headPtr = fTop;
+ fTop = nullptr;
+ // segments may have collapsed in the meantime; remove empty referenced segments
+ headPtr = &fHead;
+ while (*headPtr) {
+ SkCoincidentSpans* test = *headPtr;
+ if (test->coinPtTStart()->segment()->done() || test->oppPtTStart()->segment()->done()) {
+ *headPtr = test->next();
+ continue;
+ }
+ headPtr = (*headPtr)->nextPtr();
+ }
+}
+
+// Please keep this in sync with debugExpand()
bool SkOpCoincidence::expand() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
@@ -520,142 +1204,132 @@ bool SkOpCoincidence::expand() {
}
bool expanded = false;
do {
- SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- SkOpSegment* segment = coin->fCoinPtTStart->segment();
- SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
- SkOpSpan* prev = start->prev();
- SkOpPtT* oppPtT;
- if (prev && (oppPtT = prev->contains(oppSegment))) {
- double midT = (prev->t() + start->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- coin->fCoinPtTStart = prev->ptT();
- coin->fOppPtTStart = oppPtT;
- expanded = true;
- }
- }
- SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
- if (next && (oppPtT = next->contains(oppSegment))) {
- double midT = (end->t() + next->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- coin->fCoinPtTEnd = next->ptT();
- coin->fOppPtTEnd = oppPtT;
- expanded = true;
- }
+ if (coin->expand()) {
+ // check to see if multiple spans expanded so they are now identical
+ SkCoincidentSpans* test = fHead;
+ do {
+ if (coin == test) {
+ continue;
+ }
+ if (coin->coinPtTStart() == test->coinPtTStart()
+ && coin->oppPtTStart() == test->oppPtTStart()) {
+ this->release(fHead, test);
+ break;
+ }
+ } while ((test = test->next()));
+ expanded = true;
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return expanded;
}
-bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const {
+bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps) const {
overlaps->fHead = overlaps->fTop = nullptr;
- SkDEBUGCODE_(overlaps->debugSetGlobalState(fDebugState));
SkCoincidentSpans* outer = fHead;
while (outer) {
- SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
- SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
+ while ((inner = inner->next())) {
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
if (outerCoin == innerCoin) {
continue; // both winners are the same segment, so there's no additional overlap
}
- SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkOpPtT* overlapS, * overlapE;
- if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overlapS, &overlapE))
- || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->fCoinPtTStart,
- outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ const SkOpPtT* overlapS;
+ const SkOpPtT* overlapE;
+ if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->oppPtTStart(),
+ outer->oppPtTEnd(),inner->coinPtTStart(), inner->coinPtTEnd(), &overlapS,
+ &overlapE))
+ || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->coinPtTStart(),
+ outer->coinPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
&overlapS, &overlapE))
- || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->fOppPtTStart,
- outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
+ || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->oppPtTStart(),
+ outer->oppPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
&overlapS, &overlapE))) {
if (!overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
- overlapS, overlapE, allocator)) {
+ overlapS, overlapE)) {
return false;
}
}
}
- outer = outer->fNext;
+ outer = outer->next();
}
return true;
}
-bool SkOpCoincidence::fixAligned() {
+// Please keep this in sync with debugRemoveCollapsed()
+bool SkOpCoincidence::removeCollapsed() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return true;
}
- do {
- if (coin->fCoinPtTStart->deleted()) {
- coin->fCoinPtTStart = coin->fCoinPtTStart->doppelganger();
- }
- if (coin->fCoinPtTEnd->deleted()) {
- coin->fCoinPtTEnd = coin->fCoinPtTEnd->doppelganger();
- }
- if (coin->fOppPtTStart->deleted()) {
- coin->fOppPtTStart = coin->fOppPtTStart->doppelganger();
- }
- if (coin->fOppPtTEnd->deleted()) {
- coin->fOppPtTEnd = coin->fOppPtTEnd->doppelganger();
- }
- } while ((coin = coin->fNext));
- coin = fHead;
SkCoincidentSpans** priorPtr = &fHead;
do {
- if (coin->fCoinPtTStart == coin->fCoinPtTEnd) {
+ if (coin->coinPtTStart() == coin->coinPtTEnd()) {
return false;
}
- if (coin->fOppPtTStart == coin->fOppPtTEnd) {
+ if (coin->oppPtTStart() == coin->oppPtTEnd()) {
return false;
}
- if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)
- || coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
- *priorPtr = coin->fNext;
+ if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) {
+ *priorPtr = coin->next();
continue;
}
- priorPtr = &coin->fNext;
- } while ((coin = coin->fNext));
+ if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) {
+ *priorPtr = coin->next();
+ continue;
+ }
+ priorPtr = coin->nextPtr();
+ } while ((coin = coin->next()));
return true;
}
-void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return;
+void SkOpCoincidence::fixUp(SkOpPtT* deleted, const SkOpPtT* kept) {
+ SkASSERT(deleted != kept);
+ if (fHead) {
+ this->fixUp(fHead, deleted, kept);
+ }
+ if (fTop) {
+ this->fixUp(fTop, deleted, kept);
}
+}
+
+void SkOpCoincidence::fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept) {
+ SkCoincidentSpans* head = coin;
do {
- if (coin->fCoinPtTStart == deleted) {
- if (coin->fCoinPtTEnd->span() == kept->span()) {
- this->release(coin);
+ if (coin->coinPtTStart() == deleted) {
+ if (coin->coinPtTEnd()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fCoinPtTStart = kept;
+ coin->setCoinPtTStart(kept);
}
- if (coin->fCoinPtTEnd == deleted) {
- if (coin->fCoinPtTStart->span() == kept->span()) {
- this->release(coin);
+ if (coin->coinPtTEnd() == deleted) {
+ if (coin->coinPtTStart()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fCoinPtTEnd = kept;
- }
- if (coin->fOppPtTStart == deleted) {
- if (coin->fOppPtTEnd->span() == kept->span()) {
- this->release(coin);
+ coin->setCoinPtTEnd(kept);
+ }
+ if (coin->oppPtTStart() == deleted) {
+ if (coin->oppPtTEnd()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fOppPtTStart = kept;
+ coin->setOppPtTStart(kept);
}
- if (coin->fOppPtTEnd == deleted) {
- if (coin->fOppPtTStart->span() == kept->span()) {
- this->release(coin);
+ if (coin->oppPtTEnd() == deleted) {
+ if (coin->oppPtTStart()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fOppPtTEnd = kept;
+ coin->setOppPtTEnd(kept);
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
}
+// Please keep this in sync with debugMark()
/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
bool SkOpCoincidence::mark() {
SkCoincidentSpans* coin = fHead;
@@ -663,44 +1337,87 @@ bool SkOpCoincidence::mark() {
return true;
}
do {
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- if (end->deleted()) {
- return false;
- }
- SkOpSpanBase* oldEnd = end;
- SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
- SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- if (oEnd->deleted()) {
- return false;
- }
- SkOpSpanBase* oOldEnd = oEnd;
- SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
- bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
+ SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
+ SkASSERT(!start->deleted());
+ SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
+ SkASSERT(!end->deleted());
+ SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
+ SkASSERT(!oStart->deleted());
+ SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
+ SkASSERT(!oEnd->deleted());
+ bool flipped = coin->flipped();
if (flipped) {
SkTSwap(oStart, oEnd);
}
+ /* coin and opp spans may not match up. Mark the ends, and then let the interior
+ get marked as many times as the spans allow */
+ start->insertCoincidence(oStart->upCast());
+ end->insertCoinEnd(oEnd);
+ const SkOpSegment* segment = start->segment();
+ const SkOpSegment* oSegment = oStart->segment();
SkOpSpanBase* next = start;
SkOpSpanBase* oNext = oStart;
- do {
- next = next->upCast()->next();
- oNext = flipped ? oNext->prev() : oNext->upCast()->next();
- if (next == end || oNext == oEnd) {
- break;
+ while ((next = next->upCast()->next()) != end) {
+ if (!next->upCast()->insertCoincidence(oSegment, flipped)) {
+ return false;
}
- if (!next->containsCoinEnd(oNext)) {
- next->insertCoinEnd(oNext);
+ }
+ while ((oNext = oNext->upCast()->next()) != oEnd) {
+ if (!oNext->upCast()->insertCoincidence(segment, flipped)) {
+ return false;
}
- SkOpSpan* nextSpan = next->upCast();
- SkOpSpan* oNextSpan = oNext->upCast();
- if (!nextSpan->containsCoincidence(oNextSpan)) {
- nextSpan->insertCoincidence(oNextSpan);
+ }
+ } while ((coin = coin->next()));
+ return true;
+}
+
+// Please keep in sync with debugMarkCollapsed()
+void SkOpCoincidence::markCollapsed(SkCoincidentSpans* coin, SkOpPtT* test) {
+ SkCoincidentSpans* head = coin;
+ while (coin) {
+ if (coin->collapsed(test)) {
+ if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
+ coin->coinPtTStartWritable()->segment()->markAllDone();
}
- } while (true);
- } while ((coin = coin->fNext));
+ if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
+ coin->oppPtTStartWritable()->segment()->markAllDone();
+ }
+ this->release(head, coin);
+ }
+ coin = coin->next();
+ }
+}
+
+// Please keep in sync with debugMarkCollapsed()
+void SkOpCoincidence::markCollapsed(SkOpPtT* test) {
+ markCollapsed(fHead, test);
+ markCollapsed(fTop, test);
+}
+
+bool SkOpCoincidence::Ordered(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg) {
+ if (coinSeg->verb() < oppSeg->verb()) {
+ return true;
+ }
+ if (coinSeg->verb() > oppSeg->verb()) {
+ return false;
+ }
+ int count = (SkPathOpsVerbToPoints(coinSeg->verb()) + 1) * 2;
+ const SkScalar* cPt = &coinSeg->pts()[0].fX;
+ const SkScalar* oPt = &oppSeg->pts()[0].fX;
+ for (int index = 0; index < count; ++index) {
+ if (*cPt < *oPt) {
+ return true;
+ }
+ if (*cPt > *oPt) {
+ return false;
+ }
+ ++cPt;
+ ++oPt;
+ }
return true;
}
-bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
+bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
SkASSERT(coin1s->segment() == coin2s->segment());
*overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
@@ -708,8 +1425,24 @@ bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
return *overS < *overE;
}
+// Commented-out lines keep this in sync with debugRelease()
+void SkOpCoincidence::release(const SkOpSegment* deleted) {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == deleted
+ || coin->coinPtTEnd()->segment() == deleted
+ || coin->oppPtTStart()->segment() == deleted
+ || coin->oppPtTEnd()->segment() == deleted) {
+ this->release(fHead, coin);
+ }
+ } while ((coin = coin->next()));
+}
+
bool SkOpCoincidence::testForCoincidence(const SkCoincidentSpans* outer, const SkOpPtT* testS,
const SkOpPtT* testE) const {
return testS->segment()->testForCoincidence(testS, testE, testS->span(),
- testE->span(), outer->fCoinPtTStart->segment(), 120000); // FIXME: replace with tuned
+ testE->span(), outer->coinPtTStart()->segment());
}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index 1efe6c0613..c64d148c24 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -7,120 +7,300 @@
#ifndef SkOpCoincidence_DEFINED
#define SkOpCoincidence_DEFINED
+#include "SkTDArray.h"
#include "SkOpTAllocator.h"
#include "SkOpSpan.h"
#include "SkPathOpsTypes.h"
class SkOpPtT;
+class SkOpSpanBase;
-struct SkCoincidentSpans {
- SkCoincidentSpans* fNext;
- SkOpPtT* fCoinPtTStart;
- SkOpPtT* fCoinPtTEnd;
- SkOpPtT* fOppPtTStart;
- SkOpPtT* fOppPtTEnd;
- bool fFlipped;
- SkDEBUGCODE(int fID);
+class SkCoincidentSpans {
+public:
+ const SkOpPtT* coinPtTEnd() const { return fCoinPtTEnd; }
+ const SkOpPtT* coinPtTStart() const { return fCoinPtTStart; }
+
+ // These return non-const pointers so that, as copies, they can be added
+ // to a new span pair
+ SkOpPtT* coinPtTEndWritable() const { return const_cast<SkOpPtT*>(fCoinPtTEnd); }
+ SkOpPtT* coinPtTStartWritable() const { return const_cast<SkOpPtT*>(fCoinPtTStart); }
+
+ bool collapsed(const SkOpPtT* ) const;
+ bool contains(const SkOpPtT* s, const SkOpPtT* e) const;
+ void correctEnds();
+ void correctOneEnd(const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
+ void (SkCoincidentSpans::* setEnd)(const SkOpPtT* ptT) );
+
+#if DEBUG_COINCIDENCE_VERBOSE
+ bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
+ void debugShow() const;
+#ifdef SK_DEBUG
+ void debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
+ const SkOpGlobalState* debugState) const;
+#endif
void dump() const;
+ bool expand();
+ bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
+ bool flipped() const { return fOppPtTStart->fT > fOppPtTEnd->fT; }
+ void init() { sk_bzero(this, sizeof(*this)); }
+ const SkOpPtT* oppPtTStart() const { return fOppPtTStart; }
+ const SkOpPtT* oppPtTEnd() const { return fOppPtTEnd; }
+ // These return non-const pointers so that, as copies, they can be added
+ // to a new span pair
+ SkOpPtT* oppPtTStartWritable() const { return const_cast<SkOpPtT*>(fOppPtTStart); }
+ SkOpPtT* oppPtTEndWritable() const { return const_cast<SkOpPtT*>(fOppPtTEnd); }
+ SkCoincidentSpans* next() { return fNext; }
+ const SkCoincidentSpans* next() const { return fNext; }
+ SkCoincidentSpans** nextPtr() { return &fNext; }
+ int spanCount() const;
+
+ void set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd
+ SkDEBUGPARAMS(int id));
+
+ void setCoinPtTEnd(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fCoinPtTStart || ptT->fT != fCoinPtTStart->fT);
+ SkASSERT(!fCoinPtTStart || fCoinPtTStart->segment() == ptT->segment());
+ fCoinPtTEnd = ptT;
+ ptT->setCoincident();
+ }
+
+ void setCoinPtTStart(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fCoinPtTEnd || ptT->fT != fCoinPtTEnd->fT);
+ SkASSERT(!fCoinPtTEnd || fCoinPtTEnd->segment() == ptT->segment());
+ fCoinPtTStart = ptT;
+ ptT->setCoincident();
+ }
+
+ void setEnds(const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTEnd) {
+ this->setCoinPtTEnd(coinPtTEnd);
+ this->setOppPtTEnd(oppPtTEnd);
+ }
+
+ void setOppPtTEnd(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fOppPtTStart || ptT->fT != fOppPtTStart->fT);
+ SkASSERT(!fOppPtTStart || fOppPtTStart->segment() == ptT->segment());
+ fOppPtTEnd = ptT;
+ ptT->setCoincident();
+ }
+
+ void setOppPtTStart(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fOppPtTEnd || ptT->fT != fOppPtTEnd->fT);
+ SkASSERT(!fOppPtTEnd || fOppPtTEnd->segment() == ptT->segment());
+ fOppPtTStart = ptT;
+ ptT->setCoincident();
+ }
+
+ void setStarts(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
+ this->setCoinPtTStart(coinPtTStart);
+ this->setOppPtTStart(oppPtTStart);
+ }
+
+ void setNext(SkCoincidentSpans* next) { fNext = next; }
+
+ bool startEquals(const SkOpSpanBase* outer, const SkOpSpanBase* over) const {
+ return fCoinPtTStart->span() == over && fOppPtTStart->span() == outer;
+ }
+private:
+ SkCoincidentSpans* fNext;
+ const SkOpPtT* fCoinPtTStart;
+ const SkOpPtT* fCoinPtTEnd;
+ const SkOpPtT* fOppPtTStart;
+ const SkOpPtT* fOppPtTEnd;
+ SkDEBUGCODE(int fID);
};
class SkOpCoincidence {
public:
- SkOpCoincidence()
+ SkOpCoincidence(SkOpGlobalState* globalState)
: fHead(nullptr)
, fTop(nullptr)
- SkDEBUGPARAMS(fDebugState(nullptr))
- {
+ , fGlobalState(globalState)
+ , fContinue(false)
+ , fSpanDeleted(false)
+ , fPtAllocated(false)
+ , fCoinExtended(false)
+ , fSpanMerged(false) {
+ globalState->setCoincidence(this);
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
- bool addExpanded(SkChunkAlloc* allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* ));
- bool addMissing(SkChunkAlloc* allocator);
+ SkOpPtT* oppPtTEnd);
+ bool addEndMovedSpans();
+ bool addExpanded();
+ bool addMissing();
+ bool addUncommon();
bool apply();
bool contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, bool flipped) const;
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
+ void correctEnds();
+#if DEBUG_COINCIDENCE_VERBOSE
void debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* ) const;
void debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
const SkOpAngle* debugAngle(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugAngle(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugAngle(id), nullptr);
}
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+ void debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
+
SkOpContour* debugContour(int id) {
- return SkDEBUGRELEASE(fDebugState->debugContour(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugContour(id), nullptr);
}
+#if DEBUG_COINCIDENCE_VERBOSE
bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* ) const;
void debugMark(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkCoincidentSpans* coin, const SkOpPtT* test) const;
+ void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpPtT* test) const;
+#endif
const SkOpPtT* debugPtT(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugPtT(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugPtT(id), nullptr);
}
const SkOpSegment* debugSegment(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugSegment(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugSegment(id), nullptr);
}
- void debugSetGlobalState(SkOpGlobalState* debugState) {
- SkDEBUGCODE(fDebugState = debugState);
- }
-
- void debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugReorder(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugRelease(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSegment* ) const;
+#endif
void debugShowCoincidence() const;
const SkOpSpanBase* debugSpan(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugSpan(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugSpan(id), nullptr);
}
- void release(SkCoincidentSpans* );
+ void debugValidate() const;
void dump() const;
+ bool edge(const SkOpPtT* , bool* start) const;
bool expand();
- bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd);
- bool findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
- bool fixAligned();
- void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
+ bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
+ const SkOpPtT* oppPtTEnd);
+ bool findOverlaps(SkOpCoincidence* ) const;
+ void fixUp(SkOpPtT* deleted, const SkOpPtT* kept);
+
+ SkOpGlobalState* globalState() {
+ return fGlobalState;
+ }
bool isEmpty() const {
- return !fHead;
+ return !fHead && !fTop;
}
bool mark();
+ void markCollapsed(SkOpPtT* );
+
+ static bool Ordered(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
+ return Ordered(coinPtTStart->segment(), oppPtTStart->segment());
+ }
+
+ static bool Ordered(const SkOpSegment* coin, const SkOpSegment* opp);
+ void release(const SkOpSegment* );
+ bool removeCollapsed();
+ bool reorder();
private:
- bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e,
- SkChunkAlloc* );
+ void add(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
+ const SkOpPtT* oppPtTEnd) {
+ this->add(const_cast<SkOpPtT*>(coinPtTStart), const_cast<SkOpPtT*>(coinPtTEnd),
+ const_cast<SkOpPtT*>(oppPtTStart), const_cast<SkOpPtT*>(oppPtTEnd));
+ }
+
+ void addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan);
+ bool addEndMovedSpans(const SkOpPtT* ptT);
+
+ bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e);
+
+ bool addIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e) {
+ return addIfMissing(outer, const_cast<SkOpPtT*>(over1s), const_cast<SkOpPtT*>(over1e));
+ }
+
bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e,
double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
- SkChunkAlloc* );
- bool addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o,
- SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* );
- bool debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
- const SkOpPtT* over1e) const;
- bool debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
+
+ bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ const SkOpPtT* over2s, const SkOpPtT* over2e,
+ double tStart, double tEnd,
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ return addIfMissing(over1s, over1e, over2s, over2e, tStart, tEnd,
+ const_cast<SkOpPtT*>(coinPtTStart), coinPtTEnd,
+ const_cast<SkOpPtT*>(oppPtTStart), oppPtTEnd);
+ }
+
+ bool addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe);
+ bool addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
+ const SkOpSegment* seg2, const SkOpSegment* seg2o,
+ const SkOpPtT* overS, const SkOpPtT* overE);
+ bool alreadyAdded(const SkCoincidentSpans* check, const SkCoincidentSpans* outer,
+ const SkOpPtT* over1s, const SkOpPtT* over1e) const;
+ bool checkOverlap(SkCoincidentSpans* check,
+ const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ SkTDArray<SkCoincidentSpans*>* overlaps) const;
+ bool contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const;
+ bool contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
+ const SkOpSegment* opp, double oppT) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog*) const;
+ void debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e,
double tStart, double tEnd,
- SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
+ const char* id, SkPathOpsDebug::GlitchLog*) const;
+#endif
+ void fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept);
+ void markCollapsed(SkCoincidentSpans* head, SkOpPtT* test);
bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
double* overS, double* overE) const;
-
+ bool release(SkCoincidentSpans* coin, SkCoincidentSpans* );
+ void restoreHead();
bool testForCoincidence(const SkCoincidentSpans* outer, const SkOpPtT* testS,
const SkOpPtT* testE) const;
+ static void TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
+ double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ double* coinTs, double* coinTe);
+
SkCoincidentSpans* fHead;
SkCoincidentSpans* fTop;
- SkDEBUGCODE_(SkOpGlobalState* fDebugState);
+ SkOpGlobalState* fGlobalState;
+ bool fContinue;
+ bool fSpanDeleted;
+ bool fPtAllocated;
+ bool fCoinExtended;
+ bool fSpanMerged;
};
#endif
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index df65437c97..ed533887e5 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -10,18 +10,18 @@
#include "SkReduceOrder.h"
#include "SkTSort.h"
-SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
- SkChunkAlloc* allocator) {
+SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
+ SkChunkAlloc* allocator = this->globalState()->allocator();
switch (verb) {
case SkPath::kLine_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
- return appendSegment(allocator).addLine(ptStorage, this);
+ return appendSegment().addLine(ptStorage, this);
} break;
case SkPath::kQuad_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
- return appendSegment(allocator).addQuad(ptStorage, this);
+ return appendSegment().addQuad(ptStorage, this);
} break;
case SkPath::kConic_Verb: {
SkASSERT(0); // the original curve is a cubic, which will never reduce to a conic
@@ -29,7 +29,7 @@ SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
case SkPath::kCubic_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
- return appendSegment(allocator).addCubic(ptStorage, this);
+ return appendSegment().addCubic(ptStorage, this);
} break;
default:
SkASSERT(0);
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index b9f0b85af5..0db6eabdfe 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -11,7 +11,6 @@
#include "SkTDArray.h"
#include "SkTSort.h"
-class SkChunkAlloc;
enum class SkOpRayDir;
struct SkOpRayHit;
class SkPathWriter;
@@ -30,47 +29,31 @@ public:
bool operator<(const SkOpContour& rh) const {
return fBounds.fTop == rh.fBounds.fTop
- ? fBounds.fLeft < rh.fBounds.fLeft
- : fBounds.fTop < rh.fBounds.fTop;
+ ? fBounds.fLeft < rh.fBounds.fLeft
+ : fBounds.fTop < rh.fBounds.fTop;
}
- void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->addAlignIntersections(contourList, allocator);
- } while ((segment = segment->next()));
- }
-
- void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) {
- appendSegment(allocator).addConic(pts, weight, this);
+ void addConic(SkPoint pts[3], SkScalar weight) {
+ appendSegment().addConic(pts, weight, this);
}
- void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
- appendSegment(allocator).addCubic(pts, this);
+ void addCubic(SkPoint pts[4]) {
+ appendSegment().addCubic(pts, this);
}
- SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
+ SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4]);
- void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
- appendSegment(allocator).addLine(pts, this);
+ SkOpSegment* addLine(SkPoint pts[2]) {
+ return appendSegment().addLine(pts, this);
}
- void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
- appendSegment(allocator).addQuad(pts, this);
- }
-
- void align() {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->align();
- } while ((segment = segment->next()));
+ void addQuad(SkPoint pts[3]) {
+ appendSegment().addQuad(pts, this);
}
- SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
+ SkOpSegment& appendSegment() {
SkOpSegment* result = fCount++
- ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
+ ? SkOpTAllocator<SkOpSegment>::Allocate(this->globalState()->allocator()) : &fHead;
result->setPrev(fTail);
if (fTail) {
fTail->setNext(result);
@@ -79,8 +62,8 @@ public:
return *result;
}
- SkOpContour* appendContour(SkChunkAlloc* allocator) {
- SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+ SkOpContour* appendContour() {
+ SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
contour->setNext(nullptr);
SkOpContour* prev = this;
SkOpContour* next;
@@ -90,16 +73,16 @@ public:
prev->setNext(contour);
return contour;
}
-
+
const SkPathOpsBounds& bounds() const {
return fBounds;
}
- void calcAngles(SkChunkAlloc* allocator) {
+ void calcAngles() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
- segment->calcAngles(allocator);
+ segment->calcAngles();
} while ((segment = segment->next()));
}
@@ -132,14 +115,21 @@ public:
return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr);
}
+ const SkOpCoincidence* debugCoincidence() const {
+ return this->globalState()->coincidence();
+ }
+
+#if DEBUG_COINCIDENCE_VERBOSE
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#endif
SkOpContour* debugContour(int id) {
return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr);
}
- void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidence) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
const SkOpPtT* debugPtT(int id) const {
return SkDEBUGRELEASE(this->globalState()->debugPtT(id), nullptr);
@@ -154,7 +144,7 @@ public:
}
SkOpGlobalState* globalState() const {
- return fState;
+ return fState;
}
void debugValidate() const {
@@ -197,15 +187,6 @@ public:
return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
}
- bool findCollapsed() {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->findCollapsed();
- } while ((segment = segment->next()));
- return true;
- }
-
SkOpSpan* findSortableTop(SkOpContour* );
SkOpSegment* first() {
@@ -237,14 +218,15 @@ public:
return fXor;
}
- void markDone() {
+ void markAllDone() {
SkOpSegment* segment = &fHead;
do {
segment->markAllDone();
} while ((segment = segment->next()));
}
- bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ // Please keep this aligned with debugMissingCoincidence()
+ bool missingCoincidence() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
bool result = false;
@@ -253,7 +235,7 @@ public:
#if DEBUG_ANGLE
segment->debugCheckAngleCoin();
#endif
- } else if (segment->missingCoincidence(coincidences, allocator)) {
+ } else if (segment->missingCoincidence()) {
result = true;
// FIXME: trying again loops forever in issue3651_6
// The continue below is speculative -- once there's an actual case that requires it,
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 617ca76c82..95152a7ffa 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -36,9 +36,9 @@ int SkOpEdgeBuilder::count() const {
return count;
}
-bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
+bool SkOpEdgeBuilder::finish() {
fOperand = false;
- if (fUnparseable || !walk(allocator)) {
+ if (fUnparseable || !walk()) {
return false;
}
complete();
@@ -162,7 +162,7 @@ bool SkOpEdgeBuilder::close() {
return true;
}
-bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
+bool SkOpEdgeBuilder::walk() {
uint8_t* verbPtr = fPathVerbs.begin();
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
SkPoint* pointsPtr = fPathPts.begin() - 1;
@@ -183,20 +183,20 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
}
}
if (!fCurrentContour) {
- fCurrentContour = fContoursHead->appendContour(allocator);
+ fCurrentContour = fContoursHead->appendContour();
}
fCurrentContour->init(fGlobalState, fOperand,
fXorMask[fOperand] == kEvenOdd_PathOpsMask);
pointsPtr += 1;
continue;
case SkPath::kLine_Verb:
- fCurrentContour->addLine(pointsPtr, fAllocator);
+ fCurrentContour->addLine(pointsPtr);
break;
case SkPath::kQuad_Verb:
- fCurrentContour->addQuad(pointsPtr, fAllocator);
+ fCurrentContour->addQuad(pointsPtr);
break;
case SkPath::kConic_Verb:
- fCurrentContour->addConic(pointsPtr, *weightPtr++, fAllocator);
+ fCurrentContour->addConic(pointsPtr, *weightPtr++);
break;
case SkPath::kCubic_Verb: {
// Split complex cubics (such as self-intersecting curves or
@@ -221,13 +221,13 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) {
force_small_to_zero(&curve2[index]);
}
- fCurrentContour->addCurve(v1, curve1, fAllocator);
- fCurrentContour->addCurve(v2, curve2, fAllocator);
+ fCurrentContour->addCurve(v1, curve1);
+ fCurrentContour->addCurve(v2, curve2);
} else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
+ fCurrentContour->addCubic(pointsPtr);
}
} else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
+ fCurrentContour->addCubic(pointsPtr);
}
} break;
case SkPath::kClose_Verb:
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index f11e0e4c89..1a78a2137e 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -12,20 +12,16 @@
class SkOpEdgeBuilder {
public:
- SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator) // FIXME: replace with const, tune this
- , fGlobalState(globalState)
+ SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ : fGlobalState(globalState)
, fPath(path.nativePath())
, fContoursHead(contours2)
, fAllowOpenContours(true) {
init();
}
- SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator)
- , fGlobalState(globalState)
+ SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ : fGlobalState(globalState)
, fPath(&path)
, fContoursHead(contours2)
, fAllowOpenContours(false) {
@@ -42,7 +38,7 @@ public:
}
int count() const;
- bool finish(SkChunkAlloc* );
+ bool finish();
const SkOpContour* head() const {
return fContoursHead;
@@ -56,9 +52,8 @@ private:
void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
bool close();
int preFetch();
- bool walk(SkChunkAlloc* );
+ bool walk();
- SkChunkAlloc* fAllocator;
SkOpGlobalState* fGlobalState;
const SkPath* fPath;
SkTDArray<SkPoint> fPathPts;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 41d62369b6..e4e00bbfab 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -9,6 +9,8 @@
#include "SkOpSegment.h"
#include "SkPathWriter.h"
+#define FAIL_IF(cond) do { if (cond) return false; } while (false)
+
/*
After computing raw intersections, post process all segments to:
- find small collections of points that can be collapsed to a single point
@@ -159,90 +161,10 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
return result;
}
-void SkOpSegment::addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
- SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- const SkPoint& newPt = endPtT.fPt;
- if (newPt == oldPt) {
- return;
- }
- SkPoint line[2] = { newPt, oldPt };
- SkPathOpsBounds lineBounds;
- lineBounds.setBounds(line, 2);
- SkDLine aLine;
- aLine.set(line);
- SkOpContour* current = contourList;
- do {
- if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
- continue;
- }
- SkOpSegment* segment = current->first();
- do {
- if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
- continue;
- }
- if (newPt == segment->fPts[0]) {
- continue;
- }
- if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (oldPt == segment->fPts[0]) {
- continue;
- }
- if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (endPtT.contains(segment)) {
- continue;
- }
- SkIntersections i;
- switch (segment->fVerb) {
- case SkPath::kLine_Verb: {
- SkDLine bLine;
- bLine.set(segment->fPts);
- i.intersect(bLine, aLine);
- } break;
- case SkPath::kQuad_Verb: {
- SkDQuad bQuad;
- bQuad.set(segment->fPts);
- i.intersect(bQuad, aLine);
- } break;
- case SkPath::kConic_Verb: {
- SkDConic bConic;
- bConic.set(segment->fPts, segment->fWeight);
- i.intersect(bConic, aLine);
- } break;
- case SkPath::kCubic_Verb: {
- SkDCubic bCubic;
- bCubic.set(segment->fPts);
- i.intersect(bCubic, aLine);
- } break;
- default:
- SkASSERT(0);
- }
- if (i.used()) {
- SkASSERT(i.used() == 1);
- SkASSERT(!zero_or_one(i[0][0]));
- SkOpSpanBase* checkSpan = fHead.next();
- while (!checkSpan->final()) {
- if (checkSpan->contains(segment)) {
- goto nextSegment;
- }
- checkSpan = checkSpan->upCast()->next();
- }
- SkOpPtT* ptT = segment->addT(i[0][0], SkOpSegment::kAllowAlias, allocator);
- ptT->fPt = newPt;
- endPtT.addOpp(ptT);
- }
- nextSegment:
- ;
- } while ((segment = segment->next()));
- } while ((current = current->next()));
-}
-
bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathWriter* path) const {
if (start->starter(end)->alreadyAdded()) {
+ SkDEBUGF(("same curve added twice aborted pathops\n"));
return false;
}
SkOpCurve edge;
@@ -298,29 +220,57 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
return true;
}
-SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
- SkOpSpanBase* existing = nullptr;
- SkOpSpanBase* test = &fHead;
- double testT;
+const SkOpPtT* SkOpSegment::existing(double t, const SkOpSegment* opp) const {
+ const SkOpSpanBase* test = &fHead;
+ const SkOpPtT* testPtT;
+ SkPoint pt = this->ptAtT(t);
do {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
- }
+ testPtT = test->ptT();
+ if (testPtT->fT == t) {
break;
}
+ if (!this->match(testPtT, this, t, pt, opp ? kAllowAliasMatch : kNoAliasMatch)) {
+ if (t < testPtT->fT) {
+ return nullptr;
+ }
+ continue;
+ }
+ if (!opp) {
+ return testPtT;
+ }
+ const SkOpPtT* loop = testPtT->next();
+ while (loop != testPtT) {
+ if (loop->segment() == this && loop->fT == t && loop->fPt == pt) {
+ goto foundMatch;
+ }
+ loop = loop->next();
+ }
+ return nullptr;
} while ((test = test->upCast()->next()));
- SkOpPtT* result;
- if (existing && existing->contains(opp)) {
- result = existing->ptT();
- } else {
- result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+foundMatch:
+ return opp && !test->contains(opp) ? nullptr : testPtT;
+}
+
+// break the span so that the coincident part does not change the angle of the remainder
+bool SkOpSegment::addExpanded(double newT, const SkOpSpanBase* test, bool* startOver) {
+ if (this->contains(newT)) {
+ return true;
}
- SkASSERT(result);
- return result;
+ SkOpPtT* newPtT = this->addT(newT, kAllowAliasMatch, startOver);
+ if (!newPtT) {
+ return false;
+ }
+ newPtT->fPt = this->ptAtT(newT);
+ // const cast away to change linked list; pt/t values stays unchanged
+ SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test);
+ if (writableTest->ptT()->addOpp(newPtT)) {
+ writableTest->checkForCollapsedCoincidence();
+ }
+ return true;
}
-SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+// Please keep this in sync with debugAddT()
+SkOpPtT* SkOpSegment::addT(double t, AliasMatch allowAlias, bool* allocated) {
debugValidate();
SkPoint pt = this->ptAtT(t);
SkOpSpanBase* span = &fHead;
@@ -331,9 +281,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
if (t == result->fT) {
goto bumpSpan;
}
- if (this->match(result, this, t, pt)) {
+ if (this->match(result, this, t, pt, allowAlias)) {
// see if any existing alias matches segment, pt, and t
- loop = result->next();
+ loop = result->next();
duplicatePt = false;
while (loop != result) {
bool ptMatch = loop->fPt == pt;
@@ -343,12 +293,12 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
duplicatePt |= ptMatch;
loop = loop->next();
}
- if (kNoAlias == allowAlias) {
+ if (kNoAliasMatch == allowAlias) {
bumpSpan:
span->bumpSpanAdds();
return result;
}
- SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
+ SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(this->globalState()->allocator());
alias->init(result->span(), t, pt, duplicatePt);
result->insert(alias);
result->span()->unaligned();
@@ -358,6 +308,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
alias->segment()->debugID(), alias->span()->debugID());
#endif
span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
+ }
return alias;
}
if (t < result->fT) {
@@ -365,7 +318,7 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
if (!prev) {
return nullptr;
}
- SkOpSpan* span = insert(prev, allocator);
+ SkOpSpan* span = insert(prev);
span->init(this, prev, t, pt);
this->debugValidate();
#if DEBUG_ADD_T
@@ -373,6 +326,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
span->segment()->debugID(), span->debugID());
#endif
span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
+ }
return span->ptT();
}
SkASSERT(span != &fTail);
@@ -381,43 +337,17 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
return nullptr;
}
-// choose a solitary t and pt value; remove aliases; align the opposite ends
-void SkOpSegment::align() {
- debugValidate();
- SkOpSpanBase* span = &fHead;
- if (!span->aligned()) {
- span->alignEnd(0, fPts[0]);
- }
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
- break;
- }
- span->align();
- }
- if (!span->aligned()) {
- span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- }
- if (this->collapsed()) {
- SkOpSpan* span = &fHead;
- do {
- span->setWindValue(0);
- span->setOppValue(0);
- this->markDone(span);
- } while ((span = span->next()->upCastable()));
- }
- debugValidate();
-}
-
-void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+void SkOpSegment::calcAngles() {
bool activePrior = !fHead.isCanceled();
if (activePrior && !fHead.simple()) {
- addStartSpan(allocator);
+ addStartSpan();
}
SkOpSpan* prior = &fHead;
SkOpSpanBase* spanBase = fHead.next();
while (spanBase != &fTail) {
if (activePrior) {
- SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(
+ this->globalState()->allocator());
priorAngle->set(spanBase, prior);
spanBase->setFromAngle(priorAngle);
}
@@ -425,7 +355,8 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
bool active = !span->isCanceled();
SkOpSpanBase* next = span->next();
if (active) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(
+ this->globalState()->allocator());
angle->set(span, next);
span->setToAngle(angle);
}
@@ -434,12 +365,32 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
spanBase = next;
}
if (activePrior && !fTail.simple()) {
- addEndSpan(allocator);
+ addEndSpan();
}
}
+// Please keep this in sync with debugClearAll()
+void SkOpSegment::clearAll() {
+ SkOpSpan* span = &fHead;
+ do {
+ this->clearOne(span);
+ } while ((span = span->next()->upCastable()));
+ this->globalState()->coincidence()->release(this);
+}
+
+// Please keep this in sync with debugClearOne()
+void SkOpSegment::clearOne(SkOpSpan* span) {
+ span->setWindValue(0);
+ span->setOppValue(0);
+ this->markDone(span);
+}
+
+// Quads and conics collapse if the end points are the same, because
+// the curve doesn't enclose an area.
bool SkOpSegment::collapsed() const {
- return fVerb == SkPath::kLine_Verb && fHead.pt() == fTail.pt();
+ // FIXME: cubics can have also collapsed -- need to check if the
+ // control points are on a line with the end points
+ return fVerb < SkPath::kCubic_Verb && fHead.pt() == fTail.pt();
}
void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
@@ -571,12 +522,26 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
return start->starter(end)->windSum();
}
+bool SkOpSegment::contains(double newT) const {
+ const SkOpSpanBase* spanBase = &fHead;
+ do {
+ if (spanBase->ptT()->contains(this, newT)) {
+ return true;
+ }
+ if (spanBase == &fTail) {
+ break;
+ }
+ spanBase = spanBase->upCast()->next();
+ } while (true);
+ return false;
+}
+
void SkOpSegment::release(const SkOpSpan* span) {
if (span->done()) {
--fDoneCount;
}
--fCount;
- SkASSERT(fCount >= fDoneCount);
+ SkASSERT(this->globalState()->debugSkipAssert() || fCount >= fDoneCount);
}
double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
@@ -601,15 +566,6 @@ double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
return closestDistSq;
}
-void SkOpSegment::findCollapsed() {
- if (fHead.contains(&fTail)) {
- markAllDone();
- // move start and end to the same point
- fHead.alignEnd(0, fHead.pt());
- fTail.setAligned();
- }
-}
-
/*
The M and S variable name parts stand for the operators.
Mi stands for Minuend (see wiki subtraction, analogous to difference)
@@ -887,14 +843,12 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
}
SkOpGlobalState* SkOpSegment::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
fContour = contour;
fNext = nullptr;
- fOriginal[0] = pts[0];
- fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)];
fPts = pts;
fWeight = weight;
fVerb = verb;
@@ -1095,15 +1049,17 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
}
bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
- const SkPoint& testPt) const {
- const SkOpSegment* baseParent = base->segment();
- if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
- return true;
+ const SkPoint& testPt, AliasMatch aliasMatch) const {
+ SkASSERT(this == base->segment());
+ if (this == testParent) {
+ if (precisely_equal(base->fT, testT)) {
+ return true;
+ }
}
if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
return false;
}
- return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
+ return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
}
static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
@@ -1178,7 +1134,8 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS
return other;
}
-static void clear_visited(SkOpSpanBase* span) {
+// Please keep this in sync with DebugClearVisited()
+void SkOpSegment::ClearVisited(SkOpSpanBase* span) {
// reset visited flag back to false
do {
SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
@@ -1189,20 +1146,23 @@ static void clear_visited(SkOpSpanBase* span) {
} while (!span->final() && (span = span->upCast()->next()));
}
+// Please keep this in sync with debugMissingCoincidence()
// look for pairs of undetected coincident curves
// assumes that segments going in have visited flag clear
-// curve/curve intersection should now do a pretty good job of finding coincident runs so
-// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
-// the opp is not a line
-bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- if (this->verb() != SkPath::kLine_Verb) {
- return false;
- }
+// Even though pairs of curves correct detect coincident runs, a run may be missed
+// if the coincidence is a product of multiple intersections. For instance, given
+// curves A, B, and C:
+// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
+// the end of C that the intersection is replaced with the end of C.
+// Even though A-B correctly do not detect an intersection at point 2,
+// the resulting run from point 1 to point 2 is coincident on A and B.
+bool SkOpSegment::missingCoincidence() {
if (this->done()) {
return false;
}
SkOpSpan* prior = nullptr;
SkOpSpanBase* spanBase = &fHead;
+ bool result = false;
do {
SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase);
@@ -1211,9 +1171,6 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
continue;
}
SkOpSegment* opp = ptT->span()->segment();
-// if (opp->verb() == SkPath::kLine_Verb) {
-// continue;
-// }
if (opp->done()) {
continue;
}
@@ -1224,18 +1181,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
if (spanBase == &fHead) {
continue;
}
+ if (ptT->segment() == this) {
+ continue;
+ }
SkOpSpan* span = spanBase->upCastable();
// FIXME?: this assumes that if the opposite segment is coincident then no more
// coincidence needs to be detected. This may not be true.
- if (span && span->containsCoincidence(opp)) {
- continue;
- }
- if (spanBase->segment() == opp) {
+ if (span && span->containsCoincidence(opp)) {
continue;
}
if (spanBase->containsCoinEnd(opp)) {
continue;
- }
+ }
SkOpPtT* priorPtT = nullptr, * priorStopPtT;
// find prior span containing opp segment
SkOpSegment* priorOpp = nullptr;
@@ -1268,28 +1225,28 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
SkTSwap(priorPtT, ptT);
SkTSwap(oppStart, oppEnd);
}
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident = false;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+ SkOpCoincidence* coincidences = this->globalState()->coincidence();
+ SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
+ SkOpPtT* rootPtT = ptT->span()->ptT();
+ SkOpPtT* rootOppStart = oppStart->span()->ptT();
+ SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
+ if (coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
goto swapBack;
}
- if (opp->verb() == SkPath::kLine_Verb) {
- coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
- (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
- }
- if (!coincident) {
- coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
- }
- if (coincident) {
+ if (this->testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
// mark coincidence
- if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd)
- && !coincidences->extend(oppStart, oppEnd, priorPtT, ptT)) {
- coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
+#if DEBUG_COINCIDENCE_VERBOSE
+ SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
+ rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
+ rootOppEnd->debugID());
+#endif
+ if (!coincidences->extend(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
+ coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
}
- clear_visited(&fHead);
- return true;
+#if DEBUG_COINCIDENCE
+ SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd));
+#endif
+ result = true;
}
swapBack:
if (swapped) {
@@ -1297,19 +1254,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
- clear_visited(&fHead);
- return false;
+ ClearVisited(&fHead);
+ return result;
}
+// please keep this in sync with debugMoveMultiples()
// if a span has more than one intersection, merge the other segments' span as needed
bool SkOpSegment::moveMultiples() {
debugValidate();
SkOpSpanBase* test = &fHead;
do {
int addCount = test->spanAddsCount();
- if (addCount < 1) {
- return false;
- }
+ FAIL_IF(addCount < 1);
if (addCount == 1) {
continue;
}
@@ -1392,66 +1348,126 @@ bool SkOpSegment::moveMultiples() {
oppSegment->debugValidate();
goto checkNextSpan;
}
- tryNextSpan:
+ tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
-checkNextSpan:
+checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
debugValidate();
return true;
}
+// adjacent spans may have points close by
+bool SkOpSegment::spansNearby(const SkOpSpanBase* refSpan, const SkOpSpanBase* checkSpan) const {
+ const SkOpPtT* refHead = refSpan->ptT();
+ const SkOpPtT* checkHead = checkSpan->ptT();
+// if the first pt pair from adjacent spans are far apart, assume that all are far enough apart
+ if (!SkDPoint::RoughlyEqual(refHead->fPt, checkHead->fPt)) {
+#if DEBUG_COINCIDENCE
+ // verify that no combination of points are close
+ const SkOpPtT* dBugRef = refHead;
+ do {
+ const SkOpPtT* dBugCheck = checkHead;
+ do {
+ SkASSERT(!SkDPoint::ApproximatelyEqual(dBugRef->fPt, dBugCheck->fPt));
+ dBugCheck = dBugCheck->next();
+ } while (dBugCheck != checkHead);
+ dBugRef = dBugRef->next();
+ } while (dBugRef != refHead);
+#endif
+ return false;
+ }
+ // check only unique points
+ SkScalar distSqBest = SK_ScalarMax;
+ const SkOpPtT* refBest = nullptr;
+ const SkOpPtT* checkBest = nullptr;
+ const SkOpPtT* ref = refHead;
+ do {
+ if (ref->deleted()) {
+ continue;
+ }
+ while (ref->ptAlreadySeen(refHead)) {
+ ref = ref->next();
+ if (ref == refHead) {
+ goto doneCheckingDistance;
+ }
+ }
+ const SkOpPtT* check = checkHead;
+ const SkOpSegment* refSeg = ref->segment();
+ do {
+ if (check->deleted()) {
+ continue;
+ }
+ while (check->ptAlreadySeen(checkHead)) {
+ check = check->next();
+ if (check == checkHead) {
+ goto nextRef;
+ }
+ }
+ SkScalar distSq = ref->fPt.distanceToSqd(check->fPt);
+ if (distSqBest > distSq && (refSeg != check->segment()
+ || !refSeg->ptsDisjoint(*ref, *check))) {
+ distSqBest = distSq;
+ refBest = ref;
+ checkBest = check;
+ }
+ } while ((check = check->next()) != checkHead);
+nextRef:
+ ;
+ } while ((ref = ref->next()) != refHead);
+doneCheckingDistance:
+ return checkBest && refBest->segment()->match(refBest, checkBest->segment(), checkBest->fT,
+ checkBest->fPt, kAllowAliasMatch);
+}
+
+// Please keep this function in sync with debugMoveNearby()
// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
void SkOpSegment::moveNearby() {
debugValidate();
- SkOpSpanBase* spanS = &fHead;
+ // release undeleted spans pointing to this seg that are linked to the primary span
+ SkOpSpanBase* spanBase = &fHead;
do {
- SkOpSpanBase* test = spanS->upCast()->next();
- SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- test->upCast()->release(spanS->ptT());
- continue;
- } else if (spanS != &fHead) {
- spanS->upCast()->release(test->ptT());
- spanS = test;
- continue;
+ SkOpPtT* ptT = spanBase->ptT();
+ const SkOpPtT* headPtT = ptT;
+ while ((ptT = ptT->next()) != headPtT) {
+ SkOpSpanBase* test = ptT->span();
+ if (ptT->segment() == this && !ptT->deleted() && test != spanBase
+ && test->ptT() == ptT) {
+ if (test->final()) {
+ if (spanBase == &fHead) {
+ this->clearAll();
+ return;
+ }
+ spanBase->upCast()->release(ptT);
+ } else if (test->prev()) {
+ test->upCast()->release(headPtT);
+ }
+ break;
}
}
- do { // iterate through all spans associated with start
- SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? nullptr : test->upCast()->next();
- do {
- SkOpPtT* testBase = test->ptT();
- do {
- if (startBase == testBase) {
- goto checkNextSpan;
- }
- if (testBase->duplicate()) {
- continue;
- }
- if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
- if (test == &this->fTail) {
- if (spanS == &fHead) {
- debugValidate();
- return; // if this span has collapsed, remove it from parent
- }
- this->fTail.merge(spanS->upCast());
- debugValidate();
- return;
- }
- spanS->merge(test->upCast());
- goto checkNextSpan;
- }
- } while ((testBase = testBase->next()) != test->ptT());
- } while ((startBase = startBase->next()) != spanS->ptT());
- checkNextSpan:
- ;
- } while ((test = next));
- spanS = spanS->upCast()->next();
- } while (!spanS->final());
+ spanBase = spanBase->upCast()->next();
+ } while (!spanBase->final());
+
+ // This loop looks for adjacent spans which are near by
+ spanBase = &fHead;
+ do { // iterate through all spans associated with start
+ SkOpSpanBase* test = spanBase->upCast()->next();
+ if (this->spansNearby(spanBase, test)) {
+ if (test->final()) {
+ if (spanBase->prev()) {
+ test->merge(spanBase->upCast());
+ } else {
+ this->clearAll();
+ return;
+ }
+ } else {
+ spanBase->merge(test->upCast());
+ }
+ }
+ spanBase = test;
+ } while (!spanBase->final());
debugValidate();
}
@@ -1679,8 +1695,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
}
bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT,
- const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp,
- SkScalar flatnessLimit) const {
+ const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp) const {
// average t, find mid pt
double midT = (prior->t() + spanBase->t()) / 2;
SkPoint midPt = this->ptAtT(midT);
@@ -1688,22 +1703,28 @@ bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT
// if the mid pt is not near either end pt, project perpendicular through opp seg
if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
&& !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
+ if (priorPtT->span() == ptT->span()) {
+ return false;
+ }
coincident = false;
SkIntersections i;
- SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
- SkDLine ray = {{{midPt.fX, midPt.fY},
- {(double) midPt.fX + dxdy.fY, (double) midPt.fY - dxdy.fX}}};
- (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
+ SkDCurve curvePart;
+ this->subDivide(prior, spanBase, &curvePart);
+ SkDVector dxdy = (*CurveDDSlopeAtT[fVerb])(curvePart, 0.5f);
+ SkDPoint partMidPt = (*CurveDDPointAtT[fVerb])(curvePart, 0.5f);
+ SkDLine ray = {{{midPt.fX, midPt.fY}, {partMidPt.fX + dxdy.fY, partMidPt.fY - dxdy.fX}}};
+ SkDCurve oppPart;
+ opp->subDivide(priorPtT->span(), ptT->span(), &oppPart);
+ (*CurveDIntersectRay[opp->verb()])(oppPart, ray, &i);
// measure distance and see if it's small enough to denote coincidence
for (int index = 0; index < i.used(); ++index) {
+ if (!between(0, i[0][index], 1)) {
+ continue;
+ }
SkDPoint oppPt = i.pt(index);
if (oppPt.approximatelyEqual(midPt)) {
- SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
- opp->weight(), i[index][0]);
- oppDxdy.normalize();
- dxdy.normalize();
- SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
- coincident |= flatness < flatnessLimit;
+ // the coincidence can occur at almost any angle
+ coincident = true;
}
}
}
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 1d67b1c131..55e67a5d80 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -23,9 +23,9 @@ class SkPathWriter;
class SkOpSegment {
public:
- enum AllowAlias {
- kAllowAlias,
- kNoAlias
+ enum AliasMatch {
+ kNoAliasMatch,
+ kAllowAliasMatch,
};
bool operator<(const SkOpSegment& rh) const {
@@ -45,13 +45,6 @@ public:
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
- void addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
- SkOpContourHead* contourList, SkChunkAlloc* allocator);
-
- void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- this->addAlignIntersection(*fHead.ptT(), fOriginal[0], contourList, allocator);
- this->addAlignIntersection(*fTail.ptT(), fOriginal[1], contourList, allocator);
- }
SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) {
init(pts, weight, parent, SkPath::kConic_Verb);
@@ -71,23 +64,25 @@ public:
bool addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const;
- SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* addEndSpan() {
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
angle->set(&fTail, fTail.prev());
fTail.setFromAngle(angle);
return angle;
}
+ bool addExpanded(double newT, const SkOpSpanBase* test, bool* startOver);
+
SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) {
init(pts, 1, parent, SkPath::kLine_Verb);
fBounds.set(pts, 2);
return this;
}
- SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
+ SkOpPtT* addMissing(double t, SkOpSegment* opp, bool* allExist);
- SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* addStartSpan() {
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
angle->set(&fHead, fHead.next());
fHead.setToAngle(angle);
return angle;
@@ -101,9 +96,11 @@ public:
return this;
}
- SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
+ SkOpPtT* addT(double t, AliasMatch, bool* allocated);
- void align();
+ template<typename T> T* allocateArray(int count) {
+ return SkOpTAllocator<T>::AllocateArray(this->globalState()->allocator(), count);
+ }
const SkPathOpsBounds& bounds() const {
return fBounds;
@@ -113,7 +110,7 @@ public:
++fCount;
}
- void calcAngles(SkChunkAlloc*);
+ void calcAngles();
bool collapsed() const;
static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
@@ -121,6 +118,11 @@ public:
SkOpAngle::IncludeType );
int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
+ void clearAll();
+ void clearOne(SkOpSpan* span);
+ static void ClearVisited(SkOpSpanBase* span);
+ bool contains(double t) const;
+
SkOpContour* contour() const {
return fContour;
}
@@ -129,36 +131,30 @@ public:
return fCount;
}
- void debugAddAngle(double startT, double endT, SkChunkAlloc*);
- void debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- const SkOpPtT& endPtT, const SkPoint& oldPt,
- const SkOpContourHead* ) const;
-
- void debugAddAlignIntersections(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- SkOpContourHead* contourList) const {
- this->debugAddAlignIntersection(id, glitches, *fHead.ptT(), fOriginal[0], contourList);
- this->debugAddAlignIntersection(id, glitches, *fTail.ptT(), fOriginal[1], contourList);
- }
-
- bool debugAddMissing(double t, const SkOpSegment* opp) const;
- void debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+ void debugAddAngle(double startT, double endT);
+ const SkOpPtT* debugAddT(double t, AliasMatch , bool* allocated) const;
const SkOpAngle* debugAngle(int id) const;
#if DEBUG_ANGLE
void debugCheckAngleCoin() const;
#endif
+#if DEBUG_COINCIDENCE_VERBOSE
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+ void debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+#endif
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
- void debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
SkOpAngle* debugLastAngle();
- void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- const SkOpCoincidence* coincidences) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
void debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
void debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+#endif
const SkOpPtT* debugPtT(int id) const;
void debugReset();
const SkOpSegment* debugSegment(int id) const;
@@ -173,11 +169,24 @@ public:
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
+
+#if DEBUG_COINCIDENCE
+ static void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span);
+
+ bool debugVisited() const {
+ if (!fDebugVisited) {
+ fDebugVisited = true;
+ return false;
+ }
+ return true;
+ }
+#endif
+
void release(const SkOpSpan* );
double distSq(double t, const SkOpAngle* opp) const;
bool done() const {
- SkASSERT(fDoneCount <= fCount);
+ SkASSERT(this->globalState()->debugSkipAssert() || fDoneCount <= fCount);
return fDoneCount == fCount;
}
@@ -200,7 +209,7 @@ public:
void dumpPts(const char* prefix = "seg") const;
void dumpPtsInner(const char* prefix = "seg") const;
- void findCollapsed();
+ const SkOpPtT* existing(double t, const SkOpSegment* opp) const;
SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
int xorMiMask, int xorSuMask);
@@ -220,8 +229,8 @@ public:
void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb);
- SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
- SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
+ SkOpSpan* insert(SkOpSpan* prev) {
+ SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(this->globalState()->allocator());
SkOpSpanBase* next = prev->next();
result->setPrev(prev);
prev->setNext(result);
@@ -269,8 +278,9 @@ public:
void markDone(SkOpSpan* );
bool markWinding(SkOpSpan* , int winding);
bool markWinding(SkOpSpan* , int winding, int oppWinding);
- bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
- bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+ bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt,
+ AliasMatch ) const;
+ bool missingCoincidence();
bool moveMultiples();
void moveNearby();
@@ -302,17 +312,31 @@ public:
}
bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
+ SkASSERT(this == span.segment());
+ SkASSERT(this == test.segment());
return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
}
bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
+ SkASSERT(this == span.segment());
return ptsDisjoint(span.fT, span.fPt, t, pt);
}
+ bool ptsDisjoint(const SkOpSpanBase* span, const SkOpSpanBase* test) const {
+ SkASSERT(this == span->segment());
+ SkASSERT(this == test->segment());
+ return ptsDisjoint(span->t(), span->pt(), test->t(), test->pt());
+ }
+
bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
- void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
- SkChunkAlloc* allocator);
+ void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc*);
+
+#if DEBUG_COINCIDENCE
+ void resetDebugVisited() const {
+ fDebugVisited = false;
+ }
+#endif
void resetVisited() {
fVisited = false;
@@ -330,10 +354,6 @@ public:
fPrev = prev;
}
- void setVisited() {
- fVisited = true;
- }
-
void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
int deltaSum = SpanSign(start, end);
*maxWinding = *sumWinding;
@@ -348,6 +368,7 @@ public:
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
void sortAngles();
+ bool spansNearby(const SkOpSpanBase* ref, const SkOpSpanBase* check) const;
static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
int result = start->t() < end->t() ? -start->upCast()->windValue()
@@ -372,9 +393,10 @@ public:
}
bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior,
- const SkOpSpanBase* spanBase, const SkOpSegment* opp, SkScalar flatnessLimit) const;
+ const SkOpSpanBase* spanBase, const SkOpSegment* opp) const;
void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
+ bool uniqueT(double t, AliasMatch allowAlias) const;
int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateOppWinding(const SkOpAngle* angle) const;
int updateOppWindingReverse(const SkOpAngle* angle) const;
@@ -404,17 +426,12 @@ public:
SkOpSpan* windingSpanAtT(double tHit);
int windSum(const SkOpAngle* angle) const;
- SkPoint* writablePt(bool end) {
- return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
- }
-
private:
SkOpSpan fHead; // the head span always has its t set to zero
SkOpSpanBase fTail; // the tail span always has its t set to one
SkOpContour* fContour;
SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments
const SkOpSegment* fPrev;
- SkPoint fOriginal[2]; // if aligned, the original unaligned points are here
SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked
SkPathOpsBounds fBounds; // tight bounds
SkScalar fWeight;
@@ -422,6 +439,9 @@ private:
int fDoneCount; // number of processed spans (zero initially)
SkPath::Verb fVerb;
bool fVisited; // used by missing coincidence check
+#if DEBUG_COINCIDENCE
+ mutable bool fDebugVisited; // used by debug missing coincidence check
+#endif
SkDEBUGCODE(int fID);
};
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
index f3362235d8..577a9db326 100755
--- a/src/pathops/SkOpSpan.cpp
+++ b/src/pathops/SkOpSpan.cpp
@@ -35,54 +35,60 @@ bool SkOpPtT::contains(const SkOpPtT* check) const {
return false;
}
-SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) {
- SkASSERT(this->segment() != check);
- SkOpPtT* ptT = this;
+bool SkOpPtT::contains(const SkOpSegment* segment, const SkPoint& pt) const {
+ SkASSERT(this->segment() != segment);
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
while ((ptT = ptT->next()) != stopPtT) {
- if (ptT->segment() == check) {
- return ptT;
+ if (ptT->fPt == pt && ptT->segment() == segment) {
+ return true;
}
}
- return nullptr;
+ return false;
}
-SkOpContour* SkOpPtT::contour() const {
- return segment()->contour();
+bool SkOpPtT::contains(const SkOpSegment* segment, double t) const {
+ const SkOpPtT* ptT = this;
+ const SkOpPtT* stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT->fT == t && ptT->segment() == segment) {
+ return true;
+ }
+ }
+ return false;
}
-SkOpPtT* SkOpPtT::doppelganger() {
- SkASSERT(fDeleted);
- SkOpPtT* ptT = fNext;
- while (ptT->fDeleted) {
- ptT = ptT->fNext;
- }
+const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const {
+ SkASSERT(this->segment() != check);
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
- do {
- if (ptT->fSpan == fSpan) {
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT->segment() == check && !ptT->deleted()) {
return ptT;
}
- ptT = ptT->fNext;
- } while (stopPtT != ptT);
- SkASSERT(0);
+ }
return nullptr;
}
-SkOpPtT* SkOpPtT::find(SkOpSegment* segment) {
- SkOpPtT* ptT = this;
+SkOpContour* SkOpPtT::contour() const {
+ return segment()->contour();
+}
+
+const SkOpPtT* SkOpPtT::find(const SkOpSegment* segment) const {
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
do {
- if (ptT->segment() == segment) {
+ if (ptT->segment() == segment && !ptT->deleted()) {
return ptT;
}
ptT = ptT->fNext;
} while (stopPtT != ptT);
- SkASSERT(0);
+// SkASSERT(0);
return nullptr;
}
SkOpGlobalState* SkOpPtT::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -92,6 +98,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
fNext = this;
fDuplicatePt = duplicate;
fDeleted = false;
+ fCoincident = false;
SkDEBUGCODE(fID = span->globalState()->nextPtTID());
}
@@ -104,6 +111,16 @@ bool SkOpPtT::onEnd() const {
return span == segment->head() || span == segment->tail();
}
+bool SkOpPtT::ptAlreadySeen(const SkOpPtT* check) const {
+ while (this != check) {
+ if (this->fPt == check->fPt) {
+ return true;
+ }
+ check = check->fNext;
+ }
+ return false;
+}
+
SkOpPtT* SkOpPtT::prev() {
SkOpPtT* result = this;
SkOpPtT* next = this;
@@ -114,12 +131,12 @@ SkOpPtT* SkOpPtT::prev() {
return result;
}
-SkOpPtT* SkOpPtT::remove() {
+SkOpPtT* SkOpPtT::remove(const SkOpPtT* kept) {
SkOpPtT* prev = this;
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
- prev->removeNext(this);
+ prev->removeNext(kept);
SkASSERT(prev->fNext != prev);
fDeleted = true;
return prev;
@@ -130,12 +147,16 @@ SkOpPtT* SkOpPtT::remove() {
return nullptr;
}
-void SkOpPtT::removeNext(SkOpPtT* kept) {
+void SkOpPtT::removeNext(const SkOpPtT* kept) {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
SkASSERT(this != next->fNext);
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
+ SkOpCoincidence* coincidence = span->globalState()->coincidence();
+ if (coincidence) {
+ coincidence->fixUp(next, kept);
+ }
next->setDeleted();
if (span->ptT() == next) {
span->upCast()->release(kept);
@@ -150,85 +171,68 @@ SkOpSegment* SkOpPtT::segment() {
return span()->segment();
}
-void SkOpSpanBase::align() {
- if (this->fAligned) {
+void SkOpPtT::setDeleted() {
+ SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this);
+ SkASSERT(this->globalState()->debugSkipAssert() || !fDeleted);
+ fDeleted = true;
+}
+
+// please keep this in sync with debugAddOppAndMerge
+// If the added points envelop adjacent spans, merge them in.
+void SkOpSpanBase::addOppAndMerge(SkOpSpanBase* opp) {
+ if (this->ptT()->addOpp(opp->ptT())) {
+ this->checkForCollapsedCoincidence();
+ }
+ // compute bounds of points in span
+ SkPathOpsBounds bounds;
+ bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* nextPt = head;
+ do {
+ bounds.add(nextPt->fPt);
+ } while ((nextPt = nextPt->next()) != head);
+ if (!bounds.width() && !bounds.height()) {
return;
}
- SkASSERT(!zero_or_one(this->fPtT.fT));
- SkASSERT(this->fPtT.next());
- // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- while ((ptT = ptT->next()) != stopPtT) {
- if (zero_or_one(ptT->fT)) {
- SkOpSegment* segment = ptT->segment();
- SkASSERT(this->segment() != segment);
- SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
- if (ptT->fT) {
- segment->tail()->alignEnd(1, segment->lastPt());
- } else {
- segment->head()->alignEnd(0, segment->pts()[0]);
- }
+ this->mergeContained(bounds);
+ opp->mergeContained(bounds);
+}
+
+// Please keep this in sync with debugMergeContained()
+void SkOpSpanBase::mergeContained(const SkPathOpsBounds& bounds) {
+ // while adjacent spans' points are contained by the bounds, merge them
+ SkOpSpanBase* prev = this;
+ SkOpSegment* seg = this->segment();
+ while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
+ if (prev->prev()) {
+ this->merge(prev->upCast());
+ prev = this;
+ } else if (this->final()) {
+ seg->clearAll();
return;
+ } else {
+ prev->merge(this->upCast());
}
}
- alignInner();
- this->fAligned = true;
-}
-
-
-// FIXME: delete spans that collapse
-// delete segments that collapse
-// delete contours that collapse
-void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
- SkASSERT(zero_or_one(t));
- SkOpSegment* segment = this->segment();
- SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
- alignInner();
- *segment->writablePt(!!t) = pt;
- SkOpPtT* ptT = &this->fPtT;
- SkASSERT(t == ptT->fT);
- SkASSERT(pt == ptT->fPt);
- SkOpPtT* test = ptT, * stopPtT = ptT;
- while ((test = test->next()) != stopPtT) {
- SkOpSegment* other = test->segment();
- if (other == this->segment()) {
- continue;
+ SkOpSpanBase* current = this;
+ SkOpSpanBase* next = this;
+ while (next->upCastable() && (next = next->upCast()->next())
+ && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
+ if (!current->prev() && next->final()) {
+ seg->clearAll();
+ return;
}
- if (!zero_or_one(test->fT)) {
- continue;
+ if (current->prev()) {
+ next->merge(current->upCast());
+ current = next;
+ } else {
+ current->merge(next->upCast());
+ // extra line in debug version
}
- *other->writablePt(!!test->fT) = pt;
}
- this->fAligned = true;
-}
-
-void SkOpSpanBase::alignInner() {
- // force the spans to share points and t values
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- const SkPoint& pt = ptT->fPt;
- do {
- ptT->fPt = pt;
- const SkOpSpanBase* span = ptT->span();
- SkOpPtT* test = ptT;
- do {
- SkOpPtT* prev = test;
- if ((test = test->next()) == stopPtT) {
- break;
- }
- if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
- // omit aliases that alignment makes redundant
- if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
- SkASSERT(test->alias());
- prev->removeNext(ptT);
- test = prev;
- } else {
- SkASSERT(ptT->alias());
- stopPtT = ptT = ptT->remove();
- break;
- }
- }
- } while (true);
- } while ((ptT = ptT->next()) != stopPtT);
+#if DEBUG_COINCIDENCE
+ this->globalState()->coincidence()->debugValidate();
+#endif
}
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
@@ -244,11 +248,14 @@ bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
return false;
}
-SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
- SkOpPtT* start = &fPtT;
- SkOpPtT* walk = start;
+const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const {
+ const SkOpPtT* start = &fPtT;
+ const SkOpPtT* walk = start;
while ((walk = walk->next()) != start) {
- if (walk->segment() == segment) {
+ if (walk->deleted()) {
+ continue;
+ }
+ if (walk->segment() == segment && walk->span()->ptT() == walk) {
return walk;
}
}
@@ -271,7 +278,7 @@ SkOpContour* SkOpSpanBase::contour() const {
}
SkOpGlobalState* SkOpSpanBase::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -285,6 +292,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
fChased = false;
SkDEBUGCODE(fCount = 1);
SkDEBUGCODE(fID = globalState()->nextSpanID());
+ SkDEBUGCODE(fDeleted = false);
}
// this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -294,8 +302,11 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
SkASSERT(this->t() != spanPtT->fT);
SkASSERT(!zero_or_one(spanPtT->fT));
span->release(this->ptT());
+ if (this->contains(span)) {
+ return; // merge is already in the ptT loop
+ }
SkOpPtT* remainder = spanPtT->next();
- ptT()->insert(spanPtT);
+ this->ptT()->insert(spanPtT);
while (remainder != spanPtT) {
SkOpPtT* next = remainder->next();
SkOpPtT* compare = spanPtT->next();
@@ -311,6 +322,26 @@ tryNextRemainder:
remainder = next;
}
fSpanAdds += span->fSpanAdds;
+ this->checkForCollapsedCoincidence();
+}
+
+// please keep in sync with debugCheckForCollapsedCoincidence()
+void SkOpSpanBase::checkForCollapsedCoincidence() {
+ SkOpCoincidence* coins = this->globalState()->coincidence();
+ if (coins->isEmpty()) {
+ return;
+ }
+// the insert above may have put both ends of a coincident run in the same span
+// for each coincident ptT in loop; see if its opposite in is also in the loop
+// this implementation is the motivation for marking that a ptT is referenced by a coincident span
+ SkOpPtT* head = this->ptT();
+ SkOpPtT* test = head;
+ do {
+ if (!test->coincident()) {
+ continue;
+ }
+ coins->markCollapsed(test);
+ } while ((test = test->next()) != head);
}
int SkOpSpan::computeWindSum() {
@@ -334,7 +365,45 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
return false;
}
-void SkOpSpan::release(SkOpPtT* kept) {
+void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
+ SkASSERT(t != 1);
+ initBase(segment, prev, t, pt);
+ fCoincident = this;
+ fToAngle = nullptr;
+ fWindSum = fOppSum = SK_MinS32;
+ fWindValue = 1;
+ fOppValue = 0;
+ fTopTTry = 0;
+ fChased = fDone = false;
+ segment->bumpCount();
+ fAlreadyAdded = false;
+}
+
+// Please keep this in sync with debugInsertCoincidence()
+bool SkOpSpan::insertCoincidence(const SkOpSegment* segment, bool flipped) {
+ if (this->containsCoincidence(segment)) {
+ return true;
+ }
+ SkOpPtT* next = &fPtT;
+ while ((next = next->next()) != &fPtT) {
+ if (next->segment() == segment) {
+ SkOpSpan* span = flipped ? next->span()->prev() : next->span()->upCast();
+ if (!span) {
+ return false;
+ }
+ this->insertCoincidence(span);
+ return true;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ SkASSERT(0); // FIXME? if we get here, the span is missing its opposite segment...
+#endif
+ return true;
+}
+
+void SkOpSpan::release(const SkOpPtT* kept) {
+ SkDEBUGCODE(fDeleted = true);
+ SkASSERT(kept->span() != this);
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@@ -348,20 +417,14 @@ void SkOpSpan::release(SkOpPtT* kept) {
coincidence->fixUp(this->ptT(), kept);
}
this->ptT()->setDeleted();
-}
-
-void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
- SkASSERT(t != 1);
- initBase(segment, prev, t, pt);
- fCoincident = this;
- fToAngle = nullptr;
- fWindSum = fOppSum = SK_MinS32;
- fWindValue = 1;
- fOppValue = 0;
- fTopTTry = 0;
- fChased = fDone = false;
- segment->bumpCount();
- fAlreadyAdded = false;
+ SkOpPtT* stopPtT = this->ptT();
+ SkOpPtT* testPtT = stopPtT;
+ const SkOpSpanBase* keptSpan = kept->span();
+ do {
+ if (this == testPtT->span()) {
+ testPtT->setSpan(keptSpan);
+ }
+ } while ((testPtT = testPtT->next()) != stopPtT);
}
void SkOpSpan::setOppSum(int oppSum) {
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index c6fc4b138f..63fc1e9a1e 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -12,12 +12,13 @@
#include "SkPoint.h"
class SkChunkAlloc;
-struct SkOpAngle;
+class SkOpAngle;
class SkOpContour;
class SkOpGlobalState;
class SkOpSegment;
class SkOpSpanBase;
class SkOpSpan;
+struct SkPathOpsBounds;
// subset of op span used by terminal span (when t is equal to one)
class SkOpPtT {
@@ -27,37 +28,43 @@ public:
kIsDuplicate = 1
};
- void addOpp(SkOpPtT* opp) {
+ // please keep in sync with debugAddOpp()
+ bool addOpp(SkOpPtT* opp) {
// find the fOpp ptr to opp
SkOpPtT* oppPrev = opp->fNext;
if (oppPrev == this) {
- return;
+ return false;
}
while (oppPrev->fNext != opp) {
oppPrev = oppPrev->fNext;
- if (oppPrev == this) {
- return;
- }
+ if (oppPrev == this) {
+ return false;
+ }
}
-
SkOpPtT* oldNext = this->fNext;
SkASSERT(this != opp);
this->fNext = opp;
SkASSERT(oppPrev != oldNext);
oppPrev->fNext = oldNext;
+ return true;
}
bool alias() const;
+ bool coincident() const { return fCoincident; }
bool collapsed(const SkOpPtT* ) const;
bool contains(const SkOpPtT* ) const;
- SkOpPtT* contains(const SkOpSegment* );
+ bool contains(const SkOpSegment*, const SkPoint& ) const;
+ bool contains(const SkOpSegment*, double t) const;
+ const SkOpPtT* contains(const SkOpSegment* ) const;
SkOpContour* contour() const;
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
+ bool debugAddOpp(const SkOpPtT* opp) const;
const SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
bool debugContains(const SkOpPtT* ) const;
const SkOpPtT* debugContains(const SkOpSegment* check) const;
SkOpContour* debugContour(int id);
@@ -72,8 +79,6 @@ public:
return fDeleted;
}
- SkOpPtT* doppelganger();
-
bool duplicate() const {
return fDuplicatePt;
}
@@ -82,7 +87,7 @@ public:
void dumpAll() const;
void dumpBase() const;
- SkOpPtT* find(SkOpSegment* );
+ const SkOpPtT* find(const SkOpSegment* ) const;
SkOpGlobalState* globalState() const;
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
@@ -102,14 +107,14 @@ public:
bool onEnd() const;
- static bool Overlaps(SkOpPtT* s1, SkOpPtT* e1, SkOpPtT* s2, SkOpPtT* e2,
- SkOpPtT** sOut, SkOpPtT** eOut) {
- SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
- SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
+ static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
+ const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
+ const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
+ const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
*sOut = between(s1->fT, start2->fT, e1->fT) ? start2
: between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr;
- SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
- SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
+ const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
+ const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
*eOut = between(s1->fT, end2->fT, e1->fT) ? end2
: between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr;
if (*sOut == *eOut) {
@@ -120,16 +125,23 @@ public:
return *sOut && *eOut;
}
+ bool ptAlreadySeen(const SkOpPtT* head) const;
SkOpPtT* prev();
- SkOpPtT* remove();
- void removeNext(SkOpPtT* kept);
+ SkOpPtT* remove(const SkOpPtT* kept);
+ void removeNext(const SkOpPtT* kept);
const SkOpSegment* segment() const;
SkOpSegment* segment();
- void setDeleted() {
+ void setCoincident() const {
SkASSERT(!fDeleted);
- fDeleted = true;
+ fCoincident = true;
+ }
+
+ void setDeleted();
+
+ void setSpan(const SkOpSpanBase* span) {
+ fSpan = const_cast<SkOpSpanBase*>(span);
}
const SkOpSpanBase* span() const {
@@ -144,25 +156,21 @@ public:
return fT < end->fT ? this : end;
}
- double fT;
+ double fT;
SkPoint fPt; // cache of point value at this t
protected:
SkOpSpanBase* fSpan; // contains winding data
SkOpPtT* fNext; // intersection on opposite curve or alias on this curve
- bool fDeleted; // set if removed from span list
+ bool fDeleted; // set if removed from span list
bool fDuplicatePt; // set if identical pt is somewhere in the next loop
+ // below mutable since referrer is otherwise always const
+ mutable bool fCoincident; // set if at some point a coincident span pointed here
SkDEBUGCODE(int fID);
};
class SkOpSpanBase {
public:
- void align();
-
- bool aligned() const {
- return fAligned;
- }
-
- void alignEnd(double t, const SkPoint& pt);
+ void addOppAndMerge(SkOpSpanBase* );
void bumpSpanAdds() {
++fSpanAdds;
@@ -172,17 +180,14 @@ public:
return fChased;
}
- void clearCoinEnd() {
- SkASSERT(fCoinEnd != this);
- fCoinEnd = this;
- }
+ void checkForCollapsedCoincidence();
const SkOpSpanBase* coinEnd() const {
return fCoinEnd;
}
bool contains(const SkOpSpanBase* ) const;
- SkOpPtT* contains(const SkOpSegment* );
+ const SkOpPtT* contains(const SkOpSegment* ) const;
bool containsCoinEnd(const SkOpSpanBase* coin) const {
SkASSERT(this != coin);
@@ -198,6 +203,11 @@ public:
bool containsCoinEnd(const SkOpSegment* ) const;
SkOpContour* contour() const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* ,
+ bool* del1, bool* del2) const;
+#endif
+
int debugBumpCount() {
return SkDEBUGRELEASE(++fCount, -1);
}
@@ -209,9 +219,21 @@ public:
bool debugAlignedEnd(double t, const SkPoint& pt) const;
bool debugAlignedInner() const;
const SkOpAngle* debugAngle(int id) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#endif
+ const SkOpCoincidence* debugCoincidence() const;
bool debugCoinEndLoopCheck() const;
- bool debugContains(const SkOpSegment* ) const;
SkOpContour* debugContour(int id);
+#ifdef SK_DEBUG
+ bool debugDeleted() const { return fDeleted; }
+#endif
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkOpSpanBase* ) const;
+ void debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkPathOpsBounds& bounds, bool* deleted) const;
+#endif
const SkOpPtT* debugPtT(int id) const;
const SkOpSegment* debugSegment(int id) const;
const SkOpSpanBase* debugSpan(int id) const;
@@ -227,6 +249,7 @@ public:
void dumpCoin() const;
void dumpAll() const;
void dumpBase() const;
+ void dumpHead() const;
bool final() const {
return fPtT.fT == 1;
@@ -238,6 +261,7 @@ public:
void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+ // Please keep this in sync with debugInsertCoinEnd()
void insertCoinEnd(SkOpSpanBase* coin) {
if (containsCoinEnd(coin)) {
SkASSERT(coin->containsCoinEnd(this));
@@ -252,8 +276,13 @@ public:
}
void merge(SkOpSpan* span);
+ void mergeContained(const SkPathOpsBounds& bounds);
- SkOpSpan* prev() const {
+ const SkOpSpan* prev() const {
+ return fPrev;
+ }
+
+ SkOpSpan* prev() {
return fPrev;
}
@@ -281,8 +310,6 @@ public:
fChased = chased;
}
- SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
-
void setFromAngle(SkOpAngle* angle) {
fFromAngle = angle;
}
@@ -293,7 +320,7 @@ public:
bool simple() const {
fPtT.debugValidate();
- return fPtT.next()->next() == &fPtT;
+ return fPtT.next()->next() == &fPtT;
}
int spanAddsCount() const {
@@ -368,6 +395,7 @@ protected: // no direct access to internals to avoid treating a span base as a
bool fChased; // set after span has been added to chase array
SkDEBUGCODE(int fCount); // number of pt/t pairs added
SkDEBUGCODE(int fID);
+ SkDEBUGCODE(bool fDeleted); // set when span was merged with another span
};
class SkOpSpan : public SkOpSpanBase {
@@ -404,7 +432,12 @@ public:
}
bool debugCoinLoopCheck() const;
- void release(SkOpPtT* );
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const;
+ void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* ,
+ const SkOpSegment* , bool flipped) const;
+#endif
+ void release(const SkOpPtT* );
bool done() const {
SkASSERT(!final());
@@ -414,7 +447,9 @@ public:
void dumpCoin() const;
bool dumpSpan() const;
void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+ bool insertCoincidence(const SkOpSegment* , bool flipped);
+ // Please keep this in sync with debugInsertCoincidence()
void insertCoincidence(SkOpSpan* coin) {
if (containsCoincidence(coin)) {
SkASSERT(coin->containsCoincidence(this));
@@ -470,6 +505,7 @@ public:
void setOppValue(int oppValue) {
SkASSERT(!final());
SkASSERT(fOppSum == SK_MinS32);
+ SkASSERT(!oppValue || !fDone);
fOppValue = oppValue;
}
@@ -484,6 +520,7 @@ public:
SkASSERT(!final());
SkASSERT(windValue >= 0);
SkASSERT(fWindSum == SK_MinS32);
+ SkASSERT(!windValue || !fDone);
fWindValue = windValue;
}
diff --git a/src/pathops/SkPathOpsBounds.h b/src/pathops/SkPathOpsBounds.h
index b99f36f5d9..610d7233a3 100644
--- a/src/pathops/SkPathOpsBounds.h
+++ b/src/pathops/SkPathOpsBounds.h
@@ -33,6 +33,13 @@ struct SkPathOpsBounds : public SkRect {
add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
}
+ void add(const SkPoint& pt) {
+ if (pt.fX < fLeft) fLeft = pt.fX;
+ if (pt.fY < fTop) fTop = pt.fY;
+ if (pt.fX > fRight) fRight = pt.fX;
+ if (pt.fY > fBottom) fBottom = pt.fY;
+ }
+
void add(const SkDPoint& pt) {
if (pt.fX < fLeft) fLeft = SkDoubleToScalar(pt.fX);
if (pt.fY < fTop) fTop = SkDoubleToScalar(pt.fY);
@@ -40,13 +47,18 @@ struct SkPathOpsBounds : public SkRect {
if (pt.fY > fBottom) fBottom = SkDoubleToScalar(pt.fY);
}
- bool almostContains(const SkPoint& pt) {
+ bool almostContains(const SkPoint& pt) const {
return AlmostLessOrEqualUlps(fLeft, pt.fX)
&& AlmostLessOrEqualUlps(pt.fX, fRight)
&& AlmostLessOrEqualUlps(fTop, pt.fY)
&& AlmostLessOrEqualUlps(pt.fY, fBottom);
}
+ bool contains(const SkPoint& pt) const {
+ return fLeft <= pt.fX && fTop <= pt.fY &&
+ fRight >= pt.fX && fBottom >= pt.fY;
+ }
+
typedef SkRect INHERITED;
};
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 24ef6f1129..fd4c027ddb 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -11,6 +11,28 @@
#include "SkPathWriter.h"
#include "SkTSort.h"
+SkScalar ScaleFactor(const SkPath& path) {
+ static const SkScalar twoTo10 = 1024.f;
+ SkScalar largest = 0;
+ const SkScalar* oneBounds = &path.getBounds().fLeft;
+ for (int index = 0; index < 4; ++index) {
+ largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
+ }
+ SkScalar scale = twoTo10;
+ SkScalar next;
+ while ((next = scale * twoTo10) < largest) {
+ scale = next;
+ }
+ return scale == twoTo10 ? SK_Scalar1 : scale;
+}
+
+void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
+ SkMatrix matrix;
+ matrix.setScale(scale, scale);
+ *scaled = path;
+ scaled->transform(matrix);
+}
+
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
bool* sortablePtr) {
// find first angle, initialize winding to computed fWindSum
@@ -144,15 +166,6 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
return nullptr;
}
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkOpContourHead* contourList) {
- SkOpContour* contour = contourList;
- do {
- contour->debugShowActiveSpans();
- } while ((contour = contour->next()));
-}
-#endif
-
bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
SkTDArray<SkOpContour* > list;
SkOpContour* contour = *contourList;
@@ -201,7 +214,7 @@ public:
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContourHead contour;
- SkOpGlobalState globalState(nullptr, &contour SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
#if DEBUG_SHOW_TEST_NAME
SkDebugf("</div>\n");
@@ -209,8 +222,8 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- builder.finish(&allocator);
+ SkOpEdgeBuilder builder(path, &contour, &globalState);
+ builder.finish();
SkTDArray<const SkOpContour* > runs; // indices of partial contours
const SkOpContour* eContour = builder.head();
do {
@@ -391,40 +404,18 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#endif
}
-static void align(SkOpContourHead* contourList) {
- SkOpContour* contour = contourList;
- do {
- contour->align();
- } while ((contour = contour->next()));
-}
-
-static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkOpContour* contour = contourList;
- do {
- contour->addAlignIntersections(contourList, allocator);
- } while ((contour = contour->next()));
-}
-
-static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkOpContour* contour = contourList;
- do {
- contour->calcAngles(allocator);
- } while ((contour = contour->next()));
-}
-
-static void findCollapsed(SkOpContourHead* contourList) {
+static void calcAngles(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
do {
- contour->findCollapsed();
+ contour->calcAngles();
} while ((contour = contour->next()));
}
-static bool missingCoincidence(SkOpContourHead* contourList,
- SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
+static bool missingCoincidence(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
bool result = false;
do {
- result |= contour->missingCoincidence(coincidence, allocator);
+ result |= contour->missingCoincidence();
} while ((contour = contour->next()));
return result;
}
@@ -453,72 +444,116 @@ static void sortAngles(SkOpContourHead* contourList) {
} while ((contour = contour->next()));
}
-bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator) {
+bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
SkOpGlobalState* globalState = contourList->globalState();
- // combine t values when multiple intersections occur on some segments but not others
DEBUG_COINCIDENCE_HEALTH(contourList, "start");
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kIntersecting);
+#endif
+
+ // match up points within the coincident runs
+ if (!coincidence->addExpanded()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded");
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+ // combine t values when multiple intersections occur on some segments but not others
if (!moveMultiples(contourList)) {
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples");
- findCollapsed(contourList);
- DEBUG_COINCIDENCE_HEALTH(contourList, "findCollapsed");
// move t values and points together to eliminate small/tiny gaps
- moveNearby(contourList);
+ (void) moveNearby(contourList);
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
- align(contourList); // give all span members common values
- DEBUG_COINCIDENCE_HEALTH(contourList, "align");
- if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
- return false;
- }
- DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned");
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kIntersecting);
#endif
- // look for intersections on line segments formed by moving end points
- addAlignIntersections(contourList, allocator);
- DEBUG_COINCIDENCE_HEALTH(contourList, "addAlignIntersections");
- if (coincidence->addMissing(allocator)) {
- DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
- moveNearby(contourList);
- DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
- align(contourList); // give all span members common values
- DEBUG_COINCIDENCE_HEALTH(contourList, "align2");
- if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
+ // add coincidence formed by pairing on curve points and endpoints
+ coincidence->correctEnds();
+ if (!coincidence->addEndMovedSpans()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addEndMovedSpans");
+
+ const int SAFETY_COUNT = 100; // FIXME: tune
+ int safetyHatch = SAFETY_COUNT;
+ // look for coincidence present in A-B and A-C but missing in B-C
+ while (coincidence->addMissing()) {
+ if (!--safetyHatch) {
+ SkASSERT(0); // FIXME: take this out after verifying std tests don't trigger
return false;
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2");
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
+ moveNearby(contourList);
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
}
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kWalking);
-#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
+ // FIXME: only call this if addMissing modified something when returning false
+ moveNearby(contourList);
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
// check to see if, loosely, coincident ranges may be expanded
if (coincidence->expand()) {
DEBUG_COINCIDENCE_HEALTH(contourList, "expand1");
- if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
+ coincidence->addMissing();
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
+ if (!coincidence->addExpanded()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
+ if (!moveMultiples(contourList)) {
return false;
}
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples2");
+ moveNearby(contourList);
}
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
DEBUG_COINCIDENCE_HEALTH(contourList, "expand2");
// the expanded ranges may not align -- add the missing spans
+ SkAssertResult(coincidence->addExpanded());
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
+ coincidence->correctEnds();
if (!coincidence->mark()) { // mark spans of coincident segments as coincident
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "mark1");
- // look for coincidence missed earlier
- if (missingCoincidence(contourList, coincidence, allocator)) {
+ // look for coincidence lines and curves undetected by intersection
+ if (missingCoincidence(contourList)) {
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kIntersecting);
+#endif
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1");
(void) coincidence->expand();
DEBUG_COINCIDENCE_HEALTH(contourList, "expand3");
- if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
+ if (!coincidence->addExpanded()) {
return false;
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
- coincidence->mark();
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
+ if (!coincidence->mark()) {
+ return false;
+ }
+ } else {
+ DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
+ (void) coincidence->expand();
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
- SkOpCoincidence overlaps;
+ DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence3");
+
+ (void) coincidence->expand();
+
+#if 0 // under development
+ // coincident runs may cross two or more spans, but the opposite spans may be out of order
+ if (!coincidence->reorder()) {
+ return false;
+ }
+#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "coincidence.reorder");
+ SkOpCoincidence overlaps(globalState);
do {
SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
if (!pairs->apply()) { // adjust the winding value to account for coincident edges
@@ -527,22 +562,25 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply");
// For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
// are different, construct a new pair to resolve their mutual span
- if (!pairs->findOverlaps(&overlaps, allocator)) {
+ if (!pairs->findOverlaps(&overlaps)) {
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps");
} while (!overlaps.isEmpty());
- calcAngles(contourList, allocator);
+ calcAngles(contourList);
sortAngles(contourList);
if (globalState->angleCoincidence()) {
- (void) missingCoincidence(contourList, coincidence, allocator);
+ (void) missingCoincidence(contourList);
if (!coincidence->apply()) {
return false;
}
}
-#if DEBUG_ACTIVE_SPANS
+#if DEBUG_COINCIDENCE_VERBOSE
coincidence->debugShowCoincidence();
- DebugShowActiveSpans(contourList);
#endif
+#if DEBUG_COINCIDENCE
+ coincidence->debugValidate();
+#endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
return true;
}
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 5b4902f53b..ed80318160 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -24,12 +24,11 @@ SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
bool FixWinding(SkPath* path);
bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
+bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* );
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkDEBUGPARAMS(bool skipAssert)
SkDEBUGPARAMS(const char* testName));
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkOpContourHead* );
-#endif
+SkScalar ScaleFactor(const SkPath& path);
+void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled);
#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index a161e368e7..7fd3dd235d 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -277,7 +277,7 @@ bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
for (int index = 0; index < roots; ++index) {
if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
*t = maxCurvature[index];
- return true;
+ return *t > 0 && *t < 1;
}
}
} else if (infTCount == 1) {
diff --git a/src/pathops/SkPathOpsCurve.cpp b/src/pathops/SkPathOpsCurve.cpp
index bf44d25e17..df67efaca5 100644
--- a/src/pathops/SkPathOpsCurve.cpp
+++ b/src/pathops/SkPathOpsCurve.cpp
@@ -8,6 +8,57 @@
#include "SkPathOpsRect.h"
#include "SkPathOpsCurve.h"
+ // this cheats and assumes that the perpendicular to the point is the closest ray to the curve
+ // this case (where the line and the curve are nearly coincident) may be the only case that counts
+double SkDCurve::nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const {
+ int count = SkPathOpsVerbToPoints(verb);
+ double minX = fCubic.fPts[0].fX;
+ double maxX = minX;
+ for (int index = 0; index < count; ++index) {
+ minX = SkTMin(minX, fCubic.fPts[index].fX);
+ maxX = SkTMax(maxX, fCubic.fPts[index].fX);
+ }
+ if (!AlmostBetweenUlps(minX, xy.fX, maxX)) {
+ return -1;
+ }
+ double minY = fCubic.fPts[0].fY;
+ double maxY = minY;
+ for (int index = 0; index < count; ++index) {
+ minY = SkTMin(minY, fCubic.fPts[index].fY);
+ maxY = SkTMax(maxY, fCubic.fPts[index].fY);
+ }
+ if (!AlmostBetweenUlps(minY, xy.fY, maxY)) {
+ return -1;
+ }
+ SkIntersections i;
+ SkDLine perp = {{ xy, { xy.fX + opp.fY - xy.fY, xy.fY + xy.fX - opp.fX }}};
+ (*CurveDIntersectRay[verb])(*this, perp, &i);
+ int minIndex = -1;
+ double minDist = FLT_MAX;
+ for (int index = 0; index < i.used(); ++index) {
+ double dist = xy.distance(i.pt(index));
+ if (minDist > dist) {
+ minDist = dist;
+ minIndex = index;
+ }
+ }
+ if (minIndex < 0) {
+ return -1;
+ }
+ double largest = SkTMax(SkTMax(maxX, maxY), -SkTMin(minX, minY));
+ if (!AlmostEqualUlps_Pin(largest, largest + minDist)) { // is distance within ULPS tolerance?
+ return -1;
+ }
+ return SkPinT(i[0][minIndex]);
+}
+
+void SkDCurve::offset(SkPath::Verb verb, const SkDVector& off) {
+ int count = SkPathOpsVerbToPoints(verb);
+ for (int index = 0; index < count; ++index) {
+ fCubic.fPts[index] += off;
+ }
+}
+
void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
double tStart, double tEnd, SkPathOpsBounds* bounds) {
SkDConic dCurve;
@@ -18,7 +69,7 @@ void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
}
-void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar ,
+void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar ,
double tStart, double tEnd, SkPathOpsBounds* bounds) {
SkDCubic dCurve;
dCurve.set(curve);
diff --git a/src/pathops/SkPathOpsCurve.h b/src/pathops/SkPathOpsCurve.h
index 97e20be2b1..dc9cec97e4 100644
--- a/src/pathops/SkPathOpsCurve.h
+++ b/src/pathops/SkPathOpsCurve.h
@@ -64,14 +64,17 @@ struct SkDCurve {
return fCubic[n];
}
- SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight,
+ SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight,
double s, double e, double* topT);
SkDPoint cubicTop(const SkPoint curve[4], SkScalar , double s, double e, double* topT);
+ void dump() const;
void dumpID(int ) const;
SkDPoint lineTop(const SkPoint[2], SkScalar , double , double , double* topT);
+ double nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const;
+ void offset(SkPath::Verb verb, const SkDVector& );
SkDPoint quadTop(const SkPoint curve[3], SkScalar , double s, double e, double* topT);
- void setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
+ void setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
double s, double e, SkPathOpsBounds* );
void setCubicBounds(const SkPoint curve[4], SkScalar ,
double s, double e, SkPathOpsBounds* );
@@ -115,6 +118,30 @@ static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], SkScalar , double )
dcubic_xy_at_t
};
+static SkDPoint ddline_xy_at_t(const SkDCurve& c, double t) {
+ return c.fLine.ptAtT(t);
+}
+
+static SkDPoint ddquad_xy_at_t(const SkDCurve& c, double t) {
+ return c.fQuad.ptAtT(t);
+}
+
+static SkDPoint ddconic_xy_at_t(const SkDCurve& c, double t) {
+ return c.fConic.ptAtT(t);
+}
+
+static SkDPoint ddcubic_xy_at_t(const SkDCurve& c, double t) {
+ return c.fCubic.ptAtT(t);
+}
+
+static SkDPoint (* const CurveDDPointAtT[])(const SkDCurve& , double ) = {
+ nullptr,
+ ddline_xy_at_t,
+ ddquad_xy_at_t,
+ ddconic_xy_at_t,
+ ddcubic_xy_at_t
+};
+
static SkPoint fline_xy_at_t(const SkPoint a[2], SkScalar weight, double t) {
return dline_xy_at_t(a, weight, t).asSkPoint();
}
@@ -171,6 +198,30 @@ static SkDVector (* const CurveDSlopeAtT[])(const SkPoint[], SkScalar , double )
dcubic_dxdy_at_t
};
+static SkDVector ddline_dxdy_at_t(const SkDCurve& c, double ) {
+ return c.fLine.fPts[1] - c.fLine.fPts[0];
+}
+
+static SkDVector ddquad_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fQuad.dxdyAtT(t);
+}
+
+static SkDVector ddconic_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fConic.dxdyAtT(t);
+}
+
+static SkDVector ddcubic_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fCubic.dxdyAtT(t);
+}
+
+static SkDVector (* const CurveDDSlopeAtT[])(const SkDCurve& , double ) = {
+ nullptr,
+ ddline_dxdy_at_t,
+ ddquad_dxdy_at_t,
+ ddconic_dxdy_at_t,
+ ddcubic_dxdy_at_t
+};
+
static SkVector fline_dxdy_at_t(const SkPoint a[2], SkScalar , double ) {
return a[1] - a[0];
}
@@ -269,6 +320,30 @@ static void (* const CurveIntersectRay[])(const SkPoint[] , SkScalar , const SkD
cubic_intersect_ray
};
+static void dline_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fLine, ray);
+}
+
+static void dquad_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fQuad, ray);
+}
+
+static void dconic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fConic, ray);
+}
+
+static void dcubic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fCubic, ray);
+}
+
+static void (* const CurveDIntersectRay[])(const SkDCurve& , const SkDLine& , SkIntersections* ) = {
+ nullptr,
+ dline_intersect_ray,
+ dquad_intersect_ray,
+ dconic_intersect_ray,
+ dcubic_intersect_ray
+};
+
static int line_intercept_h(const SkPoint a[2], SkScalar , SkScalar y, double* roots) {
SkDLine line;
roots[0] = SkIntersections::HorizontalIntercept(line.set(a), y);
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 546771e84c..93743f3d6c 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -12,7 +12,7 @@
#include "SkPathOpsDebug.h"
#include "SkString.h"
-struct SkCoincidentSpans;
+class SkCoincidentSpans;
#if DEBUG_VALIDATE
extern bool FLAGS_runFail;
@@ -46,11 +46,15 @@ bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
}
#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
enum GlitchType {
kAddCorruptCoin_Glitch,
kAddExpandedCoin_Glitch,
+ kAddExpandedFail_Glitch,
+ kAddIfMissingCoin_Glitch,
kAddMissingCoin_Glitch,
+ kAddMissingExtend_Glitch,
+ kAddOrOverlap_Glitch,
kCollapsedCoin_Glitch,
kCollapsedDone_Glitch,
kCollapsedOppValue_Glitch,
@@ -60,30 +64,41 @@ enum GlitchType {
kExpandCoin_Glitch,
kMarkCoinEnd_Glitch,
kMarkCoinInsert_Glitch,
+ kMarkCoinMissing_Glitch,
+ kMarkCoinStart_Glitch,
+ kMergeContained_Glitch,
kMissingCoin_Glitch,
kMissingDone_Glitch,
kMissingIntersection_Glitch,
kMoveMultiple_Glitch,
+ kMoveNearbyClearAll_Glitch,
+ kMoveNearbyClearAll2_Glitch,
+ kMoveNearbyMerge_Glitch,
+ kMoveNearbyMergeFinal_Glitch,
+ kMoveNearbyRelease_Glitch,
+ kMoveNearbyReleaseFinal_Glitch,
+ kReleasedSpan_Glitch,
kUnaligned_Glitch,
kUnalignedHead_Glitch,
kUnalignedTail_Glitch,
- kUndetachedSpan_Glitch,
- kUnmergedSpan_Glitch,
};
-static const int kGlitchType_Count = kUnmergedSpan_Glitch + 1;
+static const int kGlitchType_Count = kUnalignedTail_Glitch + 1;
struct SpanGlitch {
const char* fStage;
const SkOpSpanBase* fBase;
const SkOpSpanBase* fSuspect;
- const SkCoincidentSpans* fCoin;
const SkOpSegment* fSegment;
+ const SkOpSegment* fOppSegment;
const SkOpPtT* fCoinSpan;
const SkOpPtT* fEndSpan;
const SkOpPtT* fOppSpan;
const SkOpPtT* fOppEndSpan;
- double fT;
+ double fStartT;
+ double fEndT;
+ double fOppStartT;
+ double fOppEndT;
SkPoint fPt;
GlitchType fType;
};
@@ -94,13 +109,16 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fStage = stage;
glitch->fBase = nullptr;
glitch->fSuspect = nullptr;
- glitch->fCoin = nullptr;
glitch->fSegment = nullptr;
+ glitch->fOppSegment = nullptr;
glitch->fCoinSpan = nullptr;
glitch->fEndSpan = nullptr;
glitch->fOppSpan = nullptr;
glitch->fOppEndSpan = nullptr;
- glitch->fT = SK_ScalarNaN;
+ glitch->fStartT = SK_ScalarNaN;
+ glitch->fEndT = SK_ScalarNaN;
+ glitch->fOppStartT = SK_ScalarNaN;
+ glitch->fOppEndT = SK_ScalarNaN;
glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
glitch->fType = type;
return glitch;
@@ -113,11 +131,22 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fSuspect = suspect;
}
+ void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
+ const SkOpPtT* ptT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fBase = base;
+ glitch->fCoinSpan = ptT;
+ }
+
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
- const SkOpPtT* coinSpan) {
+ const SkCoincidentSpans* opp = NULL) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fCoin = coin;
- glitch->fCoinSpan = coinSpan;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
+ if (opp) {
+ glitch->fOppSpan = opp->coinPtTStart();
+ glitch->fOppEndSpan = opp->coinPtTEnd();
+ }
}
void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
@@ -125,7 +154,7 @@ struct SkPathOpsDebug::GlitchLog {
SpanGlitch* glitch = recordCommon(type, stage);
glitch->fBase = base;
glitch->fSegment = seg;
- glitch->fT = t;
+ glitch->fStartT = t;
glitch->fPt = pt;
}
@@ -133,23 +162,26 @@ struct SkPathOpsDebug::GlitchLog {
SkPoint pt) {
SpanGlitch* glitch = recordCommon(type, stage);
glitch->fBase = base;
- glitch->fT = t;
+ glitch->fStartT = t;
glitch->fPt = pt;
}
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fCoin = coin;
- glitch->fCoinSpan = coinSpan;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
glitch->fEndSpan = endSpan;
+ glitch->fOppSpan = coinSpan;
+ glitch->fOppEndSpan = endSpan;
}
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
- const SkOpSpanBase* suspect) {
+ const SkOpSpanBase* base) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fSuspect = suspect;
- glitch->fCoin = coin;
+ glitch->fBase = base;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
}
void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
@@ -161,22 +193,73 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fOppEndSpan = oPtTE;
}
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg, double startT,
+ double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ glitch->fStartT = startT;
+ glitch->fEndT = endT;
+ glitch->fOppSegment = oppSeg;
+ glitch->fOppStartT = oppStartT;
+ glitch->fOppEndT = oppEndT;
+ }
+
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg,
+ const SkOpSpan* span) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ glitch->fBase = span;
+ }
+
+ void record(GlitchType type, const char* stage, double t, const SkOpSpanBase* span) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fStartT = t;
+ glitch->fBase = span;
+ }
+
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ }
+
+ void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
+ const SkOpPtT* ptT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = ptT;
+ }
+
SkTDArray<SpanGlitch> fGlitches;
};
+#endif
+void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
+#if DEBUG_ACTIVE_SPANS
+ SkOpContour* contour = contourList;
+ do {
+ contour->debugShowActiveSpans();
+ } while ((contour = contour->next()));
+#endif
+}
+
+#if DEBUG_COINCIDENCE
void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
+ contourList->globalState()->debugSetCheckHealth(true);
+#if DEBUG_COINCIDENCE_VERBOSE
GlitchLog glitches;
const SkOpContour* contour = contourList;
const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
+ coincidence->debugCheckValid(id, &glitches); // don't call validate; spans may be inconsistent
do {
contour->debugCheckHealth(id, &glitches);
- contour->debugMissingCoincidence(id, &glitches, coincidence);
+ contour->debugMissingCoincidence(id, &glitches);
} while ((contour = contour->next()));
- coincidence->debugFixAligned(id, &glitches);
+ coincidence->debugRemoveCollapsed(id, &glitches);
coincidence->debugAddMissing(id, &glitches);
coincidence->debugExpand(id, &glitches);
coincidence->debugAddExpanded(id, &glitches);
coincidence->debugMark(id, &glitches);
+ coincidence->debugReorder(id, &glitches);
unsigned mask = 0;
for (int index = 0; index < glitches.fGlitches.count(); ++index) {
const SpanGlitch& glitch = glitches.fGlitches[index];
@@ -186,6 +269,92 @@ void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
SkDebugf(mask & (1 << index) ? "x" : "-");
}
SkDebugf(" %s\n", id);
+ for (int index = 0; index < glitches.fGlitches.count(); ++index) {
+ const SpanGlitch& glitch = glitches.fGlitches[index];
+ SkDebugf("%02d: ", index);
+ if (glitch.fBase) {
+ SkDebugf(" base=%d", glitch.fBase->debugID());
+ }
+ if (glitch.fSuspect) {
+ SkDebugf(" base=%d", glitch.fSuspect->debugID());
+ }
+ if (glitch.fSegment) {
+ SkDebugf(" segment=%d", glitch.fSegment->debugID());
+ }
+ if (glitch.fCoinSpan) {
+ SkDebugf(" coinSpan=%d", glitch.fCoinSpan->debugID());
+ }
+ if (glitch.fEndSpan) {
+ SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
+ }
+ if (glitch.fOppSpan) {
+ SkDebugf(" oppSpan=%d", glitch.fOppSpan->debugID());
+ }
+ if (glitch.fOppEndSpan) {
+ SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
+ }
+ if (!SkScalarIsNaN(glitch.fStartT)) {
+ SkDebugf(" startT=%g", glitch.fStartT);
+ }
+ if (!SkScalarIsNaN(glitch.fEndT)) {
+ SkDebugf(" endT=%g", glitch.fEndT);
+ }
+ if (glitch.fOppSegment) {
+ SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
+ }
+ if (!SkScalarIsNaN(glitch.fOppStartT)) {
+ SkDebugf(" oppStartT=%g", glitch.fOppStartT);
+ }
+ if (!SkScalarIsNaN(glitch.fOppEndT)) {
+ SkDebugf(" oppEndT=%g", glitch.fOppEndT);
+ }
+ if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
+ SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
+ }
+ switch (glitch.fType) {
+ case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
+ case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
+ case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
+ case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
+ case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
+ case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
+ case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
+ case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
+ case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
+ case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
+ case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
+ case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
+ case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
+ case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
+ case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
+ case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
+ case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
+ case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
+ case kMergeContained_Glitch: SkDebugf(" MergeContained"); break;
+ case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
+ case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
+ case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
+ case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
+ case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
+ case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
+ case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
+ case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
+ case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
+ case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
+ case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
+ case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
+ case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
+ case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
+ default: SkASSERT(0);
+ }
+ SkDebugf("\n");
+ }
+ contourList->globalState()->debugSetCheckHealth(false);
+#if DEBUG_ACTIVE_SPANS
+ SkDebugf("active after %s:\n", id);
+ ShowActiveSpans(contourList);
+#endif
+#endif
}
#endif
@@ -254,7 +423,7 @@ static const char* gOpStrs[] = {
"kDifference_SkPathOp",
"kIntersect_SkPathOp",
"kUnion_SkPathOp",
- "kXor_PathOp",
+ "kXOR_PathOp",
"kReverseDifference_SkPathOp",
};
@@ -412,132 +581,72 @@ void SkDRect::debugInit() {
#include "SkOpSegment.h"
#if DEBUG_COINCIDENCE
-void SkOpSegment::debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpPtT& endPtT, const SkPoint& oldPt, const SkOpContourHead* contourList) const {
- const SkPoint& newPt = endPtT.fPt;
- if (newPt == oldPt) {
- return;
- }
- SkPoint line[2] = { newPt, oldPt };
- SkPathOpsBounds lineBounds;
- lineBounds.setBounds(line, 2);
- SkDLine aLine;
- aLine.set(line);
- const SkOpContour* current = contourList;
+// commented-out lines keep this in sync with addT()
+ const SkOpPtT* SkOpSegment::debugAddT(double t, AliasMatch allowAlias, bool* allocated) const {
+ debugValidate();
+ SkPoint pt = this->ptAtT(t);
+ const SkOpSpanBase* span = &fHead;
do {
- if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
- continue;
- }
- const SkOpSegment* segment = current->first();
- do {
- if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
- continue;
- }
- if (newPt == segment->fPts[0]) {
- continue;
- }
- if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (oldPt == segment->fPts[0]) {
- continue;
- }
- if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (endPtT.debugContains(segment)) {
- continue;
- }
- SkIntersections i;
- switch (segment->fVerb) {
- case SkPath::kLine_Verb: {
- SkDLine bLine;
- bLine.set(segment->fPts);
- i.intersect(bLine, aLine);
- } break;
- case SkPath::kQuad_Verb: {
- SkDQuad bQuad;
- bQuad.set(segment->fPts);
- i.intersect(bQuad, aLine);
- } break;
- case SkPath::kConic_Verb: {
- SkDConic bConic;
- bConic.set(segment->fPts, segment->fWeight);
- i.intersect(bConic, aLine);
- } break;
- case SkPath::kCubic_Verb: {
- SkDCubic bCubic;
- bCubic.set(segment->fPts);
- i.intersect(bCubic, aLine);
- } break;
- default:
- SkASSERT(0);
- }
- if (i.used()) {
- SkASSERT(i.used() == 1);
- SkASSERT(!zero_or_one(i[0][0]));
- SkOpSpanBase* checkSpan = fHead.next();
- while (!checkSpan->final()) {
- if (checkSpan->contains(segment)) {
- goto nextSegment;
- }
- checkSpan = checkSpan->upCast()->next();
+ const SkOpPtT* result = span->ptT();
+ const SkOpPtT* loop;
+ bool duplicatePt;
+ if (t == result->fT) {
+ goto bumpSpan;
+ }
+ if (this->match(result, this, t, pt, allowAlias)) {
+ // see if any existing alias matches segment, pt, and t
+ loop = result->next();
+ duplicatePt = false;
+ while (loop != result) {
+ bool ptMatch = loop->fPt == pt;
+ if (loop->segment() == this && loop->fT == t && ptMatch) {
+ goto bumpSpan;
}
- log->record(kMissingIntersection_Glitch, id, checkSpan, segment, i[0][0], newPt);
+ duplicatePt |= ptMatch;
+ loop = loop->next();
}
- nextSegment:
- ;
- } while ((segment = segment->next()));
- } while ((current = current->next()));
-}
-
-bool SkOpSegment::debugAddMissing(double t, const SkOpSegment* opp) const {
- const SkOpSpanBase* existing = nullptr;
- const SkOpSpanBase* test = &fHead;
- double testT;
- do {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
+ if (kNoAliasMatch == allowAlias) {
+ bumpSpan:
+// span->bumpSpanAdds();
+ return result;
}
- break;
- }
- } while ((test = test->upCast()->next()));
- return !existing || !existing->debugContains(opp);
-}
-
-void SkOpSegment::debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- const SkOpSpanBase* span = &fHead;
- if (!span->aligned()) {
- if (!span->debugAlignedEnd(0, fPts[0])) {
- glitches->record(kUnalignedHead_Glitch, id, span);
- }
- }
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
- break;
- }
- if (!span->aligned()) {
- glitches->record(kUnaligned_Glitch, id, span);
- }
- }
- if (!span->aligned()) {
- span->debugAlignedEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- }
- if (this->collapsed()) {
- const SkOpSpan* span = &fHead;
- do {
- if (span->windValue()) {
- glitches->record(kCollapsedWindValue_Glitch, id, span);
+// SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
+// alias->init(result->span(), t, pt, duplicatePt);
+// result->insert(alias);
+// result->span()->unaligned();
+ this->debugValidate();
+// #if DEBUG_ADD_T
+// SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
+// alias->segment()->debugID(), alias->span()->debugID());
+// #endif
+// span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
}
- if (span->oppValue()) {
- glitches->record(kCollapsedOppValue_Glitch, id, span);
+ return nullptr;
+ }
+ if (t < result->fT) {
+ const SkOpSpan* prev = result->span()->prev();
+ if (!prev) {
+ return nullptr; // FIXME: this is a fail case; nullptr return elsewhere means result was allocated in non-const version
}
- if (!span->done()) {
- glitches->record(kCollapsedDone_Glitch, id, span);
+// SkOpSpan* span = insert(prev, allocator);
+// span->init(this, prev, t, pt);
+ this->debugValidate();
+// #if DEBUG_ADD_T
+// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
+// span->segment()->debugID(), span->debugID());
+// #endif
+// span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
}
- } while ((span = span->next()->upCastable()));
- }
+ return nullptr;
+ }
+ SkASSERT(span != &fTail);
+ } while ((span = span->upCast()->next()));
+ SkASSERT(0);
+ return nullptr;
}
#endif
@@ -547,7 +656,7 @@ void SkOpSegment::debugCheckAngleCoin() const {
const SkOpSpan* span;
do {
const SkOpAngle* angle = base->fromAngle();
- if (angle && angle->fCheckCoincidence) {
+ if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
if (base->final()) {
@@ -555,41 +664,35 @@ void SkOpSegment::debugCheckAngleCoin() const {
}
span = base->upCast();
angle = span->toAngle();
- if (angle && angle->fCheckCoincidence) {
+ if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
} while ((base = span->next()));
}
#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
// this mimics the order of the checks in handle coincidence
void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
debugMoveMultiples(id, glitches);
- debugFindCollapsed(id, glitches);
debugMoveNearby(id, glitches);
- debugAlign(id, glitches);
- debugAddAlignIntersections(id, glitches, this->globalState()->contourHead());
+ debugMissingCoincidence(id, glitches);
+}
+// commented-out lines keep this in sync with clearAll()
+void SkOpSegment::debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ const SkOpSpan* span = &fHead;
+ do {
+ this->debugClearOne(span, id, glitches);
+ } while ((span = span->next()->upCastable()));
+ this->globalState()->coincidence()->debugRelease(id, glitches, this);
}
-void SkOpSegment::debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- if (fHead.contains(&fTail)) {
- const SkOpSpan* span = this->head();
- bool missingDone = false;
- do {
- missingDone |= !span->done();
- } while ((span = span->next()->upCastable()));
- if (missingDone) {
- glitches->record(kMissingDone_Glitch, id, &fHead);
- }
- if (!fHead.debugAlignedEnd(0, fHead.pt())) {
- glitches->record(kUnalignedHead_Glitch, id, &fHead);
- }
- if (!fTail.aligned()) {
- glitches->record(kUnalignedTail_Glitch, id, &fTail);
- }
- }
+// commented-out lines keep this in sync with clearOne()
+void SkOpSegment::debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ if (span->windValue()) glitches->record(kCollapsedWindValue_Glitch, id, span);
+ if (span->oppValue()) glitches->record(kCollapsedOppValue_Glitch, id, span);
+ if (!span->done()) glitches->record(kCollapsedDone_Glitch, id, span);
}
#endif
@@ -607,16 +710,37 @@ SkOpAngle* SkOpSegment::debugLastAngle() {
}
#if DEBUG_COINCIDENCE
-void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidences) const {
- if (this->verb() != SkPath::kLine_Verb) {
- return;
- }
+// commented-out lines keep this in sync with ClearVisited
+void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
+ // reset visited flag back to false
+ do {
+ const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ const SkOpSegment* opp = ptT->segment();
+ opp->resetDebugVisited();
+ }
+ } while (!span->final() && (span = span->upCast()->next()));
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
+// commented-out lines keep this in sync with missingCoincidence()
+// look for pairs of undetected coincident curves
+// assumes that segments going in have visited flag clear
+// Even though pairs of curves correct detect coincident runs, a run may be missed
+// if the coincidence is a product of multiple intersections. For instance, given
+// curves A, B, and C:
+// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
+// the end of C that the intersection is replaced with the end of C.
+// Even though A-B correctly do not detect an intersection at point 2,
+// the resulting run from point 1 to point 2 is coincident on A and B.
+void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
if (this->done()) {
return;
}
const SkOpSpan* prior = nullptr;
const SkOpSpanBase* spanBase = &fHead;
+// bool result = false;
do {
const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase);
@@ -624,29 +748,29 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
if (ptT->deleted()) {
continue;
}
- SkOpSegment* opp = ptT->span()->segment();
-// if (opp->verb() == SkPath::kLine_Verb) {
-// continue;
-// }
+ const SkOpSegment* opp = ptT->span()->segment();
if (opp->done()) {
continue;
}
// when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
- if (!opp->visited()) {
+ if (!opp->debugVisited()) {
continue;
}
if (spanBase == &fHead) {
continue;
}
+ if (ptT->segment() == this) {
+ continue;
+ }
const SkOpSpan* span = spanBase->upCastable();
// FIXME?: this assumes that if the opposite segment is coincident then no more
// coincidence needs to be detected. This may not be true.
- if (span && span->segment() != opp && span->containsCoincidence(opp)) {
+ if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
continue;
}
- if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) {
+ if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
continue;
- }
+ }
const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
// find prior span containing opp segment
const SkOpSegment* priorOpp = nullptr;
@@ -669,6 +793,9 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
if (!priorOpp) {
continue;
}
+ if (priorPtT == ptT) {
+ continue;
+ }
const SkOpPtT* oppStart = prior->ptT();
const SkOpPtT* oppEnd = spanBase->ptT();
bool swapped = priorPtT->fT > ptT->fT;
@@ -676,22 +803,28 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
SkTSwap(priorPtT, ptT);
SkTSwap(oppStart, oppEnd);
}
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident = false;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+ const SkOpCoincidence* coincidence = this->globalState()->coincidence();
+ const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
+ const SkOpPtT* rootPtT = ptT->span()->ptT();
+ const SkOpPtT* rootOppStart = oppStart->span()->ptT();
+ const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
+ if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
goto swapBack;
}
- if (opp->verb() == SkPath::kLine_Verb) {
- coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
- (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
- }
- if (!coincident) {
- coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
- }
- if (coincident) {
+ if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
+ // mark coincidence
+#if DEBUG_COINCIDENCE
+// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
+// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
+// rootOppEnd->debugID());
+#endif
log->record(kMissingCoin_Glitch, id, priorPtT, ptT, oppStart, oppEnd);
+ // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
+ // }
+#if DEBUG_COINCIDENCE
+// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)
+#endif
+ // result = true;
}
swapBack:
if (swapped) {
@@ -699,9 +832,14 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
+ DebugClearVisited(&fHead);
+ return;
}
+// commented-out lines keep this in sync with moveMultiples()
+// if a span has more than one intersection, merge the other segments' span as needed
void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ debugValidate();
const SkOpSpanBase* test = &fHead;
do {
int addCount = test->spanAddsCount();
@@ -777,6 +915,7 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog*
} while ((matchPtT = matchPtT->next()) != startPtT);
goto tryNextSpan;
foundMatch: // merge oppTest and oppSpan
+ oppSegment->debugValidate();
if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
SkASSERT(oppSpan != &oppSegment->fTail);
@@ -784,60 +923,67 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog*
} else {
glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppTest);
}
+ oppSegment->debugValidate();
goto checkNextSpan;
}
- tryNextSpan:
+ tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
-checkNextSpan:
+checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
+ debugValidate();
+ return;
}
+// commented-out lines keep this in sync with moveNearby()
+// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
void SkOpSegment::debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- const SkOpSpanBase* spanS = &fHead;
+ debugValidate();
+ // release undeleted spans pointing to this seg that are linked to the primary span
+ const SkOpSpanBase* spanBase = &fHead;
do {
- const SkOpSpanBase* test = spanS->upCast()->next();
- const SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- glitches->record(kUndetachedSpan_Glitch, id, test, spanS);
- } else if (spanS != &fHead) {
- glitches->record(kUndetachedSpan_Glitch, id, spanS, test);
+ const SkOpPtT* ptT = spanBase->ptT();
+ const SkOpPtT* headPtT = ptT;
+ while ((ptT = ptT->next()) != headPtT) {
+ const SkOpSpanBase* test = ptT->span();
+ if (ptT->segment() == this && !ptT->deleted() && test != spanBase
+ && test->ptT() == ptT) {
+ if (test->final()) {
+ if (spanBase == &fHead) {
+ glitches->record(kMoveNearbyClearAll_Glitch, id, this);
+// return;
+ }
+ glitches->record(kMoveNearbyReleaseFinal_Glitch, id, spanBase, ptT);
+ } else if (test->prev()) {
+ glitches->record(kMoveNearbyRelease_Glitch, id, test, headPtT);
+ }
+// break;
}
}
- do { // iterate through all spans associated with start
- const SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? nullptr : test->upCast()->next();
- do {
- const SkOpPtT* testBase = test->ptT();
- do {
- if (startBase == testBase) {
- goto checkNextSpan;
- }
- if (testBase->duplicate()) {
- continue;
- }
- if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
- if (test == &this->fTail) {
- if (spanS == &fHead) {
- glitches->record(kCollapsedSpan_Glitch, id, spanS);
- } else {
- glitches->record(kUnmergedSpan_Glitch, id, &this->fTail, spanS);
- }
- } else {
- glitches->record(kUnmergedSpan_Glitch, id, spanS, test);
- goto checkNextSpan;
- }
- }
- } while ((testBase = testBase->next()) != test->ptT());
- } while ((startBase = startBase->next()) != spanS->ptT());
- checkNextSpan:
- ;
- } while ((test = next));
- spanS = spanS->upCast()->next();
- } while (!spanS->final());
+ spanBase = spanBase->upCast()->next();
+ } while (!spanBase->final());
+
+ // This loop looks for adjacent spans which are near by
+ spanBase = &fHead;
+ do { // iterate through all spans associated with start
+ const SkOpSpanBase* test = spanBase->upCast()->next();
+ if (this->spansNearby(spanBase, test)) {
+ if (test->final()) {
+ if (spanBase->prev()) {
+ glitches->record(kMoveNearbyMergeFinal_Glitch, id, test);
+ } else {
+ glitches->record(kMoveNearbyClearAll2_Glitch, id, this);
+ // return
+ }
+ } else {
+ glitches->record(kMoveNearbyMerge_Glitch, id, spanBase);
+ }
+ }
+ spanBase = test;
+ } while (!spanBase->final());
+ debugValidate();
}
#endif
@@ -864,16 +1010,18 @@ void SkOpSegment::debugShowActiveSpans() const {
lastId = this->debugID();
lastT = span->t();
SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+ // since endpoints may have be adjusted, show actual computed curves
+ SkDCurve curvePart;
+ this->subDivide(span, span->next(), &curvePart);
+ const SkDPoint* pts = curvePart.fCubic.fPts;
+ SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+ SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
}
if (SkPath::kConic_Verb == fVerb) {
- SkDebugf(" %1.9gf", fWeight);
+ SkDebugf(" %1.9gf", curvePart.fConic.fWeight);
}
- const SkOpPtT* ptT = span->ptT();
- SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
- SkDebugf(" tEnd=%1.9g", span->next()->t());
+ SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
if (span->windSum() == SK_MinS32) {
SkDebugf(" windSum=?");
} else {
@@ -958,7 +1106,7 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int
// loop looking for a pair of angle parts that are too close to be sorted
/* This is called after other more simple intersection and angle sorting tests have been exhausted.
This should be rarely called -- the test below is thorough and time consuming.
- This checks the distance between start points; the distance between
+ This checks the distance between start points; the distance between
*/
#if DEBUG_ANGLE
void SkOpAngle::debugCheckNearCoincidence() const {
@@ -996,7 +1144,7 @@ void SkOpAngle::debugCheckNearCoincidence() const {
SkDebugf("\n");
}
test = test->fNext;
- } while (test->fNext != this);
+ } while (test->fNext != this);
}
#endif
@@ -1046,6 +1194,11 @@ void SkOpAngle::debugLoop() const {
#endif
void SkOpAngle::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpAngle* first = this;
const SkOpAngle* next = this;
@@ -1108,243 +1261,409 @@ void SkOpAngle::debugValidateNext() const {
#endif
}
+#ifdef SK_DEBUG
+void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
+ const SkOpGlobalState* debugState) const {
+ SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail());
+ SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail());
+}
+#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
+/* Commented-out lines keep this in sync with expand */
+bool SkCoincidentSpans::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ bool expanded = false;
+ const SkOpSegment* segment = coinPtTStart()->segment();
+ const SkOpSegment* oppSegment = oppPtTStart()->segment();
+ do {
+ const SkOpSpan* start = coinPtTStart()->span()->upCast();
+ const SkOpSpan* prev = start->prev();
+ const SkOpPtT* oppPtT;
+ if (!prev || !(oppPtT = prev->contains(oppSegment))) {
+ break;
+ }
+ double midT = (prev->t() + start->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ if (log) log->record(kExpandCoin_Glitch, id, this, prev->ptT(), oppPtT);
+ expanded = true;
+ } while (false); // actual continues while expansion is possible
+ do {
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
+ const SkOpPtT* oppPtT;
+ if (!next || !(oppPtT = next->contains(oppSegment))) {
+ break;
+ }
+ double midT = (end->t() + next->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ if (log) log->record(kExpandCoin_Glitch, id, this, next->ptT(), oppPtT);
+ expanded = true;
+ } while (false); // actual continues while expansion is possible
+ return expanded;
+}
+
+#define FAIL_IF(cond) do { if (cond) log->record(kAddExpandedFail_Glitch, id, coin); } while (false)
+
+/* Commented-out lines keep this in sync with addExpanded */
+// for each coincident pair, match the spans
+// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* log) const {
- // for each coincident pair, match the spans
- // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
const SkCoincidentSpans* coin = this->fHead;
if (!coin) {
- coin = this->fTop;
- }
- if (!coin) {
return;
}
do {
- const SkOpPtT* startPtT = coin->fCoinPtTStart;
- const SkOpPtT* oStartPtT = coin->fOppPtTStart;
+ const SkOpPtT* startPtT = coin->coinPtTStart();
+ const SkOpPtT* oStartPtT = coin->oppPtTStart();
SkASSERT(startPtT->contains(oStartPtT));
- SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
+ SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
const SkOpSpanBase* start = startPtT->span();
const SkOpSpanBase* oStart = oStartPtT->span();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
+ const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
+ FAIL_IF(oEnd->deleted());
const SkOpSpanBase* test = start->upCast()->next();
- const SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
+ const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
+ if (!oTest) {
+ return;
+ }
while (test != end || oTest != oEnd) {
- bool bumpTest = true;
- bool bumpOTest = true;
- if (!test->ptT()->contains(oTest->ptT())) {
+ if (!test->ptT()->contains(oTest->segment())
+ || !oTest->ptT()->contains(start->segment())) {
// use t ranges to guess which one is missing
- double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
+ double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
+ FAIL_IF(!startRange);
double startPart = (test->t() - startPtT->fT) / startRange;
- double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
+ double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
+ FAIL_IF(!oStartRange);
double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
- if (startPart == oStartPart) {
- // data is corrupt
- log->record(kAddCorruptCoin_Glitch, id, start, oStart);
- break;
+ FAIL_IF(startPart == oStartPart);
+ bool startOver = false;
+ if (startPart < oStartPart)
+ log->record(kAddExpandedCoin_Glitch, id, // strange debug formatting lines up with original
+ oStartPtT->fT + oStartRange * startPart, test);
+ else log->record(kAddExpandedCoin_Glitch, id,
+ startPtT->fT + startRange * oStartPart, oTest);
+ if (false) {
+ SkASSERT(0);
+ return;
}
- if (startPart < oStartPart) {
- double newT = oStartPtT->fT + oStartRange * startPart;
- log->record(kAddExpandedCoin_Glitch, id, oStart, newT, test->pt());
- bumpOTest = false;
- } else {
- double newT = startPtT->fT + startRange * oStartPart;
- log->record(kAddExpandedCoin_Glitch, id, start, newT, oTest->pt());
- bumpTest = false;
+ if (startOver) {
+ test = start;
+ oTest = oStart;
}
}
- if (bumpTest && test != end) {
+ if (test != end) {
test = test->upCast()->next();
}
- if (bumpOTest && oTest != oEnd) {
- oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
+ if (oTest != oEnd) {
+ oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
+ if (!oTest) {
+ return;
+ }
}
}
- } while ((coin = coin->fNext));
-}
-
-static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
- const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
- double denom = overE->fT - overS->fT;
- double start = 0 < denom ? tStart : tEnd;
- double end = 0 < denom ? tEnd : tStart;
- double sRatio = (start - overS->fT) / denom;
- double eRatio = (end - overS->fT) / denom;
- *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
- *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
-}
-
-bool SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
- const SkOpPtT* over1e) const {
- const SkCoincidentSpans* check = this->fTop;
- while (check) {
- if (check->fCoinPtTStart->span() == over1s->span()
- && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
- || !fDebugState->debugRunFail());
- return false;
- }
- if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
- && check->fOppPtTStart->span() == over1s->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- return false;
- }
- check = check->fNext;
+ } while ((coin = coin->next()));
+ return;
+}
+
+/* Commented-out lines keep this in sync with addIfMissing() */
+void SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// SkASSERT(fTop);
+ if (fTop && alreadyAdded(fTop, outer, over1s, over1e)) { // in debug, fTop may be null
+ return;
}
- return true;
+ if (fHead && alreadyAdded(fHead, outer, over1s, over1e)) {
+ return;
+ }
+ log->record(kAddIfMissingCoin_Glitch, id, outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e);
+ this->debugValidate();
+ return;
}
-bool SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
- const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
- SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
+/* Commented-out lines keep this in sync addIfMissing() */
+void SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, const char* id, SkPathOpsDebug::GlitchLog* log) const {
double coinTs, coinTe, oppTs, oppTe;
- t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
- t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
- const SkOpSegment* coinSeg = coinPtTStart->segment();
- const SkOpSegment* oppSeg = oppPtTStart->segment();
- SkASSERT(coinSeg != oppSeg);
- const SkCoincidentSpans* check = this->fTop;
- ;
- while (check) {
- const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
- const SkOpSegment* checkOppSeg;
- if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
- goto next;
- }
- checkOppSeg = check->fOppPtTStart->segment();
- if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
- goto next;
- }
- {
- int cTs = coinTs;
- int cTe = coinTe;
- int oTs = oppTs;
- int oTe = oppTe;
- if (checkCoinSeg != coinSeg) {
- SkASSERT(checkOppSeg != oppSeg);
- SkTSwap(cTs, oTs);
- SkTSwap(cTe, oTe);
- }
- int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
- + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
- // SkASSERT(tweenCount == 0 || tweenCount == 4);
- if (tweenCount) {
- return true;
- }
- }
-next:
- check = check->fNext;
+ TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
+ TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ bool swap = coinTs > coinTe;
+ if (swap) {
+ SkTSwap(coinTs, coinTe);
}
if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
SkTSwap(oppTs, oppTe);
}
- if (coinTs > coinTe) {
- SkTSwap(coinTs, coinTe);
+ if (swap) {
SkTSwap(oppTs, oppTe);
}
- bool cs = coinSeg->debugAddMissing(coinTs, oppSeg);
- bool ce = coinSeg->debugAddMissing(coinTe, oppSeg);
- if (cs == ce) {
- return false;
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (coinSeg == oppSeg) {
+ return;
}
- return true;
+ return this->debugAddOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, id, log);
+}
+
+/* Commented-out lines keep this in sync addOrOverlap() */
+void SkOpCoincidence::debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe, const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ SkTDArray<SkCoincidentSpans*> overlaps;
+ SkASSERT(!fTop); // this is (correctly) reversed in addifMissing()
+ if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
+ return;
+ }
+ if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
+ coinTe, oppTs, oppTe, &overlaps)) {
+ return;
+ }
+ const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
+ for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
+ const SkCoincidentSpans* test = overlaps[index];
+ if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTStart());
+ }
+ if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTEnd());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
+ : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTStart());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
+ : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTEnd());
+ }
+ if (!fHead) {
+ SkAssertResult(true);
+ }
+ }
+ const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
+ const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
+ if (overlap && cs && ce && overlap->contains(cs, ce)) {
+ return;
+ }
+ SkASSERT(cs != ce || !cs);
+ const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
+ const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
+ if (overlap && os && oe && overlap->contains(os, oe)) {
+ return;
+ }
+ SkASSERT(true || !cs || !cs->deleted());
+ SkASSERT(true || !os || !os->deleted());
+ SkASSERT(true || !ce || !ce->deleted());
+ SkASSERT(true || !oe || !oe->deleted());
+ const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
+ const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
+ if (csExisting && csExisting == ceExisting) {
+ return;
+ }
+ if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
+ return;
+ }
+ if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
+ return;
+ }
+ const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
+ const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
+ if (osExisting && osExisting == oeExisting) {
+ return;
+ }
+ if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
+ return;
+ }
+ if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
+ return;
+ }
+ bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
+ this->debugValidate();
+ if (!cs || !os) {
+ if (!cs)
+ cs = coinSeg->debugAddT(coinTs, SkOpSegment::kNoAliasMatch, nullptr);
+ if (!os)
+ os = oppSeg->debugAddT(oppTs, SkOpSegment::kNoAliasMatch, nullptr);
+ if (cs && os) cs->span()->debugAddOppAndMerge(id, log, os->span(), &csDeleted, &osDeleted);
+// cs = csWritable;
+// os = osWritable;
+ if ((ce && ce->deleted()) || (oe && oe->deleted())) {
+ return;
+ }
+ }
+ if (!ce || !oe) {
+ if (!ce)
+ ce = coinSeg->debugAddT(coinTe, SkOpSegment::kNoAliasMatch, nullptr);
+ if (!oe)
+ oe = oppSeg->debugAddT(oppTe, SkOpSegment::kNoAliasMatch, nullptr);
+ if (ce && oe) ce->span()->debugAddOppAndMerge(id, log, oe->span(), &ceDeleted, &oeDeleted);
+// ce = ceWritable;
+// oe = oeWritable;
+ }
+ this->debugValidate();
+ if (csDeleted || osDeleted || ceDeleted || oeDeleted) {
+ return;
+ }
+ if (!cs || !ce || cs->contains(ce) || !os || !oe || os->contains(oe)) {
+ return;
+ }
+// bool result = true;
+ if (overlap) {
+ if (overlap->coinPtTStart()->segment() == coinSeg) {
+ log->record(kAddMissingExtend_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
+ } else {
+ if (oppTs > oppTe) {
+ SkTSwap(coinTs, coinTe);
+ SkTSwap(oppTs, oppTe);
+ }
+ log->record(kAddMissingExtend_Glitch, id, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
+ }
+#if DEBUG_COINCIDENCE_VERBOSE
+// if (result) {
+// overlap->debugShow();
+// }
+#endif
+ } else {
+ log->record(kAddMissingCoin_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
+#if DEBUG_COINCIDENCE_VERBOSE
+// fHead->debugShow();
+#endif
+ }
+ this->debugValidate();
+ return;
}
+// Extra commented-out lines keep this in sync with addMissing()
+/* detects overlaps of different coincident runs on same segment */
+/* does not detect overlaps for pairs without any segments in common */
+// returns true if caller should loop again
void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* outer = fHead;
if (!outer) {
return;
}
+ // bool added = false;
+ // fTop = outer;
+ // fHead = nullptr;
do {
// addifmissing can modify the list that this is walking
// save head so that walker can iterate over old data unperturbed
// addifmissing adds to head freely then add saved head in the end
- const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
- const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
- SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
const SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
+ while ((inner = inner->next())) {
+ this->debugValidate();
double overS, overE;
- const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
- SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
- const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
- if (outerCoin == innerCoin
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ if (outerCoin == innerCoin) {
+ if (outerOpp != innerOpp
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
}
- } else if (outerCoin == innerOpp
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
+ } else if (outerCoin == innerOpp) {
+ if (outerOpp != innerCoin
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
}
- } else if (outerOpp == innerCoin
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
+ } else if (outerOpp == innerCoin) {
+ SkASSERT(outerCoin != innerOpp);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
}
- } else if (outerOpp == innerOpp
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
- }
- } else if (outerCoin != innerCoin) {
- // check to see if outer span overlaps the inner span
- // look for inner segment in pt-t list
- // if present, and if t values are in coincident range
- // add two pairs of new coincidence
- const SkOpPtT* testS = outer->fCoinPtTStart->debugContains(innerCoin);
- const SkOpPtT* testE = outer->fCoinPtTEnd->debugContains(innerCoin);
- if (testS && testS->fT >= inner->fCoinPtTStart->fT
- && testE && testE->fT <= inner->fCoinPtTEnd->fT
- && this->testForCoincidence(outer, testS, testE)) {
- if (this->debugAddIfMissing(outer, testS, testE)) {
- log->record(kAddMissingCoin_Glitch, id, outer, testS, testE);
- }
- } else {
- testS = inner->fCoinPtTStart->debugContains(outerCoin);
- testE = inner->fCoinPtTEnd->debugContains(outerCoin);
- if (testS && testS->fT >= outer->fCoinPtTStart->fT
- && testE && testE->fT <= outer->fCoinPtTEnd->fT
- && this->testForCoincidence(inner, testS, testE)) {
- if (this->debugAddIfMissing(inner, testS, testE)) {
- log->record(kAddMissingCoin_Glitch, id, inner, testS, testE);
- }
- }
+ } else if (outerOpp == innerOpp) {
+ SkASSERT(outerCoin != innerCoin);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
}
}
+ this->debugValidate();
+ }
+ } while ((outer = outer->next()));
+ // this->restoreHead();
+ return;
+}
+
+// Commented-out lines keep this in sync with release()
+void SkOpCoincidence::debugRelease(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
+ const SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == deleted
+ || coin->coinPtTEnd()->segment() == deleted
+ || coin->oppPtTStart()->segment() == deleted
+ || coin->oppPtTEnd()->segment() == deleted) {
+ log->record(kReleasedSpan_Glitch, id, coin);
+ }
+ } while ((coin = coin->next()));
+}
+
+// Commented-out lines keep this in sync with reorder()
+// iterate through all coincident pairs, looking for ranges greater than 1
+// if found, see if the opposite pair can match it -- which may require
+// reordering the ptT pairs
+void SkOpCoincidence::debugReorder(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ const SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ // most commonly, concidence are one span long; check for that first
+ int intervals = coin->spanCount();
+ if (intervals = 1) {
+#if DEBUG_COINCIDENCE_VERBOSE
+ // SkASSERT(!coin->debugExpand(nullptr, nullptr));
+#endif
+ continue;
+ }
+ coin->debugExpand(id, log);
+ if (coin->spanCount() <= 0) {
+ return;
}
- } while ((outer = outer->fNext));
+ // check to see if every span in coin has a mate in opp
+ const SkOpSpan* start = coin->coinPtTStart()->span()->upCast();
+ bool flipped = coin->flipped();
+ const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span();
+ const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast();
+ SkDebugf("", start, oppStart);
+ } while ((coin = coin->next()));
+ return;
}
+// Commented-out lines keep this in sync with expand()
+// expand the range by checking adjacent spans for coincidence
bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
@@ -1352,109 +1671,296 @@ bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log
}
bool expanded = false;
do {
- const SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSegment* segment = coin->fCoinPtTStart->segment();
- const SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
- const SkOpSpan* prev = start->prev();
- if (prev && prev->debugContains(oppSegment)) {
- double midT = (prev->t() + start->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- log->record(kExpandCoin_Glitch, id, coin, prev);
- }
- }
- SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
- if (next && next->debugContains(oppSegment)) {
- double midT = (end->t() + next->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- log->record(kExpandCoin_Glitch, id, coin, next);
- }
+ if (coin->debugExpand(id, log)) {
+ // check to see if multiple spans expanded so they are now identical
+ const SkCoincidentSpans* test = fHead;
+ do {
+ if (coin == test) {
+ continue;
+ }
+ if (coin->coinPtTStart() == test->coinPtTStart()
+ && coin->oppPtTStart() == test->oppPtTStart()) {
+ if (log) log->record(kExpandCoin_Glitch, id, fHead, test->coinPtTStart());
+ break;
+ }
+ } while ((test = test->next()));
+ expanded = true;
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return expanded;
}
-void SkOpCoincidence::debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// Commented-out lines keep this in sync with removeCollapsed()
+void SkOpCoincidence::debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
+ // SkCoincidentSpans** priorPtr = &fHead;
do {
- if (coin->fCoinPtTStart->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTStart);
- }
- if (coin->fCoinPtTEnd->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTEnd);
- }
- if (coin->fOppPtTStart->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTStart);
+ if (coin->coinPtTStart() == coin->coinPtTEnd()) {
+ return;
}
- if (coin->fOppPtTEnd->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTEnd);
+ if (coin->oppPtTStart() == coin->oppPtTEnd()) {
+ return;
}
- } while ((coin = coin->fNext));
- coin = fHead;
- do {
- if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)) {
- log->record(kCollapsedCoin_Glitch, id, coin, coin->fCoinPtTStart);
+ if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
+// continue;
}
- if (coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
- log->record(kCollapsedCoin_Glitch, id, coin, coin->fOppPtTStart);
+ if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) {
+ log->record(kCollapsedCoin_Glitch, id, coin, coin);
+// continue;
}
- } while ((coin = coin->fNext));
+ // priorPtr = &coin->nextPtr();
+ } while ((coin = coin->next()));
+ return;
}
+// Commented-out lines keep this in sync with mark()
+/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
void SkOpCoincidence::debugMark(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oldEnd = end;
- const SkOpSpan* start = coin->fCoinPtTStart->span()->debugStarter(&end);
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- const SkOpSpanBase* oOldEnd = oEnd;
- const SkOpSpanBase* oStart = coin->fOppPtTStart->span()->debugStarter(&oEnd);
- bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
+ const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
+// SkASSERT(start->deleted());
+ const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
+// SkASSERT(end->deleted());
+ const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
+// SkASSERT(oStart->deleted());
+ const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
+// SkASSERT(oEnd->deleted());
+ bool flipped = coin->flipped();
if (flipped) {
SkTSwap(oStart, oEnd);
}
+ /* coin and opp spans may not match up. Mark the ends, and then let the interior
+ get marked as many times as the spans allow */
+ start->debugInsertCoincidence(id, log, oStart->upCast());
+ end->debugInsertCoinEnd(id, log, oEnd);
+ const SkOpSegment* segment = start->segment();
+ const SkOpSegment* oSegment = oStart->segment();
const SkOpSpanBase* next = start;
const SkOpSpanBase* oNext = oStart;
- do {
- next = next->upCast()->next();
- oNext = flipped ? oNext->prev() : oNext->upCast()->next();
- if (next == end || oNext == oEnd) {
- break;
+ while ((next = next->upCast()->next()) != end) {
+ if (next->upCast()->debugInsertCoincidence(id, log, oSegment, flipped), false) {
+ return;
}
- if (!next->containsCoinEnd(oNext)) {
- log->record(kMarkCoinEnd_Glitch, id, next, oNext);
+ }
+ while ((oNext = oNext->upCast()->next()) != oEnd) {
+ if (oNext->upCast()->debugInsertCoincidence(id, log, segment, flipped), false) {
+ return;
}
- const SkOpSpan* nextSpan = next->upCast();
- const SkOpSpan* oNextSpan = oNext->upCast();
- if (!nextSpan->containsCoincidence(oNextSpan)) {
- log->record(kMarkCoinInsert_Glitch, id, nextSpan, oNextSpan);
+ }
+ } while ((coin = coin->next()));
+ return;
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with markCollapsed()
+void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
+ while (coin) {
+ if (coin->collapsed(test)) {
+ if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
}
- } while (true);
- } while ((coin = coin->fNext));
+ if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
+ }
+ }
+ coin = coin->next();
+ }
+}
+
+// Commented-out lines keep this in sync with markCollapsed()
+void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
+ this->debugMarkCollapsed(id, log, fHead, test);
+ this->debugMarkCollapsed(id, log, fTop, test);
}
#endif
+void SkCoincidentSpans::debugShow() const {
+ SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+ coinPtTStart()->segment()->debugID(),
+ coinPtTStart()->fT, coinPtTEnd()->fT);
+ SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+ oppPtTStart()->segment()->debugID(),
+ oppPtTStart()->fT, oppPtTEnd()->fT);
+}
+
void SkOpCoincidence::debugShowCoincidence() const {
- SkCoincidentSpans* span = fHead;
+#if DEBUG_COINCIDENCE
+ const SkCoincidentSpans* span = fHead;
while (span) {
- SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
- span->fCoinPtTStart->segment()->debugID(),
- span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
- SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
- span->fOppPtTStart->segment()->debugID(),
- span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
- span = span->fNext;
+ span->debugShow();
+ span = span->next();
}
+#endif
}
#if DEBUG_COINCIDENCE
+static void DebugValidate(const SkOpSpanBase* next, const SkOpSpanBase* end,
+ double oStart, double oEnd, const SkOpSegment* oSegment,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ SkASSERT(next != end);
+ SkASSERT(!next->contains(end) || log);
+ if (next->t() > end->t()) {
+ SkTSwap(next, end);
+ }
+ do {
+ const SkOpPtT* ptT = next->ptT();
+ int index = 0;
+ bool somethingBetween;
+ do {
+ ++index;
+ ptT = ptT->next();
+ const SkOpPtT* checkPtT = next->ptT();
+ if (ptT == checkPtT) {
+ break;
+ }
+ bool looped = false;
+ for (int check = 0; check < index; ++check) {
+ if ((looped = checkPtT == ptT)) {
+ break;
+ }
+ checkPtT = checkPtT->next();
+ }
+ if (looped) {
+ SkASSERT(0);
+ break;
+ }
+ if (ptT->deleted()) {
+ continue;
+ }
+ if (ptT->segment() != oSegment) {
+ continue;
+ }
+ somethingBetween |= between(oStart, ptT->fT, oEnd);
+ } while (true);
+ SkASSERT(somethingBetween);
+ } while (next != end && (next = next->upCast()->next()));
+}
+
+static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ if (!list) {
+ return;
+ }
+ const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
+ SkASSERT(coinSeg == test->coinPtTEnd()->segment());
+ const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
+ SkASSERT(oppSeg == test->oppPtTEnd()->segment());
+ SkASSERT(coinSeg != test->oppPtTStart()->segment());
+ SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
+ SkASSERT(between(0, tcs, 1));
+ SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
+ SkASSERT(between(0, tce, 1));
+ SkASSERT(tcs < tce);
+ double tos = test->oppPtTStart()->fT;
+ SkASSERT(between(0, tos, 1));
+ double toe = test->oppPtTEnd()->fT;
+ SkASSERT(between(0, toe, 1));
+ SkASSERT(tos != toe);
+ if (tos > toe) {
+ SkTSwap(tos, toe);
+ }
+ do {
+ double lcs, lce, los, loe;
+ if (coinSeg == list->coinPtTStart()->segment()) {
+ if (oppSeg != list->oppPtTStart()->segment()) {
+ continue;
+ }
+ lcs = list->coinPtTStart()->fT;
+ lce = list->coinPtTEnd()->fT;
+ los = list->oppPtTStart()->fT;
+ loe = list->oppPtTEnd()->fT;
+ if (los > loe) {
+ SkTSwap(los, loe);
+ }
+ } else if (coinSeg == list->oppPtTStart()->segment()) {
+ if (oppSeg != list->coinPtTStart()->segment()) {
+ continue;
+ }
+ lcs = list->oppPtTStart()->fT;
+ lce = list->oppPtTEnd()->fT;
+ if (lcs > lce) {
+ SkTSwap(lcs, lce);
+ }
+ los = list->coinPtTStart()->fT;
+ loe = list->coinPtTEnd()->fT;
+ } else {
+ continue;
+ }
+ SkASSERT(tce < lcs || lce < tcs);
+ SkASSERT(toe < los || loe < tos);
+ } while ((list = list->next()));
+}
+
+
+static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ // check for overlapping coincident spans
+ const SkCoincidentSpans* test = head;
+ while (test) {
+ const SkCoincidentSpans* next = test->next();
+ DebugCheckOverlap(test, next, id, log);
+ DebugCheckOverlap(test, opt, id, log);
+ test = next;
+ }
+}
+
+#if DEBUG_COINCIDENCE_VERBOSE
+void SkOpCoincidence::debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ DebugCheckOverlapTop(fHead, fTop, id, log);
+ DebugCheckOverlapTop(fTop, nullptr, id, log);
+}
+#endif
+
+static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ // look for pts inside coincident spans that are not inside the opposite spans
+ const SkCoincidentSpans* coin = head;
+ while (coin) {
+ SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
+ coin->oppPtTStart()->segment()));
+ SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
+ SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
+ SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
+ SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
+ DebugValidate(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
+ coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
+ id, log);
+ DebugValidate(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
+ coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
+ id, log);
+ coin = coin->next();
+ }
+ DebugCheckOverlapTop(head, opt, id, log);
+}
+#endif
+
+void SkOpCoincidence::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ // if (fGlobalState->debugCheckHealth()) {
+// return;
+// }
+ DebugValidate(fHead, fTop, nullptr, nullptr);
+ DebugValidate(fTop, nullptr, nullptr, nullptr);
+#endif
+}
+
+#if DEBUG_COINCIDENCE_VERBOSE
+void SkOpCoincidence::debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ DebugValidate(fHead, fTop, id, log);
+ DebugValidate(fTop, nullptr, id, log);
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkOpSegment* segment = &fHead;
do {
@@ -1462,16 +1968,36 @@ void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* lo
} while ((segment = segment->next()));
}
-void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidence) const {
+// commmented-out lines keep this aligned with missingCoincidence()
+void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
+// bool result = false;
do {
- segment->debugMissingCoincidence(id, log, coincidence);
- } while ((segment = segment->next()));
+ if (fState->angleCoincidence()) {
+// #if DEBUG_ANGLE
+// segment->debugCheckAngleCoin();
+// #endif
+ } else if (segment->debugMissingCoincidence(id, log), false) {
+// result = true;
+// see FIXME in missingCoincidence()
+//
+//
+//
+ // continue;
+ }
+ segment = segment->next();
+ } while (segment);
+ return;
}
#endif
void SkOpSegment::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpSpanBase* span = &fHead;
double lastT = -1;
@@ -1500,56 +2026,47 @@ void SkOpSegment::debugValidate() const {
#endif
}
-bool SkOpSpanBase::debugAlignedEnd(double t, const SkPoint& pt) const {
- SkASSERT(zero_or_one(t));
- const SkOpSegment* segment = this->segment();
- SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
- if (!debugAlignedInner()) {
- return false;
- }
- if ((t ? segment->lastPt() : segment->pts()[0]) != pt) {
- return false;
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with addOppAndMerge()
+// If the added points envelop adjacent spans, merge them in.
+void SkOpSpanBase::debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp, bool* spanDeleted, bool* oppDeleted) const {
+ if (this->ptT()->debugAddOpp(opp->ptT())) {
+ this->debugCheckForCollapsedCoincidence(id, log);
}
- const SkOpPtT* ptT = &this->fPtT;
- SkASSERT(t == ptT->fT);
- SkASSERT(pt == ptT->fPt);
- const SkOpPtT* test = ptT, * stopPtT = ptT;
- while ((test = test->next()) != stopPtT) {
- const SkOpSegment* other = test->segment();
- if (other == this->segment()) {
- continue;
- }
- if (!zero_or_one(test->fT)) {
- continue;
- }
- if ((test->fT ? other->lastPt() : other->pts()[0]) != pt) {
- return false;
- }
+ // compute bounds of points in span
+ SkPathOpsBounds bounds;
+ bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* nextPt = head;
+ do {
+ bounds.add(nextPt->fPt);
+ } while ((nextPt = nextPt->next()) != head);
+ if (!bounds.width() && !bounds.height()) {
+ return;
}
- return this->fAligned;
+ this->debugMergeContained(id, log, bounds, spanDeleted);
+ opp->debugMergeContained(id, log, bounds, oppDeleted);
}
-bool SkOpSpanBase::debugAlignedInner() const {
- // force the spans to share points and t values
- const SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- const SkPoint& pt = ptT->fPt;
+// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
+void SkOpSpanBase::debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ const SkOpCoincidence* coins = this->globalState()->coincidence();
+ if (coins->isEmpty()) {
+ return;
+ }
+// the insert above may have put both ends of a coincident run in the same span
+// for each coincident ptT in loop; see if its opposite in is also in the loop
+// this implementation is the motivation for marking that a ptT is referenced by a coincident span
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* test = head;
do {
- if (ptT->fPt != pt) {
- return false;
+ if (!test->coincident()) {
+ continue;
}
- const SkOpSpanBase* span = ptT->span();
- const SkOpPtT* test = ptT;
- do {
- if ((test = test->next()) == stopPtT) {
- break;
- }
- if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
- return false;
- }
- } while (true);
- } while ((ptT = ptT->next()) != stopPtT);
- return true;
+ coins->debugMarkCollapsed(id, log, test);
+ } while ((test = test->next()) != head);
}
+#endif
bool SkOpSpanBase::debugCoinEndLoopCheck() const {
int loop = 0;
@@ -1574,16 +2091,57 @@ bool SkOpSpanBase::debugCoinEndLoopCheck() const {
return true;
}
-bool SkOpSpanBase::debugContains(const SkOpSegment* segment) const {
- const SkOpPtT* start = &fPtT;
- const SkOpPtT* walk = start;
- while ((walk = walk->next()) != start) {
- if (walk->segment() == segment) {
- return true;
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with insertCoinEnd()
+void SkOpSpanBase::debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
+ if (containsCoinEnd(coin)) {
+// SkASSERT(coin->containsCoinEnd(this));
+ return;
+ }
+ debugValidate();
+// SkASSERT(this != coin);
+ log->record(kMarkCoinEnd_Glitch, id, this, coin);
+// coin->fCoinEnd = this->fCoinEnd;
+// this->fCoinEnd = coinNext;
+ debugValidate();
+}
+
+// Commented-out lines keep this in sync with mergeContained()
+void SkOpSpanBase::debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* log, const SkPathOpsBounds& bounds, bool* deleted) const {
+ // while adjacent spans' points are contained by the bounds, merge them
+ const SkOpSpanBase* prev = this;
+ const SkOpSegment* seg = this->segment();
+ while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
+ if (prev->prev()) {
+ log->record(kMergeContained_Glitch, id, this, prev);
+ } else if (this->final()) {
+ log->record(kMergeContained_Glitch, id, this, prev);
+ // return;
+ } else {
+ log->record(kMergeContained_Glitch, id, prev, this);
}
}
- return false;
+ const SkOpSpanBase* current = this;
+ const SkOpSpanBase* next = this;
+ while (next->upCastable() && (next = next->upCast()->next())
+ && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
+ if (!current->prev() && next->final()) {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ }
+ if (current->prev()) {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ } else {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ // this->globalState()->coincidence()->debugValidate();
+#endif
}
+#endif
const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
const SkOpSpanBase* end = *endPtr;
@@ -1599,6 +2157,11 @@ const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
}
void SkOpSpanBase::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpPtT* ptT = &fPtT;
SkASSERT(ptT->span() == this);
@@ -1643,6 +2206,39 @@ bool SkOpSpan::debugCoinLoopCheck() const {
return true;
}
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with insertCoincidence() in header
+void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
+ if (containsCoincidence(coin)) {
+// SkASSERT(coin->containsCoincidence(this));
+ return;
+ }
+ debugValidate();
+// SkASSERT(this != coin);
+ log->record(kMarkCoinStart_Glitch, id, this, coin);
+// coin->fCoincident = this->fCoincident;
+// this->fCoincident = coinNext;
+ debugValidate();
+}
+
+// Commented-out lines keep this in sync with insertCoincidence()
+void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped) const {
+ if (this->containsCoincidence(segment)) {
+ return;
+ }
+ const SkOpPtT* next = &fPtT;
+ while ((next = next->next()) != &fPtT) {
+ if (next->segment() == segment) {
+ log->record(kMarkCoinInsert_Glitch, id, flipped ? next->span()->prev() : next->span());
+ return;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ log->record(kMarkCoinMissing_Glitch, id, segment, this);
+#endif
+}
+#endif
+
// called only by test code
int SkIntersections::debugCoincidentUsed() const {
if (!fIsCoincident[0]) {
@@ -1667,6 +2263,27 @@ int SkIntersections::debugCoincidentUsed() const {
#include "SkOpContour.h"
+// Commented-out lines keep this in sync with addOpp()
+bool SkOpPtT::debugAddOpp(const SkOpPtT* opp) const {
+ // find the fOpp ptr to opp
+ const SkOpPtT* oppPrev = opp->fNext;
+ if (oppPrev == this) {
+ return false;
+ }
+ while (oppPrev->fNext != opp) {
+ oppPrev = oppPrev->fNext;
+ if (oppPrev == this) {
+ return false;
+ }
+ }
+// const SkOpPtT* oldNext = this->fNext;
+ SkASSERT(this != opp);
+// this->fNext = opp;
+// SkASSERT(oppPrev != oldNext);
+// oppPrev->fNext = oldNext;
+ return true;
+}
+
bool SkOpPtT::debugContains(const SkOpPtT* check) const {
SkASSERT(this != check);
const SkOpPtT* ptT = this;
@@ -1736,6 +2353,11 @@ int SkOpPtT::debugLoopLimit(bool report) const {
}
void SkOpPtT::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
SkOpGlobalState::Phase phase = contour()->globalState()->phase();
if (phase == SkOpGlobalState::kIntersecting
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index bd27e7df2d..9e910686b7 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -37,6 +37,8 @@
if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
+#define DEBUG_UNDER_DEVELOPMENT 01
+
#if FORCE_RELEASE
#define DEBUG_ACTIVE_OP 0
@@ -47,6 +49,7 @@
#define DEBUG_ANGLE 0
#define DEBUG_ASSEMBLE 0
#define DEBUG_COINCIDENCE 0
+#define DEBUG_COINCIDENCE_VERBOSE 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 0
#define DEBUG_DUMP_SEGMENTS 0
@@ -74,12 +77,13 @@
#define DEBUG_ALIGNMENT 0
#define DEBUG_ANGLE 1
#define DEBUG_ASSEMBLE 1
-#define DEBUG_COINCIDENCE 0
+#define DEBUG_COINCIDENCE 01
+#define DEBUG_COINCIDENCE_VERBOSE 01
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 1
#define DEBUG_DUMP_SEGMENTS 1
#define DEBUG_FLOW 1
-#define DEBUG_LIMIT_WIND_SUM 5
+#define DEBUG_LIMIT_WIND_SUM 15
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_PERP 1
@@ -186,6 +190,7 @@ public:
static void BumpTestName(char* );
#endif
static const char* OpStr(SkPathOp );
+ static void ShowActiveSpans(SkOpContourHead* contourList);
static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
@@ -193,37 +198,37 @@ public:
static void CheckHealth(class SkOpContourHead* contourList, const char* id);
- static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
- static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
- static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
- static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
- static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
+ static const class SkOpAngle* DebugAngleAngle(const class SkOpAngle*, int id);
+ static class SkOpContour* DebugAngleContour(class SkOpAngle*, int id);
+ static const class SkOpPtT* DebugAnglePtT(const class SkOpAngle*, int id);
+ static const class SkOpSegment* DebugAngleSegment(const class SkOpAngle*, int id);
+ static const class SkOpSpanBase* DebugAngleSpan(const class SkOpAngle*, int id);
- static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
+ static const class SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
- static const struct SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
+ static const class SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
static class SkOpContour* DebugCoincidenceContour(class SkOpCoincidence*, int id);
static const class SkOpPtT* DebugCoincidencePtT(class SkOpCoincidence*, int id);
static const class SkOpSegment* DebugCoincidenceSegment(class SkOpCoincidence*, int id);
static const class SkOpSpanBase* DebugCoincidenceSpan(class SkOpCoincidence*, int id);
- static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
+ static const class SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
- static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
+ static const class SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
- static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
+ static const class SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 1970eea124..188af5722b 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -88,7 +88,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
}
static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
- const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
+ const int xorMask, const int xorOpMask, SkPathWriter* simple) {
bool unsortable = false;
do {
SkOpSpan* span = FindSortableTop(contourList);
@@ -117,11 +117,9 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -163,9 +161,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
}
}
current = findChaseOp(chase, &start, &end);
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
if (!current) {
break;
}
@@ -226,7 +222,7 @@ static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
dump_path(file, two, false, true);
fprintf(file, " SkPath path2(path);\n");
fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
- fprintf(file, "}\n");
+ fprintf(file, "}\n");
fclose(file);
}
#endif
@@ -253,40 +249,42 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpCoincidence coincidence;
- SkOpGlobalState globalState(&coincidence, contourList
- SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpGlobalState globalState(contourList, &allocator
+ SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpCoincidence coincidence(&globalState);
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
-#endif
-#if 0 && DEBUG_SHOW_TEST_NAME
- char* debugName = DEBUG_FILENAME_STRING;
- if (debugName && debugName[0]) {
- SkPathOpsDebug::BumpTestName(debugName);
- SkPathOpsDebug::ShowPath(one, two, op, debugName);
- }
#endif
op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
- const SkPath* minuend = &one;
- const SkPath* subtrahend = &two;
+ SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
+ SkPath scaledOne, scaledTwo;
+ const SkPath* minuend, * subtrahend;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(one, 1.f / scaleFactor, &scaledOne);
+ minuend = &scaledOne;
+ ScalePath(two, 1.f / scaleFactor, &scaledTwo);
+ subtrahend = &scaledTwo;
+ } else {
+ minuend = &one;
+ subtrahend = &two;
+ }
if (op == kReverseDifference_SkPathOp) {
- minuend = &two;
- subtrahend = &one;
+ SkTSwap(minuend, subtrahend);
op = kDifference_SkPathOp;
}
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
- SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
+ SkOpEdgeBuilder builder(*minuend, &contour, &globalState);
if (builder.unparseable()) {
return false;
}
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
- if (!builder.finish(&allocator)) {
+ if (!builder.finish()) {
return false;
}
#if DEBUG_DUMP_SEGMENTS
@@ -304,14 +302,14 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkOpContour* current = contourList;
do {
SkOpContour* next = current;
- while (AddIntersectTs(current, next, &coincidence, &allocator)
+ while (AddIntersectTs(current, next, &coincidence)
&& (next = next->next()))
;
} while ((current = current->next()));
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
- if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
+ if (!HandleCoincidence(contourList, &coincidence)) {
return false;
}
#if DEBUG_ALIGNMENT
@@ -321,7 +319,7 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
result->reset();
result->setFillType(fillType);
SkPathWriter wrapper(*result);
- bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
+ bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);
@@ -339,6 +337,9 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
debugWorstState.debugDoYourWorst(&globalState);
}
#endif
+ if (scaleFactor > 1) {
+ ScalePath(*result, scaleFactor, result);
+ }
return true;
}
@@ -458,6 +459,6 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
}
return true;
#else
- return OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
#endif
}
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index e3f722bede..f30f155e8f 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -58,12 +58,20 @@ struct SkDVector {
}
// similar to cross, this bastardization considers nearly coincident to be zero
+ // uses ulps epsilon == 16
double crossCheck(const SkDVector& a) const {
double xy = fX * a.fY;
double yx = fY * a.fX;
return AlmostEqualUlps(xy, yx) ? 0 : xy - yx;
}
+ // allow tinier numbers
+ double crossNoNormalCheck(const SkDVector& a) const {
+ double xy = fX * a.fY;
+ double yx = fY * a.fX;
+ return AlmostEqualUlpsNoNormalCheck(xy, yx) ? 0 : xy - yx;
+ }
+
double dot(const SkDVector& a) const {
return fX * a.fX + fY * a.fY;
}
@@ -75,6 +83,12 @@ struct SkDVector {
double lengthSquared() const {
return fX * fX + fY * fY;
}
+
+ void normalize() {
+ double inverseLength = 1 / this->length();
+ fX *= inverseLength;
+ fY *= inverseLength;
+ }
};
struct SkDPoint {
@@ -164,7 +178,7 @@ struct SkDPoint {
float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
largest = SkTMax(largest, -tiniest);
- return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+ return AlmostDequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
// only used by testing
diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp
index 3deab21133..dafa3f5b75 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -191,6 +191,12 @@ SkDPoint SkDQuad::ptAtT(double t) const {
}
static double interp_quad_coords(const double* src, double t) {
+ if (0 == t) {
+ return src[0];
+ }
+ if (1 == t) {
+ return src[4];
+ }
double ab = SkDInterp(src[0], src[2], t);
double bc = SkDInterp(src[2], src[4], t);
double abc = SkDInterp(ab, bc, t);
@@ -228,8 +234,11 @@ Group the known values on one side:
B = D*2 - A/2 - C/2
*/
-// OPTIMIZE : special case either or both of t1 = 0, t2 = 1
+// OPTIMIZE? : special case t1 = 1 && t2 = 0
SkDQuad SkDQuad::subDivide(double t1, double t2) const {
+ if (0 == t1 && 1 == t2) {
+ return *this;
+ }
SkDQuad dst;
double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
@@ -263,7 +272,7 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
b = i.pt(0);
} else {
SkASSERT(i.used() <= 2);
- b = SkDPoint::Mid(b0[1], b1[1]);
+ return SkDPoint::Mid(b0[1], b1[1]);
}
if (t1 == 0 || t2 == 0) {
align(0, &b);
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index fa1003054e..dcd75f1666 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -10,8 +10,7 @@
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator, bool* closable) {
+static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
bool unsortable = false;
do {
SkOpSpan* span = FindSortableTop(contourList);
@@ -40,11 +39,9 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -86,9 +83,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
}
}
current = FindChase(&chase, &start, &end);
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
if (!current) {
break;
}
@@ -99,8 +94,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
}
// returns true if all edges were processed
-static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator, bool* closable) {
+static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
SkOpSegment* current;
SkOpSpanBase* start;
SkOpSpanBase* end;
@@ -108,11 +102,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
*closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
do {
- #if DEBUG_ACTIVE_SPANS
if (!unsortable && current->done()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
SkASSERT(unsortable || !current->done());
SkOpSpanBase* nextStart = start;
SkOpSpanBase* nextEnd = end;
@@ -124,11 +116,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -156,16 +146,14 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
*closable = false;
}
simple->close();
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
return true;
}
// FIXME : add this as a member of SkPath
-bool Simplify(const SkPath& path, SkPath* result) {
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+bool SimplifyDebug(const SkPath& path, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
@@ -177,16 +165,26 @@ bool Simplify(const SkPath& path, SkPath* result) {
return true;
}
// turn path into list of segments
- SkOpCoincidence coincidence;
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(false)
- SkDEBUGPARAMS(nullptr));
+ SkOpGlobalState globalState(contourList, &allocator
+ SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpCoincidence coincidence(&globalState);
+ SkScalar scaleFactor = ScaleFactor(path);
+ SkPath scaledPath;
+ const SkPath* workingPath;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(path, 1.f / scaleFactor, &scaledPath);
+ workingPath = &scaledPath;
+ } else {
+ workingPath = &path;
+ }
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ if (!builder.finish()) {
return false;
}
#if DEBUG_DUMP_SEGMENTS
@@ -201,13 +199,13 @@ bool Simplify(const SkPath& path, SkPath* result) {
SkOpContour* current = contourList;
do {
SkOpContour* next = current;
- while (AddIntersectTs(current, next, &coincidence, &allocator)
+ while (AddIntersectTs(current, next, &coincidence)
&& (next = next->next()));
} while ((current = current->next()));
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
- if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
+ if (!HandleCoincidence(contourList, &coincidence)) {
return false;
}
#if DEBUG_DUMP_ALIGNMENT
@@ -219,8 +217,8 @@ bool Simplify(const SkPath& path, SkPath* result) {
SkPathWriter wrapper(*result);
bool closable SK_INIT_TO_AVOID_WARNING;
if (builder.xorMask() == kWinding_PathOpsMask
- ? !bridgeWinding(contourList, &wrapper, &allocator, &closable)
- : !bridgeXor(contourList, &wrapper, &allocator, &closable)) {
+ ? !bridgeWinding(contourList, &wrapper, &closable)
+ : !bridgeXor(contourList, &wrapper, &closable)) {
return false;
}
if (!closable)
@@ -232,6 +230,12 @@ bool Simplify(const SkPath& path, SkPath* result) {
*result = *assembled.nativePath();
result->setFillType(fillType);
}
+ if (scaleFactor > 1) {
+ ScalePath(*result, scaleFactor, result);
+ }
return true;
}
+bool Simplify(const SkPath& path, SkPath* result) {
+ return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
+}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 48aa540945..9032af83eb 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -250,7 +250,7 @@ private:
SkTSpan<TCurve, OppCurve>* addFollowing(SkTSpan<TCurve, OppCurve>* prior);
void addForPerp(SkTSpan<OppCurve, TCurve>* span, double t);
SkTSpan<TCurve, OppCurve>* addOne();
-
+
SkTSpan<TCurve, OppCurve>* addSplitAt(SkTSpan<TCurve, OppCurve>* span, double t) {
SkTSpan<TCurve, OppCurve>* result = this->addOne();
result->splitAt(span, t, &fHeap);
@@ -343,7 +343,7 @@ void SkTCoincident<TCurve, OppCurve>::setPerp(const TCurve& c1, double t,
if (used == 0 || used == 3) {
this->init();
return;
- }
+ }
fPerpT = i[0][0];
fPerpPt = i.pt(0);
SkASSERT(used <= 2);
@@ -858,7 +858,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addOne() {
result->reset();
result->fHasPerp = false;
result->fDeleted = false;
- ++fActiveCount;
+ ++fActiveCount;
PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
SkDEBUGCODE(result->fDebugSect = this);
#ifdef SK_DEBUG
@@ -969,6 +969,9 @@ void SkTSect<TCurve, OppCurve>::coincidentCheck(SkTSect<OppCurve, TCurve>* sect2
do {
coinStart = this->extractCoincident(sect2, coinStart, last);
} while (coinStart && !last->fDeleted);
+ if (!fHead || !sect2->fHead) {
+ break;
+ }
} while ((first = next));
}
@@ -1208,7 +1211,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::extractCoincident(
}
this->validate();
sect2->validate();
- return last && !last->fDeleted ? last : nullptr;
+ return last && !last->fDeleted && fHead && sect2->fHead ? last : nullptr;
}
template<typename TCurve, typename OppCurve>
@@ -1511,7 +1514,7 @@ template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2,
double t2, bool* calcMatched, bool* oppMatched) const {
if (*calcMatched) {
- SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
+ SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
} else {
*oppMatched = this->matchedDirection(t, sect2, t2);
*calcMatched = true;
@@ -1584,7 +1587,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::prev(
test = test->fNext;
SkASSERT(test);
}
- return result;
+ return result;
}
template<typename TCurve, typename OppCurve>
@@ -1939,7 +1942,7 @@ struct SkClosestRecord {
fC1Index = mate.fC1Index;
fC2Index = mate.fC2Index;
}
-
+
void reset() {
fClosest = FLT_MAX;
SkDEBUGCODE(fC1Span = nullptr);
@@ -2099,7 +2102,7 @@ void SkTSect<TCurve, OppCurve>::BinarySearch(SkTSect<TCurve, OppCurve>* sect1,
gets stuck in a loop. It adds an extension to allow a coincident end
perpendicular to track its intersection in the opposite curve. However,
the bounding box of the extension does not intersect the original curve,
- so the extension is discarded, only to be added again the next time around. */
+ so the extension is discarded, only to be added again the next time around. */
sect1->coincidentForce(sect2, start1s, start1e);
sect1->validate();
sect2->validate();
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index 19593c228b..e3a3108bd7 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -11,11 +11,20 @@ bool TightBounds(const SkPath& path, SkRect* result) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpGlobalState globalState(nullptr, contourList SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
// turn path into list of segments
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkScalar scaleFactor = ScaleFactor(path);
+ SkPath scaledPath;
+ const SkPath* workingPath;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(path, 1.f / scaleFactor, &scaledPath);
+ workingPath = &scaledPath;
+ } else {
+ workingPath = &path;
+ }
+ SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ if (!builder.finish()) {
return false;
}
if (!SortContourList(&contourList, false, false)) {
diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp
index bad1bc7a8b..d25df12873 100644
--- a/src/pathops/SkPathOpsTypes.cpp
+++ b/src/pathops/SkPathOpsTypes.cpp
@@ -25,6 +25,13 @@ static bool equal_ulps(float a, float b, int epsilon, int depsilon) {
return aBits < bBits + epsilon && bBits < aBits + epsilon;
}
+static bool equal_ulps_no_normal_check(float a, float b, int epsilon, int depsilon) {
+ int aBits = SkFloatAs2sCompliment(a);
+ int bBits = SkFloatAs2sCompliment(b);
+ // Find the difference in ULPs.
+ return aBits < bBits + epsilon && bBits < aBits + epsilon;
+}
+
static bool equal_ulps_pin(float a, float b, int epsilon, int depsilon) {
if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
return false;
@@ -120,6 +127,11 @@ bool AlmostEqualUlps(float a, float b) {
return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon);
}
+bool AlmostEqualUlpsNoNormalCheck(float a, float b) {
+ const int UlpsEpsilon = 16;
+ return equal_ulps_no_normal_check(a, b, UlpsEpsilon, UlpsEpsilon);
+}
+
bool AlmostEqualUlps_Pin(float a, float b) {
const int UlpsEpsilon = 16;
return equal_ulps_pin(a, b, UlpsEpsilon, UlpsEpsilon);
@@ -212,10 +224,12 @@ double SkDCubeRoot(double x) {
return result;
}
-SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
+SkOpGlobalState::SkOpGlobalState(SkOpContourHead* head,
+ SkChunkAlloc* allocator
SkDEBUGPARAMS(bool debugSkipAssert)
SkDEBUGPARAMS(const char* testName))
- : fCoincidence(coincidence)
+ : fAllocator(allocator)
+ , fCoincidence(nullptr)
, fContourHead(head)
, fNested(0)
, fWindingFailed(false)
@@ -227,13 +241,9 @@ SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead*
SkDEBUGPARAMS(fContourID(0))
SkDEBUGPARAMS(fPtTID(0))
SkDEBUGPARAMS(fSegmentID(0))
- SkDEBUGPARAMS(fSpanID(0))
- SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
- if (coincidence) {
- coincidence->debugSetGlobalState(this);
- }
+SkDEBUGPARAMS(fSpanID(0))
+SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
#if DEBUG_T_SECT_LOOP_COUNT
debugResetLoopCounts();
#endif
}
-
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 00c3e5fe93..ad2ad463e6 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,7 @@ enum SkPathOpsMask {
kEvenOdd_PathOpsMask = 1
};
+class SkChunkAlloc;
class SkOpCoincidence;
class SkOpContour;
class SkOpContourHead;
@@ -30,8 +31,8 @@ class SkIntersectionHelper;
class SkOpGlobalState {
public:
- SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
- SkDEBUGPARAMS(bool debugSkipAssert)
+ SkOpGlobalState(SkOpContourHead* head,
+ SkChunkAlloc* allocator SkDEBUGPARAMS(bool debugSkipAssert)
SkDEBUGPARAMS(const char* testName));
enum Phase {
@@ -44,6 +45,10 @@ public:
kMaxWindingTries = 10
};
+ SkChunkAlloc* allocator() {
+ return fAllocator;
+ }
+
bool angleCoincidence() const {
return fAngleCoincidence;
}
@@ -65,7 +70,8 @@ public:
}
#ifdef SK_DEBUG
- const struct SkOpAngle* debugAngle(int id) const;
+ const class SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
const class SkOpPtT* debugPtT(int id) const;
bool debugRunFail() const;
@@ -83,6 +89,11 @@ public:
void debugResetLoopCounts();
#endif
+#if DEBUG_COINCIDENCE
+ void debugSetCheckHealth(bool check) { fDebugCheckHealth = check; }
+ bool debugCheckHealth() const { return fDebugCheckHealth; }
+#endif
+
int nested() const {
return fNested;
}
@@ -120,6 +131,10 @@ public:
void setAngleCoincidence() {
fAngleCoincidence = true;
}
+
+ void setCoincidence(SkOpCoincidence* coincidence) {
+ fCoincidence = coincidence;
+ }
void setContourHead(SkOpContourHead* contourHead) {
fContourHead = contourHead;
@@ -140,6 +155,7 @@ public:
}
private:
+ SkChunkAlloc* fAllocator;
SkOpCoincidence* fCoincidence;
SkOpContourHead* fContourHead;
int fNested;
@@ -162,6 +178,9 @@ private:
SkPoint fDebugWorstPts[24];
float fDebugWorstWeight[6];
#endif
+#if DEBUG_COINCIDENCE
+ bool fDebugCheckHealth;
+#endif
};
// Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
@@ -170,6 +189,11 @@ inline bool AlmostEqualUlps(double a, double b) {
return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
}
+bool AlmostEqualUlpsNoNormalCheck(float a, float b);
+inline bool AlmostEqualUlpsNoNormalCheck(double a, double b) {
+ return AlmostEqualUlpsNoNormalCheck(SkDoubleToScalar(a), SkDoubleToScalar(b));
+}
+
bool AlmostEqualUlps_Pin(float a, float b);
inline bool AlmostEqualUlps_Pin(double a, double b) {
return AlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b));
@@ -246,6 +270,8 @@ const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
const double BUMP_EPSILON = FLT_EPSILON * 4096;
+const SkScalar INVERSE_NUMBER_RANGE = FLT_EPSILON_ORDERABLE_ERR;
+
inline bool zero_or_one(double x) {
return x == 0 || x == 1;
}
@@ -298,7 +324,6 @@ inline bool approximately_zero_inverse(double x) {
return fabs(x) > FLT_EPSILON_INVERSE;
}
-// OPTIMIZATION: if called multiple times with the same denom, we want to pass 1/y instead
inline bool approximately_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
}
@@ -307,6 +332,10 @@ inline bool precisely_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
}
+inline bool roughly_zero_when_compared_to(double x, double y) {
+ return x == 0 || fabs(x) < fabs(y * ROUGH_EPSILON);
+}
+
// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
// AlmostEqualUlps instead.
inline bool approximately_equal(double x, double y) {
diff --git a/src/pathops/SkPathOpsWinding.cpp b/src/pathops/SkPathOpsWinding.cpp
index 584ebd457a..4c6c636e2b 100644
--- a/src/pathops/SkPathOpsWinding.cpp
+++ b/src/pathops/SkPathOpsWinding.cpp
@@ -192,7 +192,7 @@ SkOpSpan* SkOpSegment::windingSpanAtT(double tHit) {
next = span->next();
if (approximately_equal(tHit, next->t())) {
return nullptr;
- }
+ }
if (tHit < next->t()) {
return span;
}
@@ -243,6 +243,10 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
}
SkOpRayHit* hitHead = &hitBase;
dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
+ if (hitBase.fSpan && hitBase.fSpan->segment()->verb() > SkPath::kLine_Verb
+ && !pt_yx(hitBase.fSlope.asSkVector(), dir)) {
+ return false;
+ }
SkOpContour* contour = contourHead;
do {
contour->rayCheck(hitBase, dir, &hitHead, &allocator);
@@ -256,11 +260,11 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
}
int count = sorted.count();
SkTQSort(sorted.begin(), sorted.end() - 1, xy_index(dir)
- ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y
+ ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y
: less_than(dir) ? hit_compare_x : reverse_hit_compare_x);
// verify windings
#if DEBUG_WINDING
- SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
+ SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
gDebugRayDirName[static_cast<int>(dir)], hitBase.fSpan->segment()->debugID(),
hitBase.fT, hitBase.fPt.fX, hitBase.fPt.fY);
for (int index = 0; index < count; ++index) {
@@ -378,15 +382,20 @@ SkOpSpan* SkOpSegment::findSortableTop(SkOpContour* contourHead) {
SkOpSpan* SkOpContour::findSortableTop(SkOpContour* contourHead) {
SkOpSegment* testSegment = &fHead;
+ bool allDone = true;
do {
if (testSegment->done()) {
continue;
}
+ allDone = false;
SkOpSpan* result = testSegment->findSortableTop(contourHead);
if (result) {
return result;
}
} while ((testSegment = testSegment->next()));
+ if (allDone) {
+ fDone = true;
+ }
return nullptr;
}
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 52a19d6138..48624baee9 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -78,10 +78,12 @@ int SkReduceOrder::reduce(const SkDQuad& quad) {
minYSet |= 1 << index;
}
}
+ if ((minXSet & 0x05) == 0x5 && (minYSet & 0x05) == 0x5) { // test for degenerate
+ // this quad starts and ends at the same place, so never contributes
+ // to the fill
+ return coincident_line(quad, fQuad);
+ }
if (minXSet == 0x7) { // test for vertical line
- if (minYSet == 0x7) { // return 1 if all three are coincident
- return coincident_line(quad, fQuad);
- }
return vertical_line(quad, fQuad);
}
if (minYSet == 0x7) { // test for horizontal line
diff --git a/tests/PathOpsAngleIdeas.cpp b/tests/PathOpsAngleIdeas.cpp
index 2e3c7b9066..9d1b5994b2 100755
--- a/tests/PathOpsAngleIdeas.cpp
+++ b/tests/PathOpsAngleIdeas.cpp
@@ -406,12 +406,11 @@ static bool bruteForceCheck(skiatest::Reporter* reporter, const SkDQuad& quad1,
return ccw == upperRange.ccw;
}
-static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3],
- SkChunkAlloc* allocator) {
+static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3]) {
shortQuad[0] = quad[0].asSkPoint();
shortQuad[1] = quad[1].asSkPoint();
shortQuad[2] = quad[2].asSkPoint();
- contour->addQuad(shortQuad, allocator);
+ contour->addQuad(shortQuad);
}
static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
@@ -419,14 +418,14 @@ static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, c
SkPoint shortQuads[2][3];
SkOpContourHead contour;
- SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ SkOpGlobalState state(&contour, allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
contour.init(&state, false, false);
- makeSegment(&contour, quad1, shortQuads[0], allocator);
- makeSegment(&contour, quad1, shortQuads[1], allocator);
+ makeSegment(&contour, quad1, shortQuads[0]);
+ makeSegment(&contour, quad1, shortQuads[1]);
SkOpSegment* seg1 = contour.first();
- seg1->debugAddAngle(0, 1, allocator);
+ seg1->debugAddAngle(0, 1);
SkOpSegment* seg2 = seg1->next();
- seg2->debugAddAngle(0, 1, allocator);
+ seg2->debugAddAngle(0, 1);
int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
*seg2->debugLastAngle());
const SkDPoint& origin = quad1[0];
diff --git a/tests/PathOpsAngleTest.cpp b/tests/PathOpsAngleTest.cpp
index 6389c305bb..fa8d31469b 100644
--- a/tests/PathOpsAngleTest.cpp
+++ b/tests/PathOpsAngleTest.cpp
@@ -194,6 +194,10 @@ public:
return lh.after(&rh);
}
+ static int AllOnOneSide(SkOpAngle& lh, SkOpAngle& rh) {
+ return lh.allOnOneSide(&rh);
+ }
+
static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
return lh.convexHullOverlaps(&rh);
}
@@ -235,7 +239,7 @@ static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
DEF_TEST(PathOpsAngleCircle, reporter) {
SkChunkAlloc allocator(4096);
SkOpContourHead contour;
- SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
contour.init(&state, false, false);
for (int index = 0; index < circleDataSetSize; ++index) {
CircleData& data = circleDataSet[index];
@@ -244,20 +248,20 @@ DEF_TEST(PathOpsAngleCircle, reporter) {
}
switch (data.fPtCount) {
case 2:
- contour.addLine(data.fShortPts, &allocator);
+ contour.addLine(data.fShortPts);
break;
case 3:
- contour.addQuad(data.fShortPts, &allocator);
+ contour.addQuad(data.fShortPts);
break;
case 4:
- contour.addCubic(data.fShortPts, &allocator);
+ contour.addCubic(data.fShortPts);
break;
}
}
SkOpSegment* first = contour.first();
- first->debugAddAngle(0, 1, &allocator);
+ first->debugAddAngle(0, 1);
SkOpSegment* next = first->next();
- next->debugAddAngle(0, 1, &allocator);
+ next->debugAddAngle(0, 1);
PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
}
@@ -427,7 +431,7 @@ struct FourPoints {
DEF_TEST(PathOpsAngleAfter, reporter) {
SkChunkAlloc allocator(4096);
SkOpContourHead contour;
- SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
contour.init(&state, false, false);
for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
IntersectData* dataArray = intersectDataSets[index];
@@ -443,22 +447,22 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
}
switch (data.fPtCount) {
case 2: {
- contour.addLine(temp, &allocator);
+ contour.addLine(temp);
} break;
case 3: {
- contour.addQuad(temp, &allocator);
+ contour.addQuad(temp);
} break;
case 4: {
- contour.addCubic(temp, &allocator);
+ contour.addCubic(temp);
} break;
}
}
SkOpSegment* seg1 = contour.first();
- seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd, &allocator);
+ seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd);
SkOpSegment* seg2 = seg1->next();
- seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd, &allocator);
+ seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd);
SkOpSegment* seg3 = seg2->next();
- seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd, &allocator);
+ seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd);
SkOpAngle& angle1 = *seg1->debugLastAngle();
SkOpAngle& angle2 = *seg2->debugLastAngle();
SkOpAngle& angle3 = *seg3->debugLastAngle();
@@ -472,12 +476,12 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
}
}
-void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* allocator) {
+void SkOpSegment::debugAddAngle(double startT, double endT) {
SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
- : this->addT(startT, kNoAlias, allocator);
+ : this->addT(startT, kNoAliasMatch, nullptr);
SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
- : this->addT(endT, kNoAlias, allocator);
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ : this->addT(endT, kNoAliasMatch, nullptr);
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
SkOpSpanBase* startSpan = &fHead;
while (startSpan->ptT() != startPtT) {
startSpan = startSpan->upCast()->next();
@@ -495,3 +499,29 @@ void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* alloca
startSpan->setFromAngle(angle);
}
}
+
+DEF_TEST(PathOpsAngleAllOnOneSide, reporter) {
+ SkChunkAlloc allocator(4096);
+ SkOpContourHead contour;
+ SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ contour.init(&state, false, false);
+ SkPoint conicPts[3] = {{494.37100219726562f, 224.66200256347656f},
+ {494.37360910682298f, 224.6729026561527f},
+ {494.37600708007813f, 224.68400573730469f}};
+ SkPoint linePts[2] = {{494.371002f, 224.662003f}, {494.375000f, 224.675995f}};
+ for (int i = 10; i >= 0; --i) {
+ SkPoint modLinePts[2] = { linePts[0], linePts[1] };
+ modLinePts[1].fX += i * .1f;
+ contour.addLine(modLinePts);
+ contour.addQuad(conicPts);
+ // contour.addConic(conicPts, 0.999935746f, &allocator);
+ SkOpSegment* first = contour.first();
+ first->debugAddAngle(0, 1);
+ SkOpSegment* next = first->next();
+ next->debugAddAngle(0, 1);
+ /* int result = */
+ PathOpsAngleTester::AllOnOneSide(*first->debugLastAngle(), *next->debugLastAngle());
+ // SkDebugf("i=%d result=%d\n", i , result);
+ // SkDebugf("");
+ }
+}
diff --git a/tests/PathOpsDebug.cpp b/tests/PathOpsDebug.cpp
index 0f96ef4f88..7770b00c6a 100755
--- a/tests/PathOpsDebug.cpp
+++ b/tests/PathOpsDebug.cpp
@@ -50,6 +50,14 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
}
#endif
+static void DumpID(int id) {
+ SkDebugf("} ");
+ if (id >= 0) {
+ SkDebugf("id=%d", id);
+ }
+ SkDebugf("\n");
+}
+
void SkDConic::dump() const {
dumpInner();
SkDebugf("},\n");
@@ -57,7 +65,7 @@ void SkDConic::dump() const {
void SkDConic::dumpID(int id) const {
dumpInner();
- SkDebugf("} id=%d\n", id);
+ DumpID(id);
}
void SkDConic::dumpInner() const {
@@ -73,7 +81,8 @@ void SkDCubic::dump() const {
void SkDCubic::dumpID(int id) const {
this->dumpInner();
- SkDebugf("}} id=%d\n", id);
+ SkDebugf("}");
+ DumpID(id);
}
static inline bool double_is_NaN(double x) { return x != x; }
@@ -97,6 +106,10 @@ void SkDCubic::dumpInner() const {
fPts[index].dump();
}
+void SkDCurve::dump() const {
+ dumpID(-1);
+}
+
void SkDCurve::dumpID(int id) const {
#ifndef SK_RELEASE
switch(fVerb) {
@@ -127,7 +140,8 @@ void SkDLine::dump() const {
void SkDLine::dumpID(int id) const {
this->dumpInner();
- SkDebugf("}} id=%d\n", id);
+ SkDebugf("}");
+ DumpID(id);
}
void SkDLine::dumpInner() const {
@@ -168,7 +182,8 @@ void SkDQuad::dump() const {
void SkDQuad::dumpID(int id) const {
dumpInner();
- SkDebugf("}} id=%d\n", id);
+ SkDebugf("}");
+ DumpID(id);
}
void SkDQuad::dumpInner() const {
@@ -787,6 +802,10 @@ const SkOpAngle* SkOpAngle::debugAngle(int id) const {
return this->segment()->debugAngle(id);
}
+const SkOpCoincidence* SkOpAngle::debugCoincidence() const {
+ return this->segment()->debugCoincidence();
+}
+
SkOpContour* SkOpAngle::debugContour(int id) {
return this->segment()->debugContour(id);
}
@@ -925,6 +944,10 @@ SkOpContour* SkOpPtT::debugContour(int id) {
return this->span()->debugContour(id);
}
+const SkOpCoincidence* SkOpPtT::debugCoincidence() const {
+ return this->span()->debugCoincidence();
+}
+
const SkOpPtT* SkOpPtT::debugPtT(int id) const {
return this->span()->debugPtT(id);
}
@@ -964,7 +987,8 @@ void SkOpPtT::dumpAll() const {
}
void SkOpPtT::dumpBase() const {
- SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s", this->fT, this->fPt.fX, this->fPt.fY,
+ SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s%s", this->fT, this->fPt.fX, this->fPt.fY,
+ this->fCoincident ? " coin" : "",
this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : "");
}
@@ -972,6 +996,10 @@ const SkOpAngle* SkOpSpanBase::debugAngle(int id) const {
return this->segment()->debugAngle(id);
}
+const SkOpCoincidence* SkOpSpanBase::debugCoincidence() const {
+ return this->segment()->debugCoincidence();
+}
+
SkOpContour* SkOpSpanBase::debugContour(int id) {
return this->segment()->debugContour(id);
}
@@ -989,15 +1017,19 @@ const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const {
}
void SkOpSpanBase::dump() const {
- this->dumpAll();
- SkDebugf("\n");
+ this->dumpHead();
+ this->fPtT.dump();
}
-void SkOpSpanBase::dumpAll() const {
+void SkOpSpanBase::dumpHead() const {
SkDebugf("%.*s", contour()->debugIndent(), " ");
SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID());
this->dumpBase();
SkDebugf("\n");
+}
+
+void SkOpSpanBase::dumpAll() const {
+ this->dumpHead();
this->fPtT.dumpAll();
}
@@ -1008,6 +1040,11 @@ void SkOpSpanBase::dumpBase() const {
if (this->fChased) {
SkDebugf(" chased");
}
+#ifdef SK_DEBUG
+ if (this->fDeleted) {
+ SkDebugf(" deleted");
+ }
+#endif
if (!this->final()) {
this->upCast()->dumpSpan();
}
@@ -1069,6 +1106,11 @@ const SkOpAngle* SkOpSegment::debugAngle(int id) const {
return this->contour()->debugAngle(id);
}
+
+const SkOpCoincidence* SkOpSegment::debugCoincidence() const {
+ return this->contour()->debugCoincidence();
+}
+
SkOpContour* SkOpSegment::debugContour(int id) {
return this->contour()->debugContour(id);
}
@@ -1188,20 +1230,26 @@ void SkOpCoincidence::dump() const {
SkCoincidentSpans* span = fHead;
while (span) {
span->dump();
- span = span->fNext;
+ span = span->next();
}
if (!fTop || fHead == fTop) {
return;
}
SkDebugf("top:\n");
span = fTop;
- if (fHead) {
- span->dump();
- return;
- }
+ int count = 0;
while (span) {
span->dump();
- span = span->fNext;
+ span = span->next();
+ SkCoincidentSpans* check = fTop;
+ ++count;
+ for (int index = 0; index < count; ++index) {
+ if (span == check) {
+ SkDebugf("(loops to #%d)\n", index);
+ return;
+ }
+ check = check->next();
+ }
}
}
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index b71b115439..2f6d99d652 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -22,6 +22,15 @@
#include <sys/sysctl.h>
#endif
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert)
+ SkDEBUGPARAMS(const char* testName));
+
+bool SimplifyDebug(const SkPath& one, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert)
+ SkDEBUGPARAMS(const char* testName));
+
+
__SK_FORCE_IMAGE_DECODER_LINKING;
DEFINE_bool2(runFail, f, false, "run tests known to fail.");
@@ -38,7 +47,7 @@ static const char* opStrs[] = {
"kDifference_SkPathOp",
"kIntersect_SkPathOp",
"kUnion_SkPathOp",
- "kXor_PathOp",
+ "kXOR_PathOp",
"kReverseDifference_SkPathOp",
};
@@ -47,6 +56,7 @@ static const char* opSuffixes[] = {
"i",
"u",
"o",
+ "r",
};
#if DEBUG_SHOW_TEST_NAME
@@ -443,20 +453,43 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
return result == 0;
}
+enum class ExpectSuccess {
+ kNo,
+ kYes
+};
+
+enum class SkipAssert {
+ kNo,
+ kYes
+};
+
+enum class ExpectMatch {
+ kNo,
+ kYes
+};
+
static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
- bool checkFail) {
+ ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) {
#if 0 && DEBUG_SHOW_TEST_NAME
showPathData(path);
#endif
SkPath out;
- if (!Simplify(path, &out)) {
- SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
- REPORTER_ASSERT(reporter, 0);
+ if (!SimplifyDebug(path, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
+ SkDEBUGPARAMS(testName))) {
+ if (ExpectSuccess::kYes == expectSuccess) {
+ SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
+ REPORTER_ASSERT(reporter, 0);
+ }
return false;
+ } else {
+ if (ExpectSuccess::kNo == expectSuccess) {
+ SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename);
+ REPORTER_ASSERT(reporter, 0);
+ }
}
SkBitmap bitmap;
int errors = comparePaths(reporter, filename, path, out, bitmap);
- if (!checkFail) {
+ if (ExpectMatch::kNo == expectMatch) {
if (!errors) {
SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
REPORTER_ASSERT(reporter, 0);
@@ -470,12 +503,19 @@ static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, con
}
bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
- return inner_simplify(reporter, path, filename, true);
+ return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo,
+ ExpectMatch::kYes);
+}
+
+bool testSimplifyFailSkipAssert(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+ return inner_simplify(reporter, path, filename, ExpectSuccess::kNo, SkipAssert::kYes,
+ ExpectMatch::kNo);
}
bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
bool checkFail) {
- return inner_simplify(reporter, path, filename, checkFail);
+ return inner_simplify(reporter, path, filename, checkFail ?
+ ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
}
#if DEBUG_SHOW_TEST_NAME
@@ -487,23 +527,25 @@ static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
}
#endif
-bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
- SkDEBUGPARAMS(bool skipAssert)
- SkDEBUGPARAMS(const char* testName));
-
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
- const SkPathOp shapeOp, const char* testName, bool expectSuccess, bool skipAssert) {
+ const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess,
+ SkipAssert skipAssert, ExpectMatch expectMatch) {
#if 0 && DEBUG_SHOW_TEST_NAME
showName(a, b, shapeOp);
#endif
SkPath out;
- if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(skipAssert)
+ if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
SkDEBUGPARAMS(testName))) {
- if (expectSuccess) {
- SkDebugf("%s did not expect failure\n", __FUNCTION__);
+ if (ExpectSuccess::kYes == expectSuccess) {
+ SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName);
REPORTER_ASSERT(reporter, 0);
}
return false;
+ } else {
+ if (ExpectSuccess::kNo == expectSuccess) {
+ SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName);
+ REPORTER_ASSERT(reporter, 0);
+ }
}
if (!reporter->verbose()) {
return true;
@@ -533,37 +575,42 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
scaledOut.addPath(out, scale);
scaledOut.setFillType(out.getFillType());
int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
- a, b, shapeOp, scale, expectSuccess);
+ a, b, shapeOp, scale, ExpectMatch::kYes == expectMatch);
reporter->bumpTestCount();
return result == 0;
}
bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
- return innerPathOp(reporter, a, b, shapeOp, testName, true, false);
+ return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo,
+ ExpectMatch::kYes);
}
bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName, bool checkFail) {
- return innerPathOp(reporter, a, b, shapeOp, testName, checkFail, false);
+ return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ?
+ ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
}
bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
- return innerPathOp(reporter, a, b, shapeOp, testName, false, false);
+ return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kNo, SkipAssert::kNo,
+ ExpectMatch::kNo);
}
-bool testPathSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+bool testPathOpSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
- return innerPathOp(reporter, a, b, shapeOp, testName, true, true);
+ return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kYes,
+ ExpectMatch::kYes);
}
-bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+bool testPathOpFailSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
- return innerPathOp(reporter, a, b, shapeOp, testName, false, true);
+ return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kNo, SkipAssert::kYes,
+ ExpectMatch::kNo);
}
-bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
#if DEBUG_SHOW_TEST_NAME
showName(a, b, shapeOp);
diff --git a/tests/PathOpsExtendedTest.h b/tests/PathOpsExtendedTest.h
index 1fadeb8ca4..f6efbef040 100644
--- a/tests/PathOpsExtendedTest.h
+++ b/tests/PathOpsExtendedTest.h
@@ -40,19 +40,23 @@ extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPa
const SkPathOp , const char* testName);
extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp , const char* testName, bool checkFail);
+extern bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+ const SkPathOp, const char* testName);
extern bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp , const char* testName);
-extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
- const SkPathOp , const char* testName);
-extern bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
- const SkPathOp , const char* testName);
-extern bool testPathSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+extern bool testPathOpFailSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+ const SkPathOp , const char* testName);
+extern bool testPathOpSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp , const char* testName);
extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
const char* pathStr);
extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
const char* filename, bool checkFail);
+extern bool testSimplifyFailSkipAssert(skiatest::Reporter* reporter, const SkPath& path,
+ const char* filename);
+extern bool testSimplifySkipAssert(skiatest::Reporter* reporter, const SkPath& path,
+ const char* filename);
void initializeTests(skiatest::Reporter* reporter, const char* testName);
void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
diff --git a/tests/PathOpsFuzz763Test.cpp b/tests/PathOpsFuzz763Test.cpp
index 7bc88ab5a5..ba0aa056fd 100755
--- a/tests/PathOpsFuzz763Test.cpp
+++ b/tests/PathOpsFuzz763Test.cpp
@@ -454,7 +454,8 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
path.close();
SkPath path2(path);
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+// DEBUG_UNDER_DEVELOPMENT fuzz763_378a disable expectation check for now
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
}
@@ -495,7 +496,7 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
path.close();
SkPath path2(path);
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
}
static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
@@ -932,8 +933,8 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
path.close();
SkPath path2(path);
- // FIXME: This should not fail; trading adding this failure for fixing security bug
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+// DEBUG_UNDER_DEVELOPMENT fuzz763_4713 disable expectation check for now
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
}
static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
@@ -1529,7 +1530,7 @@ path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc
path.close();
SkPath path2(path);
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+ testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
@@ -2093,7 +2094,8 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
path.close();
SkPath path2(path);
- testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+ // DEBUG_UNDER_DEVELOPMENT fuzz763_1026368 disable expectation check for now
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
}
static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) {
@@ -2396,7 +2398,7 @@ path.close();
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_1026368;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
diff --git a/tests/PathOpsIssue3651.cpp b/tests/PathOpsIssue3651.cpp
index ae4afc4688..574bec09c3 100644
--- a/tests/PathOpsIssue3651.cpp
+++ b/tests/PathOpsIssue3651.cpp
@@ -472,7 +472,8 @@ path.close();
static void issue3651_1a(skiatest::Reporter* reporter, const char* filename) {
SkPath path = path1_a();
SkPath pathB = path2_a();
- testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+ // DEBUG_UNDER_DEVELOPMENT issue3651_1a disable expectation check for now
+ testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, !FLAGS_runFail);
}
static SkPath path3() {
@@ -1202,7 +1203,8 @@ path.close();
static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
SkPath path = path1();
SkPath pathB = path2();
- testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+ // DEBUG_UNDER_DEVELOPMENT issue3651_1 disable expectation check for now
+ testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, !FLAGS_runFail);
}
static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
@@ -1656,7 +1658,7 @@ path.close();
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = issue3651_1;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index e22a2166ee..14c9251f2f 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -1196,7 +1196,7 @@ static void rRect1(skiatest::Reporter* reporter, const char* filename) {
path.setFillType(SkPath::kInverseEvenOdd_FillType);
for (int index = 0; index < 5; ++index) {
testPathOp(reporter, path, paths[index], ops[index], filename);
- Op(path, paths[index], ops[index], &path);
+ REPORTER_ASSERT(reporter, Op(path, paths[index], ops[index], &path));
}
}
@@ -3477,27 +3477,27 @@ static void issue2753(skiatest::Reporter* reporter, const char* filename) {
static void issue2808(skiatest::Reporter* reporter, const char* filename) {
SkPath path1, path2;
- path1.moveTo(509.20300293f, 385.601989746f);
- path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
- path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
- path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
- path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
- path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
- path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
- path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
- path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
- path1.close();
-
- path2.moveTo(449.033996582f, 290.87298584f);
- path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
- path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
- path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
- path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
- path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
- path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
- path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
- path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
- path2.close();
+ path1.moveTo(509.20300293f, 385.601989746f);
+ path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
+ path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
+ path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
+ path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
+ path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
+ path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
+ path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
+ path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
+ path1.close();
+
+ path2.moveTo(449.033996582f, 290.87298584f);
+ path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
+ path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
+ path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
+ path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
+ path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
+ path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
+ path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
+ path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
+ path2.close();
testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
}
@@ -4866,7 +4866,7 @@ static void loops46i(skiatest::Reporter* reporter, const char* filename) {
}
/*
-FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */
+FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */
static void loops47i(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
@@ -5160,7 +5160,7 @@ static void fuzz38(skiatest::Reporter* reporter, const char* filename) {
path.lineTo(100.34f, 310.156f);
path.lineTo(100.34f, 303.312f);
path.close();
- testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
}
static void crbug_526025(skiatest::Reporter* reporter, const char* filename) {
@@ -5211,13 +5211,176 @@ path.lineTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0x43c61f86)); // -236.484f,
testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
}
+static void dean2(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
+path.cubicTo(SkBits2Float(0x414a835a), SkBits2Float(0x3ec07ba6), SkBits2Float(0x413fcc0d), SkBits2Float(0x3e193319), SkBits2Float(0x4134a02b), SkBits2Float(0x00000000)); // 12.6571f, 0.375943f, 11.9873f, 0.149609f, 11.2891f, 0
+path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.close();
+ SkPath path1(path);
+
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
+path.lineTo(SkBits2Float(0x417ab74b), SkBits2Float(0x4154a02b)); // 15.6697f, 13.2891f
+path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.close();
+ SkPath path2(path);
+ testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void cubics_d(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(0, 1);
+ path.cubicTo(3, 5, 1, 0, 3, 0);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 1);
+ pathB.cubicTo(0, 3, 1, 0, 5, 3);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics_d2(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(0, 1);
+ path.cubicTo(2, 5, 2, 0, 2, 1);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 2);
+ pathB.cubicTo(1, 2, 1, 0, 5, 2);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loops_i1(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(2, 3);
+ path.cubicTo(0, 4, -0.333333343f, 4.66666651f, 3, 5.83333349f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 4);
+ pathB.cubicTo(-0.333333343f, 4.66666651f, 3, 5.83333349f, 2, 3);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i2(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(2, 4);
+ path.cubicTo(0, 5, -0.333333343f, 5.66666651f, 3, 6.83333302f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 5);
+ pathB.cubicTo(-0.333333343f, 5.66666651f, 3, 6.83333302f, 2, 4);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i3(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(2, 5);
+ path.cubicTo(0, 6, -0.333333343f, 6.66666651f, 3, 7.83333302f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 6);
+ pathB.cubicTo(-0.333333343f, 6.66666651f, 3, 7.83333302f, 2, 5);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i4(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(3, 4);
+ path.cubicTo(1, 5, 0.666666627f, 5.66666651f, 4, 6.83333302f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(1, 5);
+ pathB.cubicTo(0.666666627f, 5.66666651f, 4, 6.83333302f, 3, 4);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i5(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(3, 5);
+ path.cubicTo(1, 6, 0.666666627f, 6.66666651f, 4, 7.83333302f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(1, 6);
+ pathB.cubicTo(0.666666627f, 6.66666651f, 4, 7.83333302f, 3, 5);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i6(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(4, 5);
+ path.cubicTo(2, 6, 1.66666663f, 6.66666651f, 5, 7.83333302f);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(2, 6);
+ pathB.cubicTo(1.66666663f, 6.66666651f, 5, 7.83333302f, 4, 5);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics_d3(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(3, 4);
+ path.cubicTo(0, 6, 6, 1, 4, 2);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(1, 6);
+ pathB.cubicTo(2, 4, 4, 3, 6, 0);
+ pathB.close();
+ // DEBUG_UNDER_DEVELOPMENT cubics_d3 disable expectation check for now
+ testPathOpCheck(reporter, path, pathB, kDifference_SkPathOp, filename, !FLAGS_runFail);
+}
+
+static void cubics_o(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kWinding_FillType);
+ path.moveTo(1, 4);
+ path.cubicTo(2, 6, 5, 0, 5, 3);
+ path.close();
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(0, 5);
+ pathB.cubicTo(3, 5, 4, 1, 6, 2);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
+}
+
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = cubics_d3;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
#define TEST(name) { name, #name }
static struct TestDesc tests[] = {
+ TEST(loops_i1),
+ TEST(loops_i2),
+ TEST(loops_i3),
+ TEST(loops_i4),
+ TEST(loops_i5),
+ TEST(loops_i6),
+ TEST(cubics_d3),
+ TEST(cubics_o),
+ TEST(cubics_d2),
+ TEST(cubics_d),
+ TEST(dean2),
TEST(fuzzX_392),
TEST(crbug_526025),
TEST(fuzz38),
@@ -5594,7 +5757,7 @@ static void fuzz535151(skiatest::Reporter* reporter, const char* filename) {
two.lineTo(0, 50);
two.lineTo(4.29497e+09f, 50);
SkPath dummy;
- REPORTER_ASSERT(reporter, !Op(one, two, kIntersect_SkPathOp, &dummy));
+ testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
}
static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
@@ -5602,7 +5765,7 @@ static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
SkPath pathB;
pathB.addRect(0,0, 300,16);
- testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
+ testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
}
// m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
@@ -5622,7 +5785,7 @@ static void fuzz433(skiatest::Reporter* reporter, const char* filename) {
path2.lineTo(-170 + 20,11000000000.0f + 20);
path2.close();
- testPathOpCheck(reporter, path1, path2, kIntersect_SkPathOp, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path1, path2, kIntersect_SkPathOp, filename, true);
}
static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
@@ -5645,7 +5808,7 @@ static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
path2.lineTo(190, 60);
path2.close();
- testPathOpCheck(reporter, path1, path2, kUnion_SkPathOp, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path1, path2, kUnion_SkPathOp, filename, true);
}
static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
@@ -5691,7 +5854,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
path.close();
SkPath path2(path);
- testPathOpFailCheck(reporter, path1, path2, (SkPathOp) 2, filename);
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
}
static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
@@ -5737,7 +5900,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
path.close();
SkPath path2(path);
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
}
static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
@@ -5763,7 +5926,7 @@ path.lineTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
path.close();
SkPath path2(path);
- testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+ testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
}
static void fuzz1(skiatest::Reporter* reporter, const char* filename) {
@@ -5785,7 +5948,7 @@ path.close();
path.setFillType((SkPath::FillType) 0);
SkPath path2(path);
- testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+ testPathOpFail(reporter, path1, path2, (SkPathOp) 2, filename);
}
@@ -5810,7 +5973,7 @@ path.cubicTo(SkBits2Float(0x42266e32), SkBits2Float(0xcf223cc0), SkBits2Float(0x
path.lineTo(SkBits2Float(0x40f8fbe0), SkBits2Float(0xcf223cc0)); // 7.78075f, -2.72189e+09f
SkPath path2(path);
- testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+ testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void bug597926_0(skiatest::Reporter* reporter, const char* filename) {
@@ -5833,7 +5996,7 @@ path.cubicTo(SkBits2Float(0xc51cd471), SkBits2Float(0xc49d54d0), SkBits2Float(0x
path.quadTo(SkBits2Float(0xc51bf7eb), SkBits2Float(0xc49cf010), SkBits2Float(0xc51ba866), SkBits2Float(0xc49cb9e6)); // -2495.49f, -1255.5f, -2490.52f, -1253.81f
path.cubicTo(SkBits2Float(0xc51bac0d), SkBits2Float(0xc49cc50e), SkBits2Float(0xc51c29eb), SkBits2Float(0xc49cfb01), SkBits2Float(0xc51c5bca), SkBits2Float(0xc49d1fa6)); // -2490.75f, -1254.16f, -2498.62f, -1255.84f, -2501.74f, -1256.99f
SkPath path2(path);
-testPathFailOp(reporter, path1, path2, (SkPathOp) 1, filename);
+testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
}
static void fuzz1450_0(skiatest::Reporter* reporter, const char* filename) {
@@ -5852,7 +6015,7 @@ path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000)); // 360, 1024
path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140
path.close();
SkPath path2(path);
-testPathSkipAssertOp(reporter, path1, path2, kUnion_SkPathOp, filename);
+testPathOpSkipAssert(reporter, path1, path2, kUnion_SkPathOp, filename);
}
static void fuzz1450_1(skiatest::Reporter* reporter, const char* filename) {
@@ -5881,7 +6044,7 @@ path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000)); // 2551, 64
path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321
path.close();
SkPath path2(path);
-testPathFailOp(reporter, path1, path2, kUnion_SkPathOp, filename);
+testPathOpSkipAssert(reporter, path1, path2, kUnion_SkPathOp, filename);
}
static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) {
@@ -5908,7 +6071,7 @@ path.quadTo(SkBits2Float(0xd912102a), SkBits2Float(0x284f9a28), SkBits2Float(0xb
path.lineTo(SkBits2Float(0xc809272a), SkBits2Float(0x29b02829)); // -140445, 7.82294e-14f
SkPath path2(path);
- testPathFailOp(reporter, path1, path2, (SkPathOp) 1, filename);
+ testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
}
@@ -5942,7 +6105,7 @@ path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f
path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x21df212a), SkBits2Float(0x033a8a3a)); // 6.14991e+25f, 6.77381e-19f, 9.33503e-15f, 1.51198e-18f, 5.48192e-37f
SkPath path2(path);
- testPathFailSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
}
static void fuzz763_3(skiatest::Reporter* reporter, const char* filename) {
@@ -5977,7 +6140,7 @@ path.moveTo(SkBits2Float(0x21081f3f), SkBits2Float(0x9fd4e62a)); // 4.61199e-19
path.cubicTo(SkBits2Float(0x3a293a2a), SkBits2Float(0x0e3bf0c5), SkBits2Float(0x3b29d42a), SkBits2Float(0x0f217265), SkBits2Float(0x2d5d2921), SkBits2Float(0x5568295b)); // 0.000645551f, 2.31655e-30f, 0.00259138f, 7.95994e-30f, 1.25715e-11f, 1.5954e+13f
SkPath path2(path);
- testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
}
static void fuzz763_5(skiatest::Reporter* reporter, const char* filename) {
@@ -6002,7 +6165,7 @@ path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3b21), SkBits2Float(0x2
path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d8c55)); // 4.87407e+16f, 4.88495e+16f
SkPath path2(path);
- testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 4, filename);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 4, filename);
}
static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) {
@@ -6041,10 +6204,226 @@ path.conicTo(SkBits2Float(0xf86d273b), SkBits2Float(0x27e523e3), SkBits2Float(0x
path.cubicTo(SkBits2Float(0x2f273927), SkBits2Float(0xa83a2c21), SkBits2Float(0xd7122121), SkBits2Float(0x21212921), SkBits2Float(0x3be3db3a), SkBits2Float(0xa9deb63b)); // 1.52089e-10f, -1.03346e-14f, -1.60671e+14f, 5.46034e-19f, 0.00695362f, -9.89039e-14f
SkPath path2(path);
- testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+// crbug.com/626164
+static void fuzz763_1c(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+ path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
+ path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+ path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+
+ SkPath path2(path);
+ SkPath dummy;
+ testPathOp(reporter, path1, path2, (SkPathOp)4, filename);
+}
+
+// crbug.com/626186
+static void fuzz763_1b(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.cubicTo(SkBits2Float(0x0000ff07), SkBits2Float(0xf9f9ff00), SkBits2Float(0xfe0ef9f4), SkBits2Float(0xd9b105fb), SkBits2Float(0x000000f9), SkBits2Float(0xfe11f901)); // 9.14866e-41f, -1.62257e+35f, -4.75121e+37f, -6.22846e+15f, 3.48923e-43f, -4.85077e+37f
+ path.lineTo(SkBits2Float(0xda1905ed), SkBits2Float(0x3c05fbfb)); // -1.0768e+16f, 0.00817775f
+ path.cubicTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0x3c3c3c3c), SkBits2Float(0x253c7f00), SkBits2Float(0xfa00d3fa), SkBits2Float(0x250025fe), SkBits2Float(0x00000006)); // 0.011489f, 0.011489f, 1.63494e-16f, -1.67228e+35f, 1.11151e-16f, 8.40779e-45f
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.quadTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0xfa253c3c), SkBits2Float(0xfefa00d3), SkBits2Float(0x25fad9df)); // 0.011489f, -2.14488e+35f, -1.66156e+38f, 4.35157e-16f
+ path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.close();
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.lineTo(SkBits2Float(0x8dfefa00), SkBits2Float(0xf0f9fad9)); // -1.57141e-30f, -6.1892e+29f
+ path.cubicTo(SkBits2Float(0x20fe58f9), SkBits2Float(0x0525fbed), SkBits2Float(0x1905ffff), SkBits2Float(0x01f9f9f9), SkBits2Float(0xfbfe0ef9), SkBits2Float(0xfb212fff)); // 4.30882e-19f, 7.80453e-36f, 6.92764e-24f, 9.18268e-38f, -2.63829e+36f, -8.36933e+35f
+
+ SkPath path2(path);
+ testPathOp(reporter, path1, path2, (SkPathOp)2, filename);
+}
+
+static void fuzz763_1a(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.cubicTo(SkBits2Float(0x154be880), SkBits2Float(0x80000640), SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0x80045959), SkBits2Float(0x40154be8)); // 4.11789e-26f, -2.24208e-42f, 1.49562e+13f, 7.50652e+15f, -3.99394e-40f, 2.33276f
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+ path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+ path.quadTo(SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0xbd595959), SkBits2Float(0x3f3f3f09)); // 1.49562e+13f, 7.50652e+15f, -0.0530637f, 0.747056f
+ path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0x3f3f3f3f)); // 0.747059f, 0.747059f
+ path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
+ path.lineTo(SkBits2Float(0x09090909), SkBits2Float(0x3038d509)); // 1.6495e-33f, 6.72416e-10f
+ path.conicTo(SkBits2Float(0x5947ffff), SkBits2Float(0x40e88004), SkBits2Float(0x00002059), SkBits2Float(0x28555900), SkBits2Float(0x5959d559)); // 3.51844e+15f, 7.26563f, 1.16042e-41f, 1.18432e-14f, 3.83217e+15f
+ path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
+ path.close();
+ path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
+ path.lineTo(SkBits2Float(0x38d57f4b), SkBits2Float(0x59597f4b)); // 0.000101803f, 3.82625e+15f
+ path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
+ path.close();
+ path.moveTo(SkBits2Float(0x384700ff), SkBits2Float(0x0108804b)); // 4.74462e-05f, 2.50713e-38f
+
+ SkPath path2(path);
+ testPathOp(reporter, path1, path2, (SkPathOp)0, filename);
+}
+
+// crbug.com/627780
+static void fuzz763_3a(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c)); // 1.50606e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x2a685568), SkBits2Float(0x68295b2d)); // 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 2.06354e-13f, 3.19905e+24f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0x7a6a4b77), SkBits2Float(0x3a214726)); // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, 3.04132e+35f, 0.000615226f
+path.moveTo(SkBits2Float(0x8adf2028), SkBits2Float(0x3a219a3a)); // -2.14862e-32f, 0.000616464f
+path.quadTo(SkBits2Float(0x3ab38e28), SkBits2Float(0x29283ac2), SkBits2Float(0x2be61d2a), SkBits2Float(0x812a4396)); // 0.0013699f, 3.73545e-14f, 1.63506e-12f, -3.12726e-38f
+
+ SkPath path2(path);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+// crbug.com/627689
+static void fuzz763_5a(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0
+path.conicTo(SkBits2Float(0x4183d871), SkBits2Float(0x41fea321), SkBits2Float(0xb700ff00), SkBits2Float(0x4240b8b8), SkBits2Float(0x3b058283)); // 16.4807f, 31.8297f, -7.68877e-06f, 48.1804f, 0.0020372f
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
+path.conicTo(SkBits2Float(0x3a455ec8), SkBits2Float(0xb8b8b8b3), SkBits2Float(0x38b2418d), SkBits2Float(0xb730d014), SkBits2Float(0x3f7ffff3)); // 0.000752908f, -8.80821e-05f, 8.49991e-05f, -1.05389e-05f, 0.999999f
+path.quadTo(SkBits2Float(0x3a51246a), SkBits2Float(0xb6da45a3), SkBits2Float(0x38bc5c3c), SkBits2Float(0x00000000)); // 0.000797814f, -6.50501e-06f, 8.98172e-05f, 0
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
+path.quadTo(SkBits2Float(0x39a32d2d), SkBits2Float(0x00000000), SkBits2Float(0xb8a13a00), SkBits2Float(0x00000000)); // 0.000311234f, 0, -7.68788e-05f, 0
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
+path.quadTo(SkBits2Float(0x39ba814c), SkBits2Float(0xb838fed2), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0.00035573f, -4.41063e-05f, 0, 0
+path.lineTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0
+path.close();
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+
+ SkPath path2(path);
+ testPathOp(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+// crbug.com/627401
+static void fuzz763_2a(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.quadTo(SkBits2Float(0x3e484500), SkBits2Float(0x164f3a30), SkBits2Float(0x49484801), SkBits2Float(0x7d0100c8)); // 0.195576f, 1.67397e-25f, 820352, 1.07172e+37f
+path.conicTo(SkBits2Float(0xff7f36fd), SkBits2Float(0x3e647d01), SkBits2Float(0x0c00f430), SkBits2Float(0x486b6448), SkBits2Float(0x00484848)); // -3.39239e+38f, 0.223133f, 9.93424e-32f, 241041, 6.63809e-39f
+path.lineTo(SkBits2Float(0x4f4f557d), SkBits2Float(0x48480112)); // 3.47849e+09f, 204804
+path.lineTo(SkBits2Float(0xf40c01ff), SkBits2Float(0x45008000)); // -4.43702e+31f, 2056
+path.moveTo(SkBits2Float(0x4bfffa00), SkBits2Float(0x7d4ac859)); // 3.35514e+07f, 1.68465e+37f
+path.conicTo(SkBits2Float(0x7d014f3e), SkBits2Float(0x00f4ff01), SkBits2Float(0x6b64480c), SkBits2Float(0x48484848), SkBits2Float(0x557d0100)); // 1.07426e+37f, 2.24993e-38f, 2.75975e+26f, 205089, 1.73863e+13f
+
+ SkPath path2(path);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+// crbug.com/627761
+static void fuzz763_2b(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55)); // 4.76191e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21)); // 2.50338e-13f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x4721ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f9a3a8a)); // 5.63611e+25f, 41453.5f, 9.4495e-15f, 0.000617492f, 5.17506e+09f
+path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
+path.close();
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
+path.cubicTo(SkBits2Float(0x273ac23a), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x29685568)); // 2.5918e-15f, 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 5.15884e-14f
+path.lineTo(SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321)); // 4.78968e-34f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28)); // 9.4495e-15f, 0.000617492f
+path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
+path.close();
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
+path.quadTo(SkBits2Float(0x8a4fc29a), SkBits2Float(0x3ab3283a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6)); // -1.00033e-32f, 0.00136686f, 2.25206e-21f, 300.343f
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
+path.conicTo(SkBits2Float(0x1e2ab03a), SkBits2Float(0x2920213b), SkBits2Float(0x3b3ac527), SkBits2Float(0xc422333b), SkBits2Float(0x6c2a9f1f)); // 9.03617e-21f, 3.5556e-14f, 0.00284989f, -648.8f, 8.25075e+26f
+path.quadTo(SkBits2Float(0xc25d2757), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152), SkBits2Float(0x28d91210)); // -55.2884f, 0.000916855f, 1.2818e-13f, 2.40997e-14f
+path.quadTo(SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21)); // 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f
+path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
+path.close();
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0xcb7bc003), SkBits2Float(0x47ed7a6a)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, -1.64987e+07f, 121589
+path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
+path.close();
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x2d8a3a21), SkBits2Float(0x5b682b68), SkBits2Float(0x5b292d55)); // -1.2117e+19f, 1.57146e-11f, 6.53499e+16f, 4.76191e+16f
+path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f)); // 1.43144e-13f, 4.7323e-37f
+path.conicTo(SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x3a21477a)); // 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 0.000615231f
+path.moveTo(SkBits2Float(0x21df2828), SkBits2Float(0x9a3a8a3a)); // 1.51217e-18f, -3.85756e-23f
+path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x28273ac2), SkBits2Float(0xe61d2a29), SkBits2Float(0x2a63962b)); // 0.00136978f, 9.2831e-15f, -1.85547e+23f, 2.02138e-13f
+path.conicTo(SkBits2Float(0x2d29272a), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55)); // 9.61523e-12f, 1.5954e+13f, 4.87407e+16f, 4.88097e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f
+path.close();
+
+ SkPath path2(path);
+ testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_2c(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 0, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x364a4a4a), SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0, 3.01436e-06f, 3.01436e-06f, 0, 0
+path.lineTo(SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000)); // 3.01436e-06f, 0
+path.cubicTo(SkBits2Float(0x364a30f0), SkBits2Float(0x344ac7fb), SkBits2Float(0x3656d432), SkBits2Float(0x34cabb48), SkBits2Float(0x367031a9), SkBits2Float(0x351802f1)); // 3.01288e-06f, 1.88855e-07f, 3.2012e-06f, 3.77617e-07f, 3.57917e-06f, 5.66287e-07f
+path.cubicTo(SkBits2Float(0x36a7b150), SkBits2Float(0x35ab09db), SkBits2Float(0x371874ed), SkBits2Float(0x3604f2c7), SkBits2Float(0x3784e0c7), SkBits2Float(0x36344a51)); // 4.99763e-06f, 1.27434e-06f, 9.08713e-06f, 1.98108e-06f, 1.58403e-05f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x3743dc9a), SkBits2Float(0x36344a4f), SkBits2Float(0x36fbef33), SkBits2Float(0x36344a4e), SkBits2Float(0x36604a35), SkBits2Float(0x36344a4c)); // 1.16743e-05f, 2.68653e-06f, 7.50823e-06f, 2.68653e-06f, 3.34218e-06f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x36531715), SkBits2Float(0x36344a4c), SkBits2Float(0x3645e3f5), SkBits2Float(0x36344a4b), SkBits2Float(0x3638b0d4), SkBits2Float(0x36344a4b)); // 3.14549e-06f, 2.68653e-06f, 2.9488e-06f, 2.68653e-06f, 2.75211e-06f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x35f64120), SkBits2Float(0x36344a4b), SkBits2Float(0x35764124), SkBits2Float(0x36344a4a), SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 1.83474e-06f, 2.68653e-06f, 9.17369e-07f, 2.68653e-06f, 0, 2.68653e-06f
+path.close();
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x544a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.47532e+12f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+ SkPath path2(path);
+ testPathOpCheck(reporter, path1, path2, kReverseDifference_SkPathOp, filename, true);
}
static struct TestDesc failTests[] = {
+ TEST(fuzz763_2c),
+ TEST(fuzz763_2b),
+ TEST(fuzz763_2a),
+ TEST(fuzz763_5a),
+ TEST(fuzz763_3a),
+ TEST(fuzz763_1a),
+ TEST(fuzz763_1b),
+ TEST(fuzz763_1c),
TEST(fuzz763_2),
TEST(fuzz763_5),
TEST(fuzz763_3),
@@ -6072,3 +6451,14 @@ DEF_TEST(PathOpsFailOp, reporter) {
#endif
RunTestSet(reporter, failTests, failTestCount, nullptr, nullptr, nullptr, false);
}
+
+static struct TestDesc repTests[] = {
+ TEST(loops44i),
+ TEST(loops45i),
+ TEST(loops46i),
+};
+
+DEF_TEST(PathOpsRepOp, reporter) {
+ for (int index = 0; index < 2; ++index)
+ RunTestSet(reporter, repTests, SK_ARRAY_COUNT(repTests), nullptr, nullptr, nullptr, false);
+}
diff --git a/tests/PathOpsSimplifyFailTest.cpp b/tests/PathOpsSimplifyFailTest.cpp
index 01c6272719..9b29ccd325 100644
--- a/tests/PathOpsSimplifyFailTest.cpp
+++ b/tests/PathOpsSimplifyFailTest.cpp
@@ -4,6 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "PathOpsExtendedTest.h"
#include "SkPath.h"
#include "SkPathOps.h"
#include "SkPoint.h"
@@ -93,7 +94,44 @@ static void dontFailOne(skiatest::Reporter* reporter, int index) {
reporter->bumpTestCount();
}
+static void fuzz_59(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
+ path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419)); // 200, -9.09969e+08f
+ path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b)); // 40, -9.09969e+08f
+ path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b)); // 240, -9.09969e+08f
+ path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419)); // 70, -9.09969e+08f
+ path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
+ path.close();
+ testSimplifyCheck(reporter, path, filename, true);
+}
+
+static void fuzz_x1(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+ testSimplify(reporter, path, filename);
+}
+
+static void fuzz_x2(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+ testSimplify(reporter, path, filename);
+}
+
+#define TEST(test) test(reporter, #test)
+
DEF_TEST(PathOpsSimplifyFail, reporter) {
+ TEST(fuzz_x2);
+ TEST(fuzz_x1);
+ TEST(fuzz_59);
for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) {
failOne(reporter, index);
}
@@ -102,6 +140,8 @@ DEF_TEST(PathOpsSimplifyFail, reporter) {
}
}
+#undef TEST
+
DEF_TEST(PathOpsSimplifyFailOne, reporter) {
int index = 0;
failOne(reporter, index);
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index a3f415ff89..2892b894ec 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -5052,19 +5052,259 @@ path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000)); // 3.35546e+07
path.lineTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000)); // 123, 0
path.close();
- REPORTER_ASSERT(reporter, !Simplify(path, &path));
+ testSimplify(reporter, path, filename);
}
-static void fuzz_59(skiatest::Reporter* reporter, const char* filename) {
- SkPath path;
-path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
-path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419)); // 200, -9.09969e+08f
-path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b)); // 40, -9.09969e+08f
-path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b)); // 240, -9.09969e+08f
-path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419)); // 70, -9.09969e+08f
-path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
+static void tiger8(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ SkPath path;
+ path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
+path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
+path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
+path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
+path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
+path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
+path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
+path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
+path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
+path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
+path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
+path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
+path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
+path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
+path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
+path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
+path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
+path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
+path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
+path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
+path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+path.close();
+path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f
+path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
+path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f
+path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f
+path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f
+path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f
+path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f
+path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f
+path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f
+path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f
+path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f
+path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f
+path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f
+path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f
+path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f
+path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f
+path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f
+path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f
+path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f
+path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f
+path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f
+path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225
+path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f
+path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f
+path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f
+path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f
+path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f
+path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f
+path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f
+path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f
+path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f
path.close();
- REPORTER_ASSERT(reporter, !Simplify(path, &path));
+testSimplify(reporter, path, filename);
+}
+
+// fails to include a line of edges, probably mis-sorting
+static void tiger8a(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ SkPath path;
+ path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
+path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
+path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
+path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
+path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
+path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
+path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
+path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
+path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
+path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
+path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
+path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
+path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
+path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
+path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
+path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
+path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
+path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
+path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
+path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
+path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+static void tiger8a_x(skiatest::Reporter* reporter, const char* filename, uint64_t testlines) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ SkPath path;
+uint64_t i = 0;
+if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
+if (testlines & (1LL << i++)) path.close();
+testSimplify(reporter, path, filename);
+}
+
+#include "SkRandom.h"
+
+static void tiger8a_h(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ SkRandom r;
+ for (int samples = 2; samples < 38; ++samples) {
+ for (int tests = 0; tests < 10000; ++tests) {
+ uint64_t testlines = 0;
+ for (int i = 0; i < samples; ++i) {
+ int bit;
+ do {
+ bit = r.nextRangeU(0, 38);
+ } while (testlines & (1LL << bit));
+ testlines |= 1LL << bit;
+ }
+ tiger8a_x(reporter, filename, testlines);
+ }
+ }
+}
+
+static void tiger8a_h_1(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ uint64_t testlines = 0x0000000280802863; // best so far: 0x0000001610031021;
+ tiger8a_x(reporter, filename, testlines);
+}
+
+// tries to add same edge twice
+static void tiger8b(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT // tiger
+ return;
+#endif
+ SkPath path;
+path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f
+path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
+path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f
+path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f
+path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f
+path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f
+path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f
+path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f
+path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f
+path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f
+path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f
+path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f
+path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f
+path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f
+path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f
+path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f
+path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f
+path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f
+path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f
+path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f
+path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f
+path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225
+path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f
+path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f
+path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f
+path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f
+path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f
+path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f
+path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f
+path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f
+path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f
+path.close();
+testSimplify(reporter, path, filename);
}
// FIXME: this should not fail -- it was isolated looking for the root cause to fuzz763_4713
@@ -5101,7 +5341,443 @@ path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x4
path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0));
path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
path.close();
-testSimplifyCheck(reporter, path, filename, false);
+// DEBUG_UNDER_DEVELOPMENT fuzz763_4713_b disable expectation check for now
+testSimplifyCheck(reporter, path, filename, !FLAGS_runFail);
+}
+
+static void dean4(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+
+ // start region
+ // start loop, contour: 1
+ // Segment 1145.3381097316742 2017.6783947944641 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
+ path.moveTo(1145.3381347656250, 2017.6783447265625);
+ path.lineTo(1145.3381347656250, 2017.0034179687500);
+ // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927231521568 2017.0033947825432
+ path.lineTo(1143.6927490234375, 2017.0034179687500);
+ // Segment 1143.6927231521568 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
+ path.lineTo(1144.8640136718750, 2018.1589355468750);
+ // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.6783947944641
+ path.lineTo(1145.3381347656250, 2017.6783447265625);
+ path.close();
+ // start loop, contour: 2
+ // Segment 1145.3381097316742 2016.3216052055359 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
+ path.moveTo(1145.3381347656250, 2016.3216552734375);
+ path.lineTo(1144.8640136718750, 2015.8410644531250);
+ // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927230811802 2016.9966052174568
+ path.lineTo(1143.6927490234375, 2016.9965820312500);
+ // Segment 1143.6927230811802 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
+ path.lineTo(1145.3381347656250, 2016.9965820312500);
+ // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.3216052055359
+ path.lineTo(1145.3381347656250, 2016.3216552734375);
+ path.close();
+ // start loop, contour: 3
+ // Segment 1147.3323798179626 2014.3542600870132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220239557 2014.8347900059885
+ path.moveTo(1147.3323974609375, 2014.3542480468750);
+ path.lineTo(1147.8063964843750, 2014.8348388671875);
+ // Segment 1147.8064220239557 2014.8347900059885 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
+ path.lineTo(1147.8063964843750, 2014.8348388671875);
+ // Segment 1147.8064220516883 2014.8347899786306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2014.3542600870132
+ path.lineTo(1147.3323974609375, 2014.3542480468750);
+ path.close();
+ // start loop, contour: 4
+ // Segment 1146.3696286678314 2013.4045072346926 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708778083 2013.8850371497379
+ path.moveTo(1146.3696289062500, 2013.4045410156250);
+ path.lineTo(1146.8436279296875, 2013.8850097656250);
+ // Segment 1146.8436708778083 2013.8850371497379 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
+ path.lineTo(1146.8436279296875, 2013.8850097656250);
+ // Segment 1146.8436709015571 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2013.4045072346926
+ path.lineTo(1146.3696289062500, 2013.4045410156250);
+ path.close();
+ // start loop, contour: 5
+ // Segment 1143.2063037902117 2016.5251235961914 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615802348 2016.0445936811461
+ path.moveTo(1143.2062988281250, 2016.5251464843750);
+ path.lineTo(1142.7322998046875, 2016.0445556640625);
+ // Segment 1142.7322615802348 2016.0445936811461 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
+ path.lineTo(1142.7322998046875, 2016.0445556640625);
+ // Segment 1142.7322615564860 2016.0445937045740 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2016.5251235961914
+ path.lineTo(1143.2062988281250, 2016.5251464843750);
+ path.close();
+ // start loop, contour: 6
+ // Segment 1143.0687679275870 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2017.2091718784643
+ path.moveTo(1143.0687255859375, 2016.7286376953125);
+ path.lineTo(1143.5428466796875, 2017.2092285156250);
+ // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2017.0109272411960
+ path.lineTo(1143.7437744140625, 2017.0109863281250);
+ // Segment 1143.7437679395080 2017.0109272411960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
+ path.lineTo(1143.7437744140625, 2016.7286376953125);
+ // Segment 1143.7437679395080 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.0687679275870 2016.7286419868469
+ path.lineTo(1143.0687255859375, 2016.7286376953125);
+ path.close();
+ // start loop, contour: 7
+ // Segment 1143.2063037902117 2017.4748764038086 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615603032 2017.9554062991915
+ path.moveTo(1143.2062988281250, 2017.4748535156250);
+ path.lineTo(1142.7322998046875, 2017.9554443359375);
+ // Segment 1142.7322615603032 2017.9554062991915 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
+ path.lineTo(1142.7322998046875, 2017.9554443359375);
+ // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2017.4748764038086
+ path.lineTo(1143.2062988281250, 2017.4748535156250);
+ path.close();
+ // start loop, contour: 8
+ // Segment 1146.3696286678314 2020.5954928398132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708977399 2020.1149629444303
+ path.moveTo(1146.3696289062500, 2020.5954589843750);
+ path.lineTo(1146.8436279296875, 2020.1149902343750);
+ // Segment 1146.8436708977399 2020.1149629444303 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
+ path.lineTo(1146.8436279296875, 2020.1149902343750);
+ // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2020.5954928398132
+ path.lineTo(1146.3696289062500, 2020.5954589843750);
+ path.close();
+ // start loop, contour: 9
+ // Segment 1147.3323798179626 2019.6457400321960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220484741 2019.1652101374082
+ path.moveTo(1147.3323974609375, 2019.6457519531250);
+ path.lineTo(1147.8063964843750, 2019.1651611328125);
+ // Segment 1147.8064220484741 2019.1652101374082 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
+ path.lineTo(1147.8063964843750, 2019.1651611328125);
+ // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2019.6457400321960
+ path.lineTo(1147.3323974609375, 2019.6457519531250);
+ path.close();
+ // start loop, contour: 10
+ // Segment 1145.3381097316742 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
+ path.moveTo(1145.3381347656250, 2018.3533935546875);
+ path.lineTo(1156.6848144531250, 2018.3533935546875);
+ // Segment 1156.6848182678223 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2017.0033947825432
+ path.lineTo(1156.6848144531250, 2017.0034179687500);
+ // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
+ path.lineTo(1145.3381347656250, 2017.0034179687500);
+ // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2018.3533948063850
+ path.lineTo(1145.3381347656250, 2018.3533935546875);
+ path.close();
+ // start loop, contour: 11
+ // Segment 1156.6848182678223 2018.3533948063850 0.3569631313191 0.0000000000000 -0.2645167304388 0.2609454237780 1157.6574279406423 2017.9723661860094
+ path.moveTo(1156.6848144531250, 2018.3533935546875);
+ path.cubicTo(1157.0417480468750, 2018.3533935546875, 1157.3929443359375, 2018.2332763671875, 1157.6574707031250, 2017.9724121093750);
+ // Segment 1157.6574279406423 2017.9723661860094 0.2653344079822 -0.2617520616521 0.0000000000000 0.3596905289350 1158.0474975705147 2017.0000000000000
+ path.cubicTo(1157.9227294921875, 2017.7105712890625, 1158.0474853515625, 2017.3597412109375, 1158.0474853515625, 2017.0000000000000);
+ // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6974975466728 2017.0000000000000
+ path.lineTo(1156.6975097656250, 2017.0000000000000);
+ // Segment 1156.6974975466728 2017.0000000000000 0.0028009248351 0.0403311981485 0.0118595244351 -0.0220843520393 1156.6941780622435 2017.0325257649940
+ path.cubicTo(1156.7003173828125, 2017.0402832031250, 1156.7060546875000, 2017.0104980468750, 1156.6942138671875, 2017.0324707031250);
+ // Segment 1156.6941780622435 2017.0325257649940 -0.0032637855860 0.0184860248562 0.0120617528380 -0.0065934603083 1156.7093435710913 2017.0113063061967
+ path.cubicTo(1156.6909179687500, 2017.0510253906250, 1156.7214355468750, 2017.0047607421875, 1156.7093505859375, 2017.0113525390625);
+ // split at 0.4496445953846
+ // path.cubicTo(1156.6927490234375, 2017.0407714843750, 1156.6981201171875, 2017.0360107421875, 1156.7033691406250, 2017.0289306640625);
+ // path.cubicTo(1156.7097167968750, 2017.0201416015625, 1156.7159423828125, 2017.0076904296875, 1156.7093505859375, 2017.0113525390625);
+ // Segment 1156.7093435710913 2017.0113063061967 -0.0070717276929 0.0122220954353 0.0203483811973 -0.0039136894418 1156.7268834554304 2016.9985353221975
+ path.cubicTo(1156.7022705078125, 2017.0235595703125, 1156.7471923828125, 2016.9946289062500, 1156.7269287109375, 2016.9985351562500);
+ // Segment 1156.7268834554304 2016.9985353221975 -0.0244396787691 0.0123649140586 0.0433322464027 0.0026558844666 1156.6848182678223 2017.0033947825432
+ path.cubicTo(1156.7023925781250, 2017.0108642578125, 1156.7281494140625, 2017.0061035156250, 1156.6848144531250, 2017.0034179687500);
+ // split at 0.4418420493603
+ // path.cubicTo(1156.7160644531250, 2017.0040283203125, 1156.7150878906250, 2017.0061035156250, 1156.7136230468750, 2017.0065917968750);
+ // path.cubicTo(1156.7116699218750, 2017.0070800781250, 1156.7089843750000, 2017.0048828125000, 1156.6848144531250, 2017.0034179687500);
+ // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
+ path.lineTo(1156.6848144531250, 2018.3533935546875);
+ path.close();
+ // start loop, contour: 12
+ // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 -0.3596905289350 0.2653344079822 0.2617520616521 1157.6574279406423 2016.0276338139906
+ path.moveTo(1158.0474853515625, 2017.0000000000000);
+ path.cubicTo(1158.0474853515625, 2016.6402587890625, 1157.9227294921875, 2016.2894287109375, 1157.6574707031250, 2016.0275878906250);
+ // Segment 1157.6574279406423 2016.0276338139906 -0.2645167304388 -0.2609454237780 0.3569631313191 0.0000000000000 1156.6848182678223 2015.6466051936150
+ path.cubicTo(1157.3929443359375, 2015.7667236328125, 1157.0417480468750, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
+ // split at 0.5481675863266
+ // path.cubicTo(1157.5124511718750, 2015.8846435546875, 1157.3414306640625, 2015.7839355468750, 1157.1577148437500, 2015.7220458984375);
+ // path.cubicTo(1157.0062255859375, 2015.6711425781250, 1156.8460693359375, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
+ // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
+ path.lineTo(1156.6848144531250, 2016.9965820312500);
+ // Segment 1156.6848182678223 2016.9966052174568 0.0433322464027 -0.0026558844666 -0.0244396787691 -0.0123649140586 1156.7268834554304 2017.0014646778025
+ path.cubicTo(1156.7281494140625, 2016.9938964843750, 1156.7023925781250, 2016.9891357421875, 1156.7269287109375, 2017.0014648437500);
+ // split at 0.5581579208374
+ // path.cubicTo(1156.7089843750000, 2016.9951171875000, 1156.7116699218750, 2016.9929199218750, 1156.7136230468750, 2016.9934082031250);
+ // path.cubicTo(1156.7150878906250, 2016.9938964843750, 1156.7160644531250, 2016.9959716796875, 1156.7269287109375, 2017.0014648437500);
+ // Segment 1156.7268834554304 2017.0014646778025 0.0203483811973 0.0039136894418 -0.0070717276929 -0.0122220954353 1156.7093435710913 2016.9886936938033
+ path.cubicTo(1156.7471923828125, 2017.0053710937500, 1156.7022705078125, 2016.9764404296875, 1156.7093505859375, 2016.9886474609375);
+ // Segment 1156.7093435710913 2016.9886936938033 0.0120617528380 0.0065934603083 -0.0032637855860 -0.0184860248562 1156.6941780622435 2016.9674742350060
+ path.cubicTo(1156.7214355468750, 2016.9952392578125, 1156.6909179687500, 2016.9489746093750, 1156.6942138671875, 2016.9675292968750);
+ // Segment 1156.6941780622435 2016.9674742350060 0.0118595244351 0.0220843520393 0.0028009248351 -0.0403311981485 1156.6974975466728 2017.0000000000000
+ path.cubicTo(1156.7060546875000, 2016.9895019531250, 1156.7003173828125, 2016.9597167968750, 1156.6975097656250, 2017.0000000000000);
+ // split at 0.4572408795357
+ // path.cubicTo(1156.6995849609375, 2016.9775390625000, 1156.7014160156250, 2016.9768066406250, 1156.7014160156250, 2016.9768066406250);
+ // path.cubicTo(1156.7014160156250, 2016.9769287109375, 1156.6989746093750, 2016.9781494140625, 1156.6975097656250, 2017.0000000000000);
+ // Segment 1156.6974975466728 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1158.0474975705147 2017.0000000000000
+ path.lineTo(1158.0474853515625, 2017.0000000000000);
+ path.close();
+ // start loop, contour: 13
+ // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2015.6466051936150
+ path.moveTo(1156.6848144531250, 2015.6466064453125);
+ path.lineTo(1145.3381347656250, 2015.6466064453125);
+ // Segment 1145.3381097316742 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
+ path.lineTo(1145.3381347656250, 2016.9965820312500);
+ // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
+ path.lineTo(1156.6848144531250, 2016.9965820312500);
+ // Segment 1156.6848182678223 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2015.6466051936150
+ path.lineTo(1156.6848144531250, 2015.6466064453125);
+ path.close();
+ // start loop, contour: 14
+ // Segment 1145.8121519375022 2016.8021351246741 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220237907 2014.8347900061515
+ path.moveTo(1145.8121337890625, 2016.8021240234375);
+ path.lineTo(1147.8063964843750, 2014.8348388671875);
+ // Segment 1147.8064220237907 2014.8347900061515 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583376121346 2013.8737301678750
+ path.lineTo(1146.8583984375000, 2013.8737792968750);
+ // Segment 1146.8583376121346 2013.8737301678750 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
+ path.lineTo(1144.8640136718750, 2015.8410644531250);
+ // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519375022 2016.8021351246741
+ path.lineTo(1145.8121337890625, 2016.8021240234375);
+ path.close();
+ // start loop, contour: 15
+ // Segment 1147.8064220516883 2014.8347899786306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2012.9239773430752
+ path.moveTo(1147.8063964843750, 2014.8348388671875);
+ path.cubicTo(1148.3494873046875, 2014.2990722656250, 1148.3494873046875, 2013.4597167968750, 1147.8063964843750, 2012.9239501953125);
+ // Segment 1147.8064220516883 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2013.8850371263100
+ path.lineTo(1146.8583984375000, 2013.8850097656250);
+ // Segment 1146.8583375842370 2013.8850371263100 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2013.8737301953959
+ path.cubicTo(1146.8654785156250, 2013.8920898437500, 1146.8654785156250, 2013.8666992187500, 1146.8583984375000, 2013.8737792968750);
+ // Segment 1146.8583375842370 2013.8737301953959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
+ path.lineTo(1147.8063964843750, 2014.8348388671875);
+ path.close();
+ // start loop, contour: 16
+ // Segment 1147.8064220516883 2012.9239773430752 -0.5379138488298 -0.5306514472866 0.5379138488298 -0.5306514472866 1145.8955864341058 2012.9239773430752
+ path.moveTo(1147.8063964843750, 2012.9239501953125);
+ path.cubicTo(1147.2685546875000, 2012.3933105468750, 1146.4334716796875, 2012.3933105468750, 1145.8956298828125, 2012.9239501953125);
+ // Segment 1145.8955864341058 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
+ path.lineTo(1146.8436279296875, 2013.8850097656250);
+ // Segment 1146.8436709015571 2013.8850371263100 0.0122295718664 -0.0120644598103 -0.0122295718664 -0.0120644598103 1146.8583375842370 2013.8850371263100
+ path.cubicTo(1146.8559570312500, 2013.8729248046875, 1146.8460693359375, 2013.8729248046875, 1146.8583984375000, 2013.8850097656250);
+ // Segment 1146.8583375842370 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2012.9239773430752
+ path.lineTo(1147.8063964843750, 2012.9239501953125);
+ path.close();
+ // start loop, contour: 17
+ // Segment 1145.8955864579798 2012.9239773195236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615803600 2016.0445936810224
+ path.moveTo(1145.8956298828125, 2012.9239501953125);
+ path.lineTo(1142.7322998046875, 2016.0445556640625);
+ // Segment 1142.7322615803600 2016.0445936810224 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460000633 2017.0056535113604
+ path.lineTo(1143.6802978515625, 2017.0056152343750);
+ // Segment 1143.6803460000633 2017.0056535113604 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708776831 2013.8850371498615
+ path.lineTo(1146.8436279296875, 2013.8850097656250);
+ // Segment 1146.8436708776831 2013.8850371498615 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864579798 2012.9239773195236
+ path.lineTo(1145.8956298828125, 2012.9239501953125);
+ path.close();
+ // start loop, contour: 18
+ // Segment 1142.7322615564860 2016.0445937045740 -0.0343838913237 0.0339196727021 0.0561572931720 -0.0710493024751 1142.5744069596683 2016.2183613784646
+ path.moveTo(1142.7322998046875, 2016.0445556640625);
+ path.cubicTo(1142.6978759765625, 2016.0784912109375, 1142.6306152343750, 2016.1473388671875, 1142.5744628906250, 2016.2183837890625);
+ // Segment 1142.5744069596683 2016.2183613784646 -0.0547779032556 0.0720510806539 0.0000000000000 -0.2570904015602 1142.3937679156661 2016.7286419868469
+ path.cubicTo(1142.5196533203125, 2016.2904052734375, 1142.3937988281250, 2016.4715576171875, 1142.3937988281250, 2016.7286376953125);
+ // Segment 1142.3937679156661 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
+ path.lineTo(1143.7437744140625, 2016.7286376953125);
+ // Segment 1143.7437679395080 2016.7286419868469 -0.0051909534315 0.0665915567290 0.0133980913650 -0.0361675066532 1143.6976291086639 2016.9514128270803
+ path.cubicTo(1143.7385253906250, 2016.7952880859375, 1143.7110595703125, 2016.9152832031250, 1143.6976318359375, 2016.9514160156250);
+ // Segment 1143.6976291086639 2016.9514128270803 -0.0142876819622 0.0277028472317 0.0040377216094 -0.0063254385208 1143.6490888124401 2017.0354042045738
+ path.cubicTo(1143.6833496093750, 2016.9791259765625, 1143.6530761718750, 2017.0290527343750, 1143.6490478515625, 2017.0354003906250);
+ // Segment 1143.6490888124401 2017.0354042045738 -0.0045813437564 0.0032098513409 -0.0343840362634 0.0339198156850 1143.6803460239373 2017.0056534878088
+ path.cubicTo(1143.6445312500000, 2017.0385742187500, 1143.6459960937500, 2017.0395507812500, 1143.6802978515625, 2017.0056152343750);
+ // Segment 1143.6803460239373 2017.0056534878088 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
+ path.lineTo(1142.7322998046875, 2016.0445556640625);
+ path.close();
+ // start loop, contour: 19
+ // Segment 1142.5947256938614 2016.2481120952295 -0.1857487117715 0.1832409092043 0.0167379373694 -0.0990717748979 1142.3430278987244 2016.7518748698508
+ path.moveTo(1142.5947265625000, 2016.2481689453125);
+ path.cubicTo(1142.4089355468750, 2016.4313964843750, 1142.3597412109375, 2016.6528320312500, 1142.3430175781250, 2016.7518310546875);
+ // Segment 1142.3430278987244 2016.7518748698508 -0.0156657977007 0.1069052535795 0.0000000000000 -0.0339197441936 1142.3249999880791 2017.0000000000000
+ path.cubicTo(1142.3273925781250, 2016.8587646484375, 1142.3249511718750, 2016.9660644531250, 1142.3249511718750, 2017.0000000000000);
+ // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6750000119209 2017.0000000000000
+ path.lineTo(1143.6750488281250, 2017.0000000000000);
+ // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 -0.0339197441936 -0.0015261841961 -0.0051459911965 1143.6741640831724 2016.9767671169961
+ path.cubicTo(1143.6750488281250, 2016.9660644531250, 1143.6726074218750, 2016.9716796875000, 1143.6741943359375, 2016.9768066406250);
+ // Segment 1143.6741640831724 2016.9767671169961 -0.0007886982052 0.0013596649622 0.0074114058388 -0.0224954551713 1143.6525251830094 2017.0486861571169
+ path.cubicTo(1143.6733398437500, 2016.9781494140625, 1143.6599121093750, 2017.0262451171875, 1143.6524658203125, 2017.0487060546875);
+ // split at 0.4203657805920
+ // path.cubicTo(1143.6738281250000, 2016.9774169921875, 1143.6712646484375, 2016.9862060546875, 1143.6678466796875, 2016.9979248046875);
+ // path.cubicTo(1143.6630859375000, 2017.0140380859375, 1143.6567382812500, 2017.0356445312500, 1143.6524658203125, 2017.0487060546875);
+ // Segment 1143.6525251830094 2017.0486861571169 -0.0119644334077 0.0236755853369 0.0381324473830 -0.0447670202574 1143.5428101613127 2017.2091718784643
+ path.cubicTo(1143.6405029296875, 2017.0723876953125, 1143.5809326171875, 2017.1644287109375, 1143.5428466796875, 2017.2092285156250);
+ // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2016.2481120952295
+ path.lineTo(1142.5947265625000, 2016.2481689453125);
+ path.close();
+ // start loop, contour: 20
+ // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0339197441936 -0.0156657977007 -0.1069052535795 1142.3430278987244 2017.2481251301492
+ path.moveTo(1142.3249511718750, 2017.0000000000000);
+ path.cubicTo(1142.3249511718750, 2017.0339355468750, 1142.3273925781250, 2017.1412353515625, 1142.3430175781250, 2017.2481689453125);
+ // Segment 1142.3430278987244 2017.2481251301492 0.0167379373694 0.0990717748979 -0.1857487117715 -0.1832409092043 1142.5947256938614 2017.7518879047705
+ path.cubicTo(1142.3597412109375, 2017.3471679687500, 1142.4089355468750, 2017.5686035156250, 1142.5947265625000, 2017.7518310546875);
+ // split at 0.4008532166481
+ // path.cubicTo(1142.3497314453125, 2017.2878417968750, 1142.3616943359375, 2017.3471679687500, 1142.3854980468750, 2017.4158935546875);
+ // path.cubicTo(1142.4211425781250, 2017.5185546875000, 1142.4833984375000, 2017.6420898437500, 1142.5947265625000, 2017.7518310546875);
+ // Segment 1142.5947256938614 2017.7518879047705 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2016.7908281215357
+ path.lineTo(1143.5428466796875, 2016.7907714843750);
+ // Segment 1143.5428101613127 2016.7908281215357 0.0381324473830 0.0447670202574 -0.0119644334077 -0.0236755853369 1143.6525251830094 2016.9513138428831
+ path.cubicTo(1143.5809326171875, 2016.8355712890625, 1143.6405029296875, 2016.9276123046875, 1143.6524658203125, 2016.9512939453125);
+ // Segment 1143.6525251830094 2016.9513138428831 0.0074114058388 0.0224954551713 -0.0007886982052 -0.0013596649622 1143.6741640831724 2017.0232328830039
+ path.cubicTo(1143.6599121093750, 2016.9737548828125, 1143.6733398437500, 2017.0218505859375, 1143.6741943359375, 2017.0231933593750);
+ // Segment 1143.6741640831724 2017.0232328830039 -0.0015261841961 0.0051459911965 0.0000000000000 0.0339197441936 1143.6750000119209 2017.0000000000000
+ path.cubicTo(1143.6726074218750, 2017.0283203125000, 1143.6750488281250, 2017.0339355468750, 1143.6750488281250, 2017.0000000000000);
+ // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.3249999880791 2017.0000000000000
+ path.lineTo(1142.3249511718750, 2017.0000000000000);
+ path.close();
+ // start loop, contour: 21
+ // Segment 1142.5947256938614 2017.7518879047705 -0.0799271403989 -0.1522613934208 -0.2174629955730 -0.2879403701950 1142.7322615564860 2017.9554062954260
+ path.moveTo(1142.5947265625000, 2017.7518310546875);
+ path.cubicTo(1142.5147705078125, 2017.5996093750000, 1142.5147705078125, 2017.6674804687500, 1142.7322998046875, 2017.9554443359375);
+ // Segment 1142.7322615564860 2017.9554062954260 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460239373 2016.9943465121912
+ path.lineTo(1143.6802978515625, 2016.9943847656250);
+ // Segment 1143.6803460239373 2016.9943465121912 0.0799271403989 0.1522613934208 0.2174629955730 0.2879403701950 1143.5428101613127 2016.7908281215357
+ path.cubicTo(1143.7602539062500, 2017.1466064453125, 1143.7602539062500, 2017.0787353515625, 1143.5428466796875, 2016.7907714843750);
+ // Segment 1143.5428101613127 2016.7908281215357 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2017.7518879047705
+ path.lineTo(1142.5947265625000, 2017.7518310546875);
+ path.close();
+ // start loop, contour: 22
+ // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864522438 2021.0760227493236
+ path.moveTo(1142.7322998046875, 2017.9554443359375);
+ path.lineTo(1145.8956298828125, 2021.0760498046875);
+ // Segment 1145.8955864522438 2021.0760227493236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
+ path.lineTo(1146.8436279296875, 2020.1149902343750);
+ // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460057993 2016.9943464942983
+ path.lineTo(1143.6802978515625, 2016.9943847656250);
+ // Segment 1143.6803460057993 2016.9943464942983 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
+ path.lineTo(1142.7322998046875, 2017.9554443359375);
+ path.close();
+ // start loop, contour: 23
+ // Segment 1145.8955864341058 2021.0760227314306 0.2730164534637 0.2693304447891 -0.3016608168437 0.0000000000000 1146.8510041236877 2021.4740112423897
+ path.moveTo(1145.8956298828125, 2021.0760498046875);
+ path.cubicTo(1146.1685791015625, 2021.3453369140625, 1146.5493164062500, 2021.4739990234375, 1146.8509521484375, 2021.4739990234375);
+ // Segment 1146.8510041236877 2021.4740112423897 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2020.1240112185478
+ path.lineTo(1146.8509521484375, 2020.1240234375000);
+ // Segment 1146.8510041236877 2020.1240112185478 -0.0031276099109 0.0031991747760 0.0281856144058 0.0140930868099 1146.8580791488898 2020.1202473991566
+ path.cubicTo(1146.8479003906250, 2020.1271972656250, 1146.8862304687500, 2020.1343994140625, 1146.8580322265625, 2020.1202392578125);
+ // split at 0.3845077157021
+ // path.cubicTo(1146.8497314453125, 2020.1252441406250, 1146.8547363281250, 2020.1270751953125, 1146.8596191406250, 2020.1280517578125);
+ // path.cubicTo(1146.8675537109375, 2020.1296386718750, 1146.8753662109375, 2020.1289062500000, 1146.8580322265625, 2020.1202392578125);
+ // Segment 1146.8580791488898 2020.1202473991566 -0.0369995545027 -0.0123195805663 0.0067223483810 0.0136883790721 1146.8436709015571 2020.1149629481959
+ path.cubicTo(1146.8210449218750, 2020.1079101562500, 1146.8503417968750, 2020.1286621093750, 1146.8436279296875, 2020.1149902343750);
+ // Segment 1146.8436709015571 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864341058 2021.0760227314306
+ path.lineTo(1145.8956298828125, 2021.0760498046875);
+ path.close();
+ // start loop, contour: 24
+ // Segment 1146.8510041236877 2021.4740112423897 0.3016605789999 0.0000000000000 -0.2730166120260 0.2693306012106 1147.8064220516883 2021.0760227314306
+ path.moveTo(1146.8509521484375, 2021.4739990234375);
+ path.cubicTo(1147.1527099609375, 2021.4739990234375, 1147.5334472656250, 2021.3453369140625, 1147.8063964843750, 2021.0760498046875);
+ // Segment 1147.8064220516883 2021.0760227314306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1149629481959
+ path.lineTo(1146.8583984375000, 2020.1149902343750);
+ // Segment 1146.8583375842370 2020.1149629481959 -0.0067222671256 0.0136883164611 0.0369996293611 -0.0123196021258 1146.8439293663473 2020.1202473404985
+ path.cubicTo(1146.8515625000000, 2020.1286621093750, 1146.8809814453125, 2020.1079101562500, 1146.8438720703125, 2020.1202392578125);
+ // Segment 1146.8439293663473 2020.1202473404985 -0.0281857033438 0.0140931104690 0.0031276541428 0.0031991704542 1146.8510041236877 2020.1240112185478
+ path.cubicTo(1146.8157958984375, 2020.1343994140625, 1146.8541259765625, 2020.1271972656250, 1146.8509521484375, 2020.1240234375000);
+ // Segment 1146.8510041236877 2020.1240112185478 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2021.4740112423897
+ path.lineTo(1146.8509521484375, 2021.4739990234375);
+ path.close();
+ // start loop, contour: 25
+ // Segment 1147.8064220516883 2021.0760227314306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2019.1652101405787
+ path.moveTo(1147.8063964843750, 2021.0760498046875);
+ path.cubicTo(1148.3494873046875, 2020.5402832031250, 1148.3494873046875, 2019.7009277343750, 1147.8063964843750, 2019.1651611328125);
+ // Segment 1147.8064220516883 2019.1652101405787 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1262699238134
+ path.lineTo(1146.8583984375000, 2020.1262207031250);
+ // Segment 1146.8583375842370 2020.1262699238134 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2020.1149629481959
+ path.cubicTo(1146.8654785156250, 2020.1333007812500, 1146.8654785156250, 2020.1079101562500, 1146.8583984375000, 2020.1149902343750);
+ // Segment 1146.8583375842370 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2021.0760227314306
+ path.lineTo(1147.8063964843750, 2021.0760498046875);
+ path.close();
+ // start loop, contour: 26
+ // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519520594 2017.1978648896866
+ path.moveTo(1147.8063964843750, 2019.1651611328125);
+ path.lineTo(1145.8121337890625, 2017.1978759765625);
+ // Segment 1145.8121519520594 2017.1978648896866 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
+ path.lineTo(1144.8640136718750, 2018.1589355468750);
+ // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375975775 2020.1262699369736
+ path.lineTo(1146.8583984375000, 2020.1262207031250);
+ // Segment 1146.8583375975775 2020.1262699369736 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
+ path.lineTo(1147.8063964843750, 2019.1651611328125);
+ path.close();
+
+testSimplify(reporter, path, filename);
+}
+
+static void testQuads66(skiatest::Reporter* reporter,const char* filename) {
+ SkPath path;
+ path.moveTo(2, 0);
+ path.quadTo(3, 1, 2, 2);
+ path.lineTo(2, 3);
+ path.close();
+ path.moveTo(2, 1);
+ path.lineTo(2, 1);
+ path.quadTo(1, 2, 2, 2);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads67(skiatest::Reporter* reporter,const char* filename) {
+ SkPath path;
+ path.moveTo(3, 2);
+ path.quadTo(1, 3, 3, 3);
+ path.lineTo(3, 3);
+ path.close();
+ path.moveTo(0, 0);
+ path.lineTo(1, 0);
+ path.quadTo(2, 3, 3, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads68(skiatest::Reporter* reporter,const char* filename) {
+ SkPath path;
+ path.moveTo(1, 2);
+ path.quadTo(0, 3, 2, 3);
+ path.lineTo(2, 3);
+ path.close();
+ path.moveTo(1, 0);
+ path.lineTo(0, 1);
+ path.quadTo(1, 3, 2, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads69(skiatest::Reporter* reporter,const char* filename) {
+ SkPath path;
+ path.moveTo(1, 0);
+ path.quadTo(2, 2, 2, 3);
+ path.lineTo(2, 3);
+ path.close();
+ path.moveTo(1, 0);
+ path.lineTo(1, 0);
+ path.quadTo(3, 0, 1, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads70(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.moveTo(1, 1);
+ path.quadTo(2, 3, 3, 3);
+ path.lineTo(3, 3);
+ path.close();
+ path.moveTo(2, 0);
+ path.lineTo(2, 2);
+ path.quadTo(1, 3, 3, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads71(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.moveTo(1, 1);
+ path.quadTo(2, 3, 3, 3);
+ path.lineTo(3, 3);
+ path.close();
+ path.moveTo(3, 0);
+ path.lineTo(2, 2);
+ path.quadTo(1, 3, 3, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
+static void testQuads72(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.moveTo(1, 1);
+ path.quadTo(2, 3, 3, 3);
+ path.lineTo(3, 3);
+ path.close();
+ path.moveTo(0, 1);
+ path.lineTo(2, 2);
+ path.quadTo(1, 3, 3, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
@@ -5109,8 +5785,20 @@ static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static TestDesc tests[] = {
+ TEST(testQuads72),
+ TEST(testQuads71),
+ TEST(testQuads70),
+ TEST(testQuads69),
+ TEST(testQuads68),
+ TEST(testQuads67),
+ TEST(testQuads66),
+ TEST(dean4),
+ TEST(tiger8a_h_1),
+ TEST(tiger8a_h),
+ TEST(tiger8a),
+ TEST(tiger8b),
+ TEST(tiger8),
TEST(fuzz763_4713_b),
- TEST(fuzz_59),
TEST(fuzz_twister2),
TEST(fuzz_twister),
TEST(fuzz994s_3414),
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 3c24aeb138..65868cdcfd 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -6,51 +6,18 @@
<title></title>
<div style="height:0">
-<div id="sect0">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+<div id="angle">
+{{{2, 6, 1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f}}}
+{{{1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6}}}
+{{{1.995156049728393555, 5.980457782745361328}, {2.08147298604749853, 5.917692615073925744}, {2.164281118403629023, 5.850987095630077128}, {2.242042064666748047, 5.780299663543701172}}}
+{{{1.995156049728393555, 5.980457782745361328}, {1.82665117196054072, 6.185735619599722845}, {1.80264212281170999, 5.19703332512428684}, {1.994958639144897461, 5.979661464691162109}}}
+{{{1.995156049728393555, 5.980457782745361328}, {1.825196881732315868, 6.187507280789372288}, {1.801190554235020613, 5.204762216940081565}, {2, 6}}}
</div>
-<div id="sect1">
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f} id=1
-{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f} id=2
-{{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f} id=4
-</div>
-
-<div id="sect2">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f} id=3
-{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f} id=2
-</div>
-
-<div id="sect3">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f} id=3
-{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f} id=6
-</div>
-
-<div id="sect4">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f} id=3
-{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f} id=6
-</div>
-
-<div id="sect5">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f} id=3
-{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f} id=6
-</div>
-
-<div id="sect6">
-{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
-{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
-{{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f} id=3
-{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f} id=6
+<div id="ref">
+{{{0.7153972983360290527, 4.712343692779541016}, {0.2472269223126296878, 4.55502436068874772}, {-0.1220090791716240131, 4.244018092892478222}, {0, 4}}},
+{{{0.7153972983360290527, 4.712343692779541016}, {0.1339736781445877156, 4.133975051508096854}, {0.7320473976783675729, 3.63397630116081638}, {2, 3}}},
+{{fX=-0.0012699038296868359 fY=-0.0012605104293301750 } {fX=-0.0025337575085910835 fY=-0.0025229424048465177 }
</div>
</div>
@@ -58,13 +25,8 @@
<script type="text/javascript">
var testDivs = [
-sect0,
-sect1,
-sect2,
-sect3,
-sect4,
-sect5,
-sect6,
+ angle,
+ ref,
];
var decimal_places = 3;
@@ -499,7 +461,7 @@ function dxy_at_t(curve, t) {
function drawPointAtT(curve) {
var x = x_at_t(curve, curveT);
var y = y_at_t(curve, curveT);
- drawPoint(x, y);
+ drawPoint(x, y, false);
}
function drawLine(x1, y1, x2, y2) {
@@ -511,7 +473,7 @@ function dxy_at_t(curve, t) {
ctx.stroke();
}
- function drawPoint(px, py) {
+ function drawPoint(px, py, xend) {
for (var pts = 0; pts < drawnPts.length; pts += 2) {
var x = drawnPts[pts];
var y = drawnPts[pts + 1];
@@ -524,8 +486,15 @@ function dxy_at_t(curve, t) {
var _px = (px - srcLeft) * scale;
var _py = (py - srcTop) * scale;
ctx.beginPath();
- ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
- ctx.closePath();
+ if (xend) {
+ ctx.moveTo(_px - 3, _py - 3);
+ ctx.lineTo(_px + 3, _py + 3);
+ ctx.moveTo(_px - 3, _py + 3);
+ ctx.lineTo(_px + 3, _py - 3);
+ } else {
+ ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
+ ctx.closePath();
+ }
ctx.stroke();
if (draw_point_xy) {
var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
@@ -537,7 +506,7 @@ function dxy_at_t(curve, t) {
}
function drawPointSolid(px, py) {
- drawPoint(px, py);
+ drawPoint(px, py, false);
ctx.fillStyle = "rgba(0,0,0, 0.4)";
ctx.fill();
}
@@ -814,15 +783,17 @@ function dxy_at_t(curve, t) {
}
ctx.stroke();
if (draw_endpoints > 0) {
- drawPoint(curve[0], curve[1]);
+ drawPoint(curve[0], curve[1], false);
if (draw_endpoints > 1 || curve.length == 4) {
- drawPoint(curve[2], curve[3]);
+ drawPoint(curve[2], curve[3], curve.length == 4 && draw_endpoints == 3);
}
if (curve.length == 6 || curve.length == 7 ||
(draw_endpoints > 1 && curve.length == 8)) {
- drawPoint(curve[4], curve[5]);
+ drawPoint(curve[4], curve[5], (curve.length == 6 || curve.length == 7) && draw_endpoints == 3);
+ }
+ if (curve.length == 8) {
+ drawPoint(curve[6], curve[7], curve.length == 8 && draw_endpoints == 3);
}
- if (curve.length == 8) drawPoint(curve[6], curve[7]);
}
if (draw_midpoint != 0) {
if ((curves == 0) == (midLeft == 0)) {
@@ -1168,7 +1139,7 @@ function dxy_at_t(curve, t) {
redraw();
break;
case 'e':
- draw_endpoints = (draw_endpoints + 1) % 3;
+ draw_endpoints = (draw_endpoints + 1) % 4;
redraw();
break;
case 'f':
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index 076f5914ea..30140ad4b6 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -2,886 +2,706 @@
<head>
<div height="0" hidden="true">
-Skia UnitTests: --match Simplify$ --resourcePath resources\ SK_DEBUG
-
-<div id="fuzz763_4713_b">
-seg=1 {{{41, 33}, {41, 36.3137093f}, {38.6568527f, 38.6568527f}}}
-seg=2 {{{38.6568527f, 38.6568527f}, {36.3137093f, 41}, {33, 41}}}
-seg=3 {{{33, 41}, {29.6862907f, 41}, {27.3431454f, 38.6568527f}}}
-seg=4 {{{27.3431454f, 38.6568527f}, {25, 36.3137093f}, {25, 33}}}
-seg=5 {{{25, 33}, {25, 29.6862907f}, {27.3431454f, 27.3431454f}}}
-seg=6 {{{27.3431454f, 27.3431454f}, {29.6862907f, 25}, {33, 25}}}
-seg=7 {{{33, 25}, {36.3137093f, 25}, {38.6568527f, 27.3431454f}}}
-seg=8 {{{38.6568527f, 27.3431454f}, {41, 29.6862907f}, {41, 33}}}
-seg=9 {{{33.2413864f, 24.6781349f}, {36.5549393f, 24.6459332f}, {38.920742f, 26.966198f}}}
-seg=10 {{{38.920742f, 26.966198f}, {41.2865486f, 29.2864628f}, {41.3187523f, 32.6000175f}}}
-seg=11 {{{41.3187523f, 32.6000175f}, {41.3509521f, 35.9135704f}, {39.0306854f, 38.2793732f}}}
-seg=12 {{{39.0306854f, 38.2793732f}, {38.9995995f, 38.3110695f}, {38.9681816f, 38.3424988f}}}
-seg=13 {{{38.9681816f, 38.3424988f}, {38.9374619f, 38.3742142f}, {38.9064751f, 38.4056053f}}}
-seg=14 {{{38.9064751f, 38.4056053f}, {38.8441086f, 38.4687881f}, {38.7809143f, 38.5304031f}}}
-seg=15 {{{38.7809143f, 38.5304031f}, {38.7196693f, 38.5940361f}, {38.6568527f, 38.6568527f}}}
-seg=16 {{{38.6568527f, 38.6568527f}, {36.3137093f, 41}, {33, 41}}}
-seg=17 {{{33, 41}, {29.6862907f, 41}, {27.3431454f, 38.6568527f}}}
-seg=18 {{{27.3431454f, 38.6568527f}, {25, 36.3137093f}, {25, 33}}}
-seg=19 {{{25, 33}, {25, 29.6862907f}, {27.3431454f, 27.3431454f}}}
-seg=20 {{{27.3431454f, 27.3431454f}, {27.3875446f, 27.2987461f}, {27.4323025f, 27.2551785f}}}
-seg=21 {{{27.4323025f, 27.2551785f}, {27.4755878f, 27.2101307f}, {27.5197105f, 27.165432f}}}
-seg=22 {{{27.5197105f, 27.165432f}, {27.541851f, 27.1430035f}, {27.5638676f, 27.1209965f}}}
-seg=23 {{{27.5638676f, 27.1209965f}, {27.5855064f, 27.0986347f}, {27.6075668f, 27.0761414f}}}
-seg=24 {{{27.6075668f, 27.0761414f}, {29.9278316f, 24.7103367f}, {33.2413864f, 24.6781349f}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33.2413864,24.6781349}, {36.5549393,24.6459332}, {38.920742,26.966198}}} {{38.920742,26.966198}} wnTs[0]=0 {{{38.920742,26.966198}, {41.2865486,29.2864628}, {41.3187523,32.6000175}}}
-debugShowQuadIntersection wtTs[0]=0 {{{33.2413864,24.6781349}, {36.5549393,24.6459332}, {38.920742,26.966198}}} {{33.2413864,24.6781349}} wnTs[0]=1 {{{27.6075668,27.0761414}, {29.9278316,24.7103367}, {33.2413864,24.6781349}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.920742,26.966198}, {41.2865486,29.2864628}, {41.3187523,32.6000175}}} {{41.3187523,32.6000175}} wnTs[0]=0 {{{41.3187523,32.6000175}, {41.3509521,35.9135704}, {39.0306854,38.2793732}}}
-debugShowQuadIntersection wtTs[0]=1 {{{41.3187523,32.6000175}, {41.3509521,35.9135704}, {39.0306854,38.2793732}}} {{39.0306854,38.2793732}} wnTs[0]=0 {{{39.0306854,38.2793732}, {38.9995995,38.3110695}, {38.9681816,38.3424988}}}
-debugShowQuadIntersection wtTs[0]=1 {{{39.0306854,38.2793732}, {38.9995995,38.3110695}, {38.9681816,38.3424988}}} {{38.9681816,38.3424988}} wnTs[0]=0 {{{38.9681816,38.3424988}, {38.9374619,38.3742142}, {38.9064751,38.4056053}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.9681816,38.3424988}, {38.9374619,38.3742142}, {38.9064751,38.4056053}}} {{38.9064751,38.4056053}} wnTs[0]=0 {{{38.9064751,38.4056053}, {38.8441086,38.4687881}, {38.7809143,38.5304031}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.9064751,38.4056053}, {38.8441086,38.4687881}, {38.7809143,38.5304031}}} {{38.7809143,38.5304031}} wnTs[0]=0 {{{38.7809143,38.5304031}, {38.7196693,38.5940361}, {38.6568527,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.7809143,38.5304031}, {38.7196693,38.5940361}, {38.6568527,38.6568527}}} {{38.6568527,38.6568527}} wnTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{33,41}} wnTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{27.3431454,38.6568527}} wnTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{25,33}} wnTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{27.3431454,27.3431454}} wnTs[0]=0 {{{27.3431454,27.3431454}, {27.3875446,27.2987461}, {27.4323025,27.2551785}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,27.3431454}, {27.3875446,27.2987461}, {27.4323025,27.2551785}}} {{27.4323025,27.2551785}} wnTs[0]=0 {{{27.4323025,27.2551785}, {27.4755878,27.2101307}, {27.5197105,27.165432}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.4323025,27.2551785}, {27.4755878,27.2101307}, {27.5197105,27.165432}}} {{27.5197105,27.165432}} wnTs[0]=0 {{{27.5197105,27.165432}, {27.541851,27.1430035}, {27.5638676,27.1209965}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.5197105,27.165432}, {27.541851,27.1430035}, {27.5638676,27.1209965}}} {{27.5638676,27.1209965}} wnTs[0]=0 {{{27.5638676,27.1209965}, {27.5855064,27.0986347}, {27.6075668,27.0761414}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.5638676,27.1209965}, {27.5855064,27.0986347}, {27.6075668,27.0761414}}} {{27.6075668,27.0761414}} wnTs[0]=0 {{{27.6075668,27.0761414}, {29.9278316,24.7103367}, {33.2413864,24.6781349}}}
-id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [3,1]
-id=1 1=(0,0.5) [2] 3=(0.5,1) [4] id=2 2=(0,0.5) [1] 4=(0.5,1) [3]
-id=1 3=(0.5,1) [4] id=2 4=(0.5,1) [3]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{33.2413864,24.6781349}, {36.5549393,24.6459332}, {38.920742,26.966198}}} {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{38.920742,26.966198}, {41.2865486,29.2864628}, {41.3187523,32.6000175}}} {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-id=1 1=(0,0.5) [2] 3=(0.5,1) [2] id=2 2=(0,1) [3,1]
-id=1 1=(0,0.5) [2] 3=(0.5,1) [4,2] id=2 2=(0,0.5) [3,1] 4=(0.5,1) [3]
-id=1 3=(0.5,1) [4,2] id=2 2=(0,0.5) [3] 4=(0.5,1) [3]
-id=1 3=(0.5,1) [6,4] id=2 6=(0.25,0.5) [3] 4=(0.5,1) [3]
-id=1 3=(0.5,0.75) [4] 7=(0.75,1) [4] id=2 4=(0.5,1) [7,3]
-id=1 7=(0.75,1) [8,4] id=2 4=(0.5,0.75) [7] 8=(0.75,1) [7]
-id=1 7=(0.75,1) [10,8] id=2 10=(0.625,0.75) [7] 8=(0.75,1) [7]
-id=1 9=(0.875,1) [8] id=2 8=(0.75,1) [9]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{41.3187523,32.6000175}, {41.3509521,35.9135704}, {39.0306854,38.2793732}}} {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-debugShowQuadIntersection no intersect {{{41.3187523,32.6000175}, {41.3509521,35.9135704}, {39.0306854,38.2793732}}} {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-id=1 1=(0,1) [6] id=2 6=(0.75,1) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{39.0306854,38.2793732}, {38.9995995,38.3110695}, {38.9681816,38.3424988}}} {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-id=1 1=(0,1) [6] id=2 6=(0.75,1) [1]
-id=1 1=(0,1) [8] id=2 8=(0.875,1) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{38.9681816,38.3424988}, {38.9374619,38.3742142}, {38.9064751,38.4056053}}} {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-id=1 1=(0,1) [6] id=2 6=(0.75,1) [1]
-id=1 1=(0,1) [8] id=2 8=(0.875,1) [1]
-id=1 1=(0,1) [10] id=2 10=(0.9375,1) [1]
-id=1 1=(0,1) [12,10] id=2 10=(0.9375,0.96875) [1] 12=(0.96875,1) [1]
-id=1 1=(0,1) [14,12,10] id=2 10=(0.9375,0.953125) [1] 14=(0.953125,0.96875) [1] 12=(0.96875,1) [1]
-id=1 1=(0,1) [14,12,10] id=2 10=(0.9375,0.953125) [1] 14=(0.953125,0.96875) [1] 12=(0.96875,0.984375) [1]
-id=1 3=(0.5,1) [12] id=2 12=(0.96875,0.984375) [3]
-id=1 3=(0.5,1) [12] id=2 12=(0.96875,0.976563) [3]
-id=1 5=(0.75,1) [12] id=2 12=(0.96875,0.976563) [5]
-id=1 5=(0.75,1) [20,12] id=2 12=(0.96875,0.972656) [5] 20=(0.972656,0.976563) [5]
-id=1 7=(0.875,1) [20] id=2 20=(0.972656,0.976563) [7]
-id=1 7=(0.875,1) [20] id=2 20=(0.972656,0.974609) [7]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{38.9064751,38.4056053}, {38.8441086,38.4687881}, {38.7809143,38.5304031}}} {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-id=1 1=(0,1) [4] id=2 4=(0.5,1) [1]
-id=1 1=(0,1) [6] id=2 6=(0.75,1) [1]
-id=1 1=(0,1) [8] id=2 8=(0.875,1) [1]
-id=1 1=(0,1) [10] id=2 10=(0.9375,1) [1]
-id=1 1=(0,1) [12] id=2 12=(0.96875,1) [1]
-id=1 1=(0,1) [14,12] id=2 12=(0.96875,0.984375) [1] 14=(0.984375,1) [1]
-id=1 1=(0,0.5) [14,12] 3=(0.5,1) [14] id=2 12=(0.96875,0.984375) [1] 14=(0.984375,1) [3,1]
-id=1 1=(0,0.5) [16,14,12] 3=(0.5,1) [14] id=2 12=(0.96875,0.976563) [1] 16=(0.976563,0.984375) [1] 14=(0.984375,1) [3,1]
-id=1 1=(0,0.5) [16,14,12] 3=(0.5,1) [18,14] id=2 12=(0.96875,0.976563) [1] 16=(0.976563,0.984375) [1] 14=(0.984375,0.992188) [3,1] 18=(0.992188,1) [3]
-id=1 1=(0,0.25) [16,12] 5=(0.25,0.5) [14,16] 3=(0.5,1) [18,14] id=2 12=(0.96875,0.976563) [1] 16=(0.976563,0.984375) [5,1] 14=(0.984375,0.992188) [5,3] 18=(0.992188,1) [3]
-id=1 1=(0,0.25) [16,12] 5=(0.25,0.5) [14,16] 3=(0.5,0.75) [18,14] 7=(0.75,1) [18] id=2 12=(0.96875,0.976563) [1] 16=(0.976563,0.984375) [5,1] 14=(0.984375,0.992188) [5,3] 18=(0.992188,1) [7,3]
-id=1 1=(0,0.25) [20,16] 5=(0.25,0.5) [14,16] 3=(0.5,0.75) [18,14] 7=(0.75,1) [18] id=2 20=(0.972656,0.976563) [1] 16=(0.976563,0.984375) [5,1] 14=(0.984375,0.992188) [5,3] 18=(0.992188,1) [7,3]
-id=1 1=(0,0.25) [20,16] 5=(0.25,0.5) [22,14,16] 3=(0.5,0.75) [18,14] 7=(0.75,1) [18] id=2 20=(0.972656,0.976563) [1] 16=(0.976563,0.980469) [5,1] 22=(0.980469,0.984375) [5] 14=(0.984375,0.992188) [5,3] 18=(0.992188,1) [7,3]
-id=1 1=(0,0.25) [20,16] 5=(0.25,0.5) [22,14,16] 3=(0.5,0.75) [24,18,14] 7=(0.75,1) [18] id=2 20=(0.972656,0.976563) [1] 16=(0.976563,0.980469) [5,1] 22=(0.980469,0.984375) [5] 14=(0.984375,0.988281) [5,3] 24=(0.988281,0.992188) [3] 18=(0.992188,1) [7,3]
-id=1 1=(0,0.25) [20,16] 5=(0.25,0.5) [22,14,16] 3=(0.5,0.75) [24,18,14] 7=(0.75,1) [26,18] id=2 20=(0.972656,0.976563) [1] 16=(0.976563,0.980469) [5,1] 22=(0.980469,0.984375) [5] 14=(0.984375,0.988281) [5,3] 24=(0.988281,0.992188) [3] 18=(0.992188,0.996094) [7,3] 26=(0.996094,1) [7]
-id=1 1=(0,0.125) [20] 9=(0.125,0.25) [16,20] 5=(0.25,0.5) [22,14,16] 3=(0.5,0.75) [24,18,14] 7=(0.75,1) [26,18] id=2 20=(0.972656,0.976563) [9,1] 16=(0.976563,0.980469) [9,5] 22=(0.980469,0.984375) [5] 14=(0.984375,0.988281) [5,3] 24=(0.988281,0.992188) [3] 18=(0.992188,0.996094) [7,3] 26=(0.996094,1) [7]
-id=1 1=(0,0.125) [20] 9=(0.125,0.25) [16,20] 5=(0.25,0.375) [22,16] 11=(0.375,0.5) [14,22] 3=(0.5,0.75) [24,18,14] 7=(0.75,1) [26,18] id=2 20=(0.972656,0.976563) [9,1] 16=(0.976563,0.980469) [9,5] 22=(0.980469,0.984375) [11,5] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [3] 18=(0.992188,0.996094) [7,3] 26=(0.996094,1) [7]
-id=1 1=(0,0.125) [20] 9=(0.125,0.25) [16,20] 5=(0.25,0.375) [22,16] 11=(0.375,0.5) [14,22] 3=(0.5,0.625) [24,14] 13=(0.625,0.75) [18,24] 7=(0.75,1) [26,18] id=2 20=(0.972656,0.976563) [9,1] 16=(0.976563,0.980469) [9,5] 22=(0.980469,0.984375) [11,5] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [7]
-id=1 1=(0,0.125) [20] 9=(0.125,0.25) [16,20] 5=(0.25,0.375) [22,16] 11=(0.375,0.5) [14,22] 3=(0.5,0.625) [24,14] 13=(0.625,0.75) [18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.976563) [9,1] 16=(0.976563,0.980469) [9,5] 22=(0.980469,0.984375) [11,5] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [28,16] 5=(0.25,0.375) [22,16] 11=(0.375,0.5) [14,22] 3=(0.5,0.625) [24,14] 13=(0.625,0.75) [18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.980469) [9,5] 22=(0.980469,0.984375) [11,5] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [30,22] 11=(0.375,0.5) [14,22] 3=(0.5,0.625) [24,14] 13=(0.625,0.75) [18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.984375) [11,5] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [32,14] 3=(0.5,0.625) [24,14] 13=(0.625,0.75) [18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.988281) [11,3] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.992188) [13,3] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.996094) [13,7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,1) [15,7]
-id=1 1=(0,0.125) [28,20] 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 20=(0.972656,0.974609) [1] 28=(0.974609,0.976563) [1,9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.974609375 cPt=(38.7743301,38.5372393) == oppT=0.0537252999 fPerpPt=(38.774329,38.5372382)
-setPerp t=0.9765625 cPt=(38.7654006,38.5464847) == oppT=0.126456412 fPerpPt=(38.7653995,38.5464837)
-setPerp t=0.0625 cPt=(38.7732525,38.5383541) == oppT=0.974845025 fPerpPt=(38.7732537,38.5383551)
-setPerp t=0.974609375 cPt=(38.7743301,38.5372393) == oppT=0.0537252999 fPerpPt=(38.774329,38.5372382)
-setPerp t=0 cPt=(38.7809143,38.5304031) == oppT=0.973166462 fPerpPt=(38.7809154,38.5304042)
-setPerp t=0.974609375 cPt=(38.7743301,38.5372393) == oppT=0.0537252999 fPerpPt=(38.774329,38.5372382)
-setPerp t=0.9765625 cPt=(38.7654006,38.5464847) == oppT=0.126456412 fPerpPt=(38.7653995,38.5464837)
-setPerp t=0.0625 cPt=(38.7732525,38.5383541) == oppT=0.974845025 fPerpPt=(38.7732537,38.5383551)
-setPerp t=0.125 cPt=(38.7655785,38.5462986) == oppT=0.976523392 fPerpPt=(38.7655796,38.5462997)
-id=1 9=(0.125,0.25) [30,28,16] 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 28=(0.974609,0.976563) [9] 16=(0.976563,0.978516) [9] 30=(0.978516,0.980469) [5,9] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.974609375 cPt=(38.7743301,38.5372393) == oppT=0.0537252999 fPerpPt=(38.774329,38.5372382)
-setPerp t=0.9765625 cPt=(38.7654006,38.5464847) == oppT=0.126456412 fPerpPt=(38.7653995,38.5464837)
-setPerp t=0.125 cPt=(38.7655785,38.5462986) == oppT=0.976523392 fPerpPt=(38.7655796,38.5462997)
-setPerp t=0.9765625 cPt=(38.7654006,38.5464847) == oppT=0.126456412 fPerpPt=(38.7653995,38.5464837)
-setPerp t=0.978515625 cPt=(38.7564533,38.5557228) == oppT=0.199197443 fPerpPt=(38.7564523,38.5557218)
-setPerp t=0.1875 cPt=(38.7578922,38.5542368) == oppT=0.978201562 fPerpPt=(38.7578932,38.5542378)
-setPerp t=0.9765625 cPt=(38.7654006,38.5464847) == oppT=0.126456412 fPerpPt=(38.7653995,38.5464837)
-setPerp t=0.978515625 cPt=(38.7564533,38.5557228) == oppT=0.199197443 fPerpPt=(38.7564523,38.5557218)
-setPerp t=0.1875 cPt=(38.7578922,38.5542368) == oppT=0.978201562 fPerpPt=(38.7578932,38.5542378)
-setPerp t=0.978515625 cPt=(38.7564533,38.5557228) == oppT=0.199197443 fPerpPt=(38.7564523,38.5557218)
-setPerp t=0.98046875 cPt=(38.7474881,38.5649534) == oppT=0.271948381 fPerpPt=(38.7474871,38.5649525)
-setPerp t=0.25 cPt=(38.7501936,38.5621686) == oppT=0.979879536 fPerpPt=(38.7501946,38.5621695)
-id=1 5=(0.25,0.375) [32,30,22] 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 30=(0.978516,0.980469) [5] 22=(0.980469,0.982422) [5] 32=(0.982422,0.984375) [5,11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.978515625 cPt=(38.7564533,38.5557228) == oppT=0.199197443 fPerpPt=(38.7564523,38.5557218)
-setPerp t=0.98046875 cPt=(38.7474881,38.5649534) == oppT=0.271948381 fPerpPt=(38.7474871,38.5649525)
-setPerp t=0.25 cPt=(38.7501936,38.5621686) == oppT=0.979879536 fPerpPt=(38.7501946,38.5621695)
-setPerp t=0.98046875 cPt=(38.7474881,38.5649534) == oppT=0.271948381 fPerpPt=(38.7474871,38.5649525)
-setPerp t=0.982421875 cPt=(38.738505,38.5741767) == oppT=0.344709216 fPerpPt=(38.7385041,38.5741759)
-setPerp t=0.3125 cPt=(38.7424827,38.570094) == oppT=0.981557313 fPerpPt=(38.7424836,38.5700949)
-setPerp t=0.98046875 cPt=(38.7474881,38.5649534) == oppT=0.271948381 fPerpPt=(38.7474871,38.5649525)
-setPerp t=0.982421875 cPt=(38.738505,38.5741767) == oppT=0.344709216 fPerpPt=(38.7385041,38.5741759)
-setPerp t=0.3125 cPt=(38.7424827,38.570094) == oppT=0.981557313 fPerpPt=(38.7424836,38.5700949)
-setPerp t=0.982421875 cPt=(38.738505,38.5741767) == oppT=0.344709216 fPerpPt=(38.7385041,38.5741759)
-setPerp t=0.984375 cPt=(38.729504,38.5833925) == oppT=0.417479935 fPerpPt=(38.7295033,38.5833918)
-setPerp t=0.375 cPt=(38.7347596,38.5780131) == oppT=0.983234895 fPerpPt=(38.7347604,38.5780138)
-id=1 11=(0.375,0.5) [34,32,14] 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 32=(0.982422,0.984375) [11] 14=(0.984375,0.986328) [11] 34=(0.986328,0.988281) [3,11] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.982421875 cPt=(38.738505,38.5741767) == oppT=0.344709216 fPerpPt=(38.7385041,38.5741759)
-setPerp t=0.984375 cPt=(38.729504,38.5833925) == oppT=0.417479935 fPerpPt=(38.7295033,38.5833918)
-setPerp t=0.375 cPt=(38.7347596,38.5780131) == oppT=0.983234895 fPerpPt=(38.7347604,38.5780138)
-setPerp t=0.984375 cPt=(38.729504,38.5833925) == oppT=0.417479935 fPerpPt=(38.7295033,38.5833918)
-setPerp t=0.986328125 cPt=(38.7204852,38.592601) == oppT=0.490260525 fPerpPt=(38.7204846,38.5926004)
-setPerp t=0.4375 cPt=(38.7270241,38.5859257) == oppT=0.984912281 fPerpPt=(38.7270248,38.5859264)
-setPerp t=0.984375 cPt=(38.729504,38.5833925) == oppT=0.417479935 fPerpPt=(38.7295033,38.5833918)
-setPerp t=0.986328125 cPt=(38.7204852,38.592601) == oppT=0.490260525 fPerpPt=(38.7204846,38.5926004)
-setPerp t=0.4375 cPt=(38.7270241,38.5859257) == oppT=0.984912281 fPerpPt=(38.7270248,38.5859264)
-setPerp t=0.986328125 cPt=(38.7204852,38.592601) == oppT=0.490260525 fPerpPt=(38.7204846,38.5926004)
-setPerp t=0.98828125 cPt=(38.7114485,38.601802) == oppT=0.563050975 fPerpPt=(38.711448,38.6018015)
-setPerp t=0.5 cPt=(38.7192764,38.593832) == oppT=0.986589471 fPerpPt=(38.719277,38.5938326)
-id=1 3=(0.5,0.625) [34,24] 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 34=(0.986328,0.988281) [3] 24=(0.988281,0.990234) [13,3] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.986328125 cPt=(38.7204852,38.592601) == oppT=0.490260525 fPerpPt=(38.7204846,38.5926004)
-setPerp t=0.98828125 cPt=(38.7114485,38.601802) == oppT=0.563050975 fPerpPt=(38.711448,38.6018015)
-setPerp t=0.5 cPt=(38.7192764,38.593832) == oppT=0.986589471 fPerpPt=(38.719277,38.5938326)
-setPerp t=0.5625 cPt=(38.7115164,38.6017319) == oppT=0.988266467 fPerpPt=(38.7115169,38.6017324)
-setPerp t=0.98828125 cPt=(38.7114485,38.601802) == oppT=0.563050975 fPerpPt=(38.711448,38.6018015)
-setPerp t=0.990234375 cPt=(38.7023939,38.6109956) == oppT=0.635851272 fPerpPt=(38.7023935,38.6109953)
-setPerp t=0.625 cPt=(38.7037442,38.6096255) == oppT=0.989943268 fPerpPt=(38.7037445,38.6096258)
-setPerp t=0.986328125 cPt=(38.7204852,38.592601) == oppT=0.490260525 fPerpPt=(38.7204846,38.5926004)
-setPerp t=0.98828125 cPt=(38.7114485,38.601802) == oppT=0.563050975 fPerpPt=(38.711448,38.6018015)
-setPerp t=0.5625 cPt=(38.7115164,38.6017319) == oppT=0.988266467 fPerpPt=(38.7115169,38.6017324)
-id=1 13=(0.625,0.75) [36,18,24] 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 24=(0.988281,0.990234) [13] 36=(0.990234,0.992188) [13] 18=(0.992188,0.994141) [13,7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.990234375 cPt=(38.7023939,38.6109956) == oppT=0.635851272 fPerpPt=(38.7023935,38.6109953)
-setPerp t=0.9921875 cPt=(38.6933214,38.6201819) == oppT=0.708661403 fPerpPt=(38.6933211,38.6201816)
-setPerp t=0.6875 cPt=(38.6959596,38.6175126) == oppT=0.991619875 fPerpPt=(38.6959599,38.6175129)
-setPerp t=0.98828125 cPt=(38.7114485,38.601802) == oppT=0.563050975 fPerpPt=(38.711448,38.6018015)
-setPerp t=0.990234375 cPt=(38.7023939,38.6109956) == oppT=0.635851272 fPerpPt=(38.7023935,38.6109953)
-setPerp t=0.625 cPt=(38.7037442,38.6096255) == oppT=0.989943268 fPerpPt=(38.7037445,38.6096258)
-setPerp t=0.9921875 cPt=(38.6933214,38.6201819) == oppT=0.708661403 fPerpPt=(38.6933211,38.6201816)
-setPerp t=0.994140625 cPt=(38.684231,38.6293607) == oppT=0.781481354 fPerpPt=(38.6842309,38.6293605)
-setPerp t=0.75 cPt=(38.6881628,38.6253934) == oppT=0.993296287 fPerpPt=(38.688163,38.6253936)
-setPerp t=0.990234375 cPt=(38.7023939,38.6109956) == oppT=0.635851272 fPerpPt=(38.7023935,38.6109953)
-setPerp t=0.9921875 cPt=(38.6933214,38.6201819) == oppT=0.708661403 fPerpPt=(38.6933211,38.6201816)
-setPerp t=0.6875 cPt=(38.6959596,38.6175126) == oppT=0.991619875 fPerpPt=(38.6959599,38.6175129)
-id=1 7=(0.75,0.875) [38,26,18] 15=(0.875,1) [40,26] id=2 18=(0.992188,0.994141) [7] 38=(0.994141,0.996094) [7] 26=(0.996094,0.998047) [15,7] 40=(0.998047,1) [15]
-setPerp t=0.994140625 cPt=(38.684231,38.6293607) == oppT=0.781481354 fPerpPt=(38.6842309,38.6293605)
-setPerp t=0.99609375 cPt=(38.6751228,38.6385321) == oppT=0.854311113 fPerpPt=(38.6751227,38.638532)
-setPerp t=0.8125 cPt=(38.6803537,38.6332678) == oppT=0.994972505 fPerpPt=(38.6803538,38.6332679)
-setPerp t=0.9921875 cPt=(38.6933214,38.6201819) == oppT=0.708661403 fPerpPt=(38.6933211,38.6201816)
-setPerp t=0.994140625 cPt=(38.684231,38.6293607) == oppT=0.781481354 fPerpPt=(38.6842309,38.6293605)
-setPerp t=0.75 cPt=(38.6881628,38.6253934) == oppT=0.993296287 fPerpPt=(38.688163,38.6253936)
-setPerp t=0.99609375 cPt=(38.6751228,38.6385321) == oppT=0.854311113 fPerpPt=(38.6751227,38.638532)
-setPerp t=0.998046875 cPt=(38.6659967,38.6476961) == oppT=0.927150666 fPerpPt=(38.6659967,38.6476961)
-setPerp t=0.875 cPt=(38.6725323,38.6411358) == oppT=0.99664853 fPerpPt=(38.6725324,38.6411359)
-setPerp t=0.994140625 cPt=(38.684231,38.6293607) == oppT=0.781481354 fPerpPt=(38.6842309,38.6293605)
-setPerp t=0.99609375 cPt=(38.6751228,38.6385321) == oppT=0.854311113 fPerpPt=(38.6751227,38.638532)
-setPerp t=0.8125 cPt=(38.6803537,38.6332678) == oppT=0.994972505 fPerpPt=(38.6803538,38.6332679)
-id=1 15=(0.875,1) [40,26] id=2 26=(0.996094,0.998047) [15] 40=(0.998047,1) [15]
-setPerp t=0.998046875 cPt=(38.6659967,38.6476961) == oppT=0.927150666 fPerpPt=(38.6659967,38.6476961)
-setPerp t=1 cPt=(38.6568527,38.6568527) == oppT=1 fPerpPt=(38.6568527,38.6568527)
-setPerp t=0.9375 cPt=(38.6646987,38.6489975) == oppT=0.998324361 fPerpPt=(38.6646987,38.6489975)
-setPerp t=0.99609375 cPt=(38.6751228,38.6385321) == oppT=0.854311113 fPerpPt=(38.6751227,38.638532)
-setPerp t=0.998046875 cPt=(38.6659967,38.6476961) == oppT=0.927150666 fPerpPt=(38.6659967,38.6476961)
-setPerp t=0.875 cPt=(38.6725323,38.6411358) == oppT=0.99664853 fPerpPt=(38.6725324,38.6411359)
-id=1 31=(0.9375,1) [40] id=2 40=(0.998047,1) [31]
-setPerp t=0.9375 cPt=(38.6646987,38.6489975) == oppT=0.998324361 fPerpPt=(38.6646987,38.6489975)
-setPerp t=1 cPt=(38.6568527,38.6568527) == oppT=1 fPerpPt=(38.6568527,38.6568527)
-setPerp t=0.999023438 cPt=(38.6614269,38.6522753) == oppT=0.963574111 fPerpPt=(38.6614269,38.6522753)
-id=1 31=(1,1) [42] id=2 42=(1,1) [31]
-debugShowQuadIntersection wtTs[0]=1 {{{38.7809143,38.5304031}, {38.7196693,38.5940361}, {38.6568527,38.6568527}}} {{38.6568527,38.6568527}} wnTs[0]=1 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.7809143,38.5304031}, {38.7196693,38.5940361}, {38.6568527,38.6568527}}} {{38.6568527,38.6568527}} wnTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}}
-debugShowQuadIntersection wtTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{38.6568527,38.6568527}} wnTs[0]=1 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,1) [3,1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [6,2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,0.75) [3,1] 6=(0.75,1) [3]
-id=1 1=(0,0.5) [4,2] 3=(0.5,0.75) [6,2,4] 5=(0.75,1) [4,6] id=2 2=(0,0.5) [3,1] 4=(0.5,0.75) [5,3,1] 6=(0.75,1) [5,3]
-id=1 1=(0,0.5) [8,4,2] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [4,6] id=2 2=(0,0.25) [1] 8=(0.25,0.5) [1,3] 4=(0.5,0.75) [5,3,1] 6=(0.75,1) [5,3]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [4,6] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,1) [5,3]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [10,4,6] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,0.875) [5,3] 10=(0.875,1) [5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,0.875) [10,4,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,0.875) [9,5,3] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [12,8,6,4] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.625) [7,3] 12=(0.625,0.75) [3,5] 6=(0.75,0.875) [9,5,3] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.625) [12,8,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.625) [11,7,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [14,2,4,8] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.375) [7,1] 14=(0.375,0.5) [3,7] 4=(0.5,0.625) [11,7,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.375) [14,2,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.375) [13,7,1] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [16,8,2] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.125) [1] 16=(0.125,0.25) [1,7] 8=(0.25,0.375) [13,7,1] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.125) [16,2] 15=(0.125,0.25) [2,8,16] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.125) [15,1] 16=(0.125,0.25) [15,1,7] 8=(0.25,0.375) [15,13,7] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.125) [16,2] 15=(0.125,0.25) [2,8,16] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [18,6,10] id=2 2=(0,0.125) [15,1] 16=(0.125,0.25) [15,1,7] 8=(0.25,0.375) [15,13,7] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,0.9375) [9,5] 18=(0.9375,1) [9]
-setPerp t=0 cPt=(38.6568527,38.6568527) == oppT=0 fPerpPt=(38.6568527,38.6568527)
-setPerp t=0.125 cPt=(38.0559018,39.2060279) == oppT=0.125 fPerpPt=(38.0559018,39.2060279)
-setPerp t=0.25 cPt=(37.4246206,39.6819797) == oppT=0.25 fPerpPt=(37.4246206,39.6819797)
-setPerp t=0.375 cPt=(36.7630093,40.0847081) == oppT=0.375 fPerpPt=(36.7630093,40.0847081)
-setPerp t=0.5 cPt=(36.0710678,40.4142132) == oppT=0.5 fPerpPt=(36.0710678,40.4142132)
-setPerp t=0.625 cPt=(35.3487961,40.6704949) == oppT=0.625 fPerpPt=(35.3487961,40.6704949)
-setPerp t=0.75 cPt=(34.5961943,40.8535533) == oppT=0.75 fPerpPt=(34.5961943,40.8535533)
-setPerp t=0.875 cPt=(33.8132622,40.9633883) == oppT=0.875 fPerpPt=(33.8132622,40.9633883)
-setPerp t=0.9375 cPt=(33.4104224,40.9908471) == oppT=0.9375 fPerpPt=(33.4104224,40.9908471)
-setPerp t=1 cPt=(33,41) == oppT=1 fPerpPt=(33,41)
-setPerp t=0 cPt=(38.6568527,38.6568527) == oppT=0 fPerpPt=(38.6568527,38.6568527)
-setPerp t=1 cPt=(33,41) == oppT=1 fPerpPt=(33,41)
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection wtTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{38.6568527,38.6568527}} wtTs[1]=1 {{33,41}} wnTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{33,41}} wnTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{33,41}} wnTs[0]=1 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,1) [3,1]
-id=1 1=(0,0.5) [6,4,2] 3=(0.5,1) [6,4] id=2 2=(0,0.25) [1] 6=(0.25,0.5) [1,3] 4=(0.5,1) [3,1]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,1) [6,4] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,1) [5,3]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,1) [8,6,4] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [5,3] 8=(0.75,1) [3]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.25) [10,6,2] 5=(0.25,0.5) [10,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [1] 10=(0.125,0.25) [1,5] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.5) [10,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.5) [9,5,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.5) [12,10,4,6] 3=(0.5,0.75) [12,8,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [9,5] 12=(0.375,0.5) [3,5] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.75) [12,8,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.75) [11,7,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.75) [14,12,8,4] 7=(0.75,1) [14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [11,3] 14=(0.625,0.75) [3,7] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,1) [14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,1) [13,7]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,1) [16,14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [13,7] 16=(0.875,1) [7]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,0.875) [16,14,8] 15=(0.875,1) [8,16] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [15,13,7] 16=(0.875,1) [15,7]
-id=1 1=(0,0.125) [18,10,2] 9=(0.125,0.25) [18,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,0.875) [16,14,8] 15=(0.875,1) [8,16] id=2 2=(0,0.0625) [1] 18=(0.0625,0.125) [1,9] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [15,13,7] 16=(0.875,1) [15,7]
-setPerp t=0 cPt=(33,41) == oppT=0 fPerpPt=(33,41)
-setPerp t=0.0625 cPt=(32.5895776,40.9908471) == oppT=0.0625 fPerpPt=(32.5895776,40.9908471)
-setPerp t=0.125 cPt=(32.1867377,40.9633883) == oppT=0.125 fPerpPt=(32.1867377,40.9633883)
-setPerp t=0.25 cPt=(31.4038056,40.8535533) == oppT=0.25 fPerpPt=(31.4038056,40.8535533)
-setPerp t=0.375 cPt=(30.6512036,40.6704949) == oppT=0.375 fPerpPt=(30.6512036,40.6704949)
-setPerp t=0.5 cPt=(29.9289317,40.4142132) == oppT=0.5 fPerpPt=(29.9289317,40.4142132)
-setPerp t=0.625 cPt=(29.2369899,40.0847081) == oppT=0.625 fPerpPt=(29.2369899,40.0847081)
-setPerp t=0.75 cPt=(28.5753783,39.6819797) == oppT=0.75 fPerpPt=(28.5753783,39.6819797)
-setPerp t=0.875 cPt=(27.9440968,39.2060279) == oppT=0.875 fPerpPt=(27.9440968,39.2060279)
-setPerp t=1 cPt=(27.3431454,38.6568527) == oppT=1 fPerpPt=(27.3431454,38.6568527)
-setPerp t=0 cPt=(33,41) == oppT=0 fPerpPt=(33,41)
-setPerp t=1 cPt=(27.3431454,38.6568527) == oppT=1 fPerpPt=(27.3431454,38.6568527)
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection wtTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{33,41}} wtTs[1]=1 {{27.3431454,38.6568527}} wnTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{27.3431454,38.6568527}} wnTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}}
-debugShowQuadIntersection wtTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{27.3431454,38.6568527}} wnTs[0]=1 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,1) [3,1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [6,2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,0.75) [3,1] 6=(0.75,1) [3]
-id=1 1=(0,0.5) [4,2] 3=(0.5,0.75) [6,2,4] 5=(0.75,1) [4,6] id=2 2=(0,0.5) [3,1] 4=(0.5,0.75) [5,3,1] 6=(0.75,1) [5,3]
-id=1 1=(0,0.5) [8,4,2] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [4,6] id=2 2=(0,0.25) [1] 8=(0.25,0.5) [1,3] 4=(0.5,0.75) [5,3,1] 6=(0.75,1) [5,3]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [4,6] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,1) [5,3]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,1) [10,4,6] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,0.875) [5,3] 10=(0.875,1) [5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [8,6,4] 5=(0.75,0.875) [10,4,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.75) [7,5,3] 6=(0.75,0.875) [9,5,3] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.75) [12,8,6,4] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.625) [7,3] 12=(0.625,0.75) [3,5] 6=(0.75,0.875) [9,5,3] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [2,4,8] 3=(0.5,0.625) [12,8,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.5) [7,1,3] 4=(0.5,0.625) [11,7,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.5) [14,2,4,8] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.375) [7,1] 14=(0.375,0.5) [3,7] 4=(0.5,0.625) [11,7,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [8,2] 7=(0.25,0.375) [14,2,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.25) [7,1] 8=(0.25,0.375) [13,7,1] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.25) [16,8,2] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.125) [1] 16=(0.125,0.25) [1,7] 8=(0.25,0.375) [13,7,1] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.125) [16,2] 15=(0.125,0.25) [2,8,16] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [6,10] id=2 2=(0,0.125) [15,1] 16=(0.125,0.25) [15,1,7] 8=(0.25,0.375) [15,13,7] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,1) [9,5]
-id=1 1=(0,0.125) [16,2] 15=(0.125,0.25) [2,8,16] 7=(0.25,0.375) [16,14,8] 13=(0.375,0.5) [8,4,14] 3=(0.5,0.625) [14,12,4] 11=(0.625,0.75) [4,6,12] 5=(0.75,0.875) [12,10,6] 9=(0.875,1) [18,6,10] id=2 2=(0,0.125) [15,1] 16=(0.125,0.25) [15,1,7] 8=(0.25,0.375) [15,13,7] 14=(0.375,0.5) [13,3,7] 4=(0.5,0.625) [13,11,3] 12=(0.625,0.75) [11,3,5] 6=(0.75,0.875) [11,9,5] 10=(0.875,0.9375) [9,5] 18=(0.9375,1) [9]
-setPerp t=0 cPt=(27.3431454,38.6568527) == oppT=0 fPerpPt=(27.3431454,38.6568527)
-setPerp t=0.125 cPt=(26.7939707,38.0559018) == oppT=0.125 fPerpPt=(26.7939707,38.0559018)
-setPerp t=0.25 cPt=(26.3180193,37.4246206) == oppT=0.25 fPerpPt=(26.3180193,37.4246206)
-setPerp t=0.375 cPt=(25.9152912,36.7630093) == oppT=0.375 fPerpPt=(25.9152912,36.7630093)
-setPerp t=0.5 cPt=(25.5857863,36.0710678) == oppT=0.5 fPerpPt=(25.5857863,36.0710678)
-setPerp t=0.625 cPt=(25.3295048,35.3487961) == oppT=0.625 fPerpPt=(25.3295048,35.3487961)
-setPerp t=0.75 cPt=(25.1464466,34.5961943) == oppT=0.75 fPerpPt=(25.1464466,34.5961943)
-setPerp t=0.875 cPt=(25.0366116,33.8132622) == oppT=0.875 fPerpPt=(25.0366116,33.8132622)
-setPerp t=0.9375 cPt=(25.0091529,33.4104224) == oppT=0.9375 fPerpPt=(25.0091529,33.4104224)
-setPerp t=1 cPt=(25,33) == oppT=1 fPerpPt=(25,33)
-setPerp t=0 cPt=(27.3431454,38.6568527) == oppT=0 fPerpPt=(27.3431454,38.6568527)
-setPerp t=1 cPt=(25,33) == oppT=1 fPerpPt=(25,33)
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection wtTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{27.3431454,38.6568527}} wtTs[1]=1 {{25,33}} wnTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{25,33}} wnTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{25,33}} wnTs[0]=1 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 1=(0,0.5) [4,2] 3=(0.5,1) [2,4] id=2 2=(0,0.5) [3,1] 4=(0.5,1) [3,1]
-id=1 1=(0,0.5) [6,4,2] 3=(0.5,1) [6,4] id=2 2=(0,0.25) [1] 6=(0.25,0.5) [1,3] 4=(0.5,1) [3,1]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,1) [6,4] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,1) [5,3]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,1) [8,6,4] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [5,3] 8=(0.75,1) [3]
-id=1 1=(0,0.25) [6,2] 5=(0.25,0.5) [2,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.25) [5,1] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.25) [10,6,2] 5=(0.25,0.5) [10,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [1] 10=(0.125,0.25) [1,5] 6=(0.25,0.5) [5,1,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.5) [10,4,6] 3=(0.5,0.75) [8,6,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.5) [9,5,3] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.5) [12,10,4,6] 3=(0.5,0.75) [12,8,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [9,5] 12=(0.375,0.5) [3,5] 4=(0.5,0.75) [7,5,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.75) [12,8,4] 7=(0.75,1) [4,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.75) [11,7,3] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.75) [14,12,8,4] 7=(0.75,1) [14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [11,3] 14=(0.625,0.75) [3,7] 8=(0.75,1) [7,3]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,1) [14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,1) [13,7]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,1) [16,14,8] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [13,7] 16=(0.875,1) [7]
-id=1 1=(0,0.125) [10,2] 9=(0.125,0.25) [2,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,0.875) [16,14,8] 15=(0.875,1) [8,16] id=2 2=(0,0.125) [9,1] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [15,13,7] 16=(0.875,1) [15,7]
-id=1 1=(0,0.125) [18,10,2] 9=(0.125,0.25) [18,6,10] 5=(0.25,0.375) [12,10,6] 11=(0.375,0.5) [6,4,12] 3=(0.5,0.625) [14,12,4] 13=(0.625,0.75) [4,8,14] 7=(0.75,0.875) [16,14,8] 15=(0.875,1) [8,16] id=2 2=(0,0.0625) [1] 18=(0.0625,0.125) [1,9] 10=(0.125,0.25) [9,1,5] 6=(0.25,0.375) [11,9,5] 12=(0.375,0.5) [11,3,5] 4=(0.5,0.625) [13,11,3] 14=(0.625,0.75) [13,3,7] 8=(0.75,0.875) [15,13,7] 16=(0.875,1) [15,7]
-setPerp t=0 cPt=(25,33) == oppT=0 fPerpPt=(25,33)
-setPerp t=0.0625 cPt=(25.0091529,32.5895776) == oppT=0.0625 fPerpPt=(25.0091529,32.5895776)
-setPerp t=0.125 cPt=(25.0366116,32.1867377) == oppT=0.125 fPerpPt=(25.0366116,32.1867377)
-setPerp t=0.25 cPt=(25.1464466,31.4038056) == oppT=0.25 fPerpPt=(25.1464466,31.4038056)
-setPerp t=0.375 cPt=(25.3295048,30.6512036) == oppT=0.375 fPerpPt=(25.3295048,30.6512036)
-setPerp t=0.5 cPt=(25.5857863,29.9289317) == oppT=0.5 fPerpPt=(25.5857863,29.9289317)
-setPerp t=0.625 cPt=(25.9152912,29.2369899) == oppT=0.625 fPerpPt=(25.9152912,29.2369899)
-setPerp t=0.75 cPt=(26.3180193,28.5753783) == oppT=0.75 fPerpPt=(26.3180193,28.5753783)
-setPerp t=0.875 cPt=(26.7939707,27.9440968) == oppT=0.875 fPerpPt=(26.7939707,27.9440968)
-setPerp t=1 cPt=(27.3431454,27.3431454) == oppT=1 fPerpPt=(27.3431454,27.3431454)
-setPerp t=0 cPt=(25,33) == oppT=0 fPerpPt=(25,33)
-setPerp t=1 cPt=(27.3431454,27.3431454) == oppT=1 fPerpPt=(27.3431454,27.3431454)
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection wtTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{25,33}} wtTs[1]=1 {{27.3431454,27.3431454}} wnTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{27.3431454,27.3431454}} wnTs[0]=0 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-debugShowQuadIntersection wtTs[0]=0 {{{27.3431454,27.3431454}, {27.3875446,27.2987461}, {27.4323025,27.2551785}}} {{27.3431454,27.3431454}} wnTs[0]=1 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}}
-id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.25) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.125) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.0625) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.03125) [1]
-id=1 1=(0,1) [14,2] id=2 2=(0,0.015625) [1] 14=(0.015625,0.03125) [1]
-id=1 1=(0,0.5) [2] 3=(0.5,1) [2,14] id=2 2=(0,0.015625) [3,1] 14=(0.015625,0.03125) [3]
-id=1 1=(0,0.5) [2] 3=(0.5,1) [2,14] id=2 2=(0,0.015625) [3,1] 14=(0.015625,0.0234375) [3]
-id=1 1=(0,0.5) [18,2] 3=(0.5,1) [18,14] id=2 2=(0,0.0078125) [1] 18=(0.0078125,0.015625) [1,3] 14=(0.015625,0.0234375) [3]
-id=1 1=(0,0.5) [18,2] 3=(0.5,0.75) [18] 5=(0.75,1) [14,18] id=2 2=(0,0.0078125) [1] 18=(0.0078125,0.015625) [5,1,3] 14=(0.015625,0.0234375) [5]
-id=1 1=(0,0.25) [2] 7=(0.25,0.5) [2,18] 3=(0.5,0.75) [18] 5=(0.75,1) [14,18] id=2 2=(0,0.0078125) [7,1] 18=(0.0078125,0.015625) [7,5,3] 14=(0.015625,0.0234375) [5]
-id=1 1=(0,0.25) [2] 7=(0.25,0.5) [2,18] 3=(0.5,0.75) [18] 5=(0.75,1) [14,18] id=2 2=(0,0.0078125) [7,1] 18=(0.0078125,0.015625) [7,5,3] 14=(0.015625,0.0195313) [5]
-id=1 1=(0,0.25) [2] 7=(0.25,0.5) [2,18] 3=(0.5,0.75) [22,18] 5=(0.75,1) [22,14] id=2 2=(0,0.0078125) [7,1] 18=(0.0078125,0.0117188) [7,3] 22=(0.0117188,0.015625) [3,5] 14=(0.015625,0.0195313) [5]
-id=1 1=(0,0.25) [24,2] 7=(0.25,0.5) [24,18] 3=(0.5,0.75) [22,18] 5=(0.75,1) [22,14] id=2 2=(0,0.00390625) [1] 24=(0.00390625,0.0078125) [1,7] 18=(0.0078125,0.0117188) [7,3] 22=(0.0117188,0.015625) [3,5] 14=(0.015625,0.0195313) [5]
-id=1 1=(0,0.25) [24,2] 7=(0.25,0.5) [24,18] 3=(0.5,0.75) [22,18] 5=(0.75,0.875) [22,14] 9=(0.875,1) [14] id=2 2=(0,0.00390625) [1] 24=(0.00390625,0.0078125) [1,7] 18=(0.0078125,0.0117188) [7,3] 22=(0.0117188,0.015625) [3,5] 14=(0.015625,0.0195313) [9,5]
-id=1 1=(0,0.25) [24,2] 7=(0.25,0.5) [24,18] 3=(0.5,0.625) [22,18] 11=(0.625,0.75) [22] 5=(0.75,0.875) [22,14] 9=(0.875,1) [14] id=2 2=(0,0.00390625) [1] 24=(0.00390625,0.0078125) [1,7] 18=(0.0078125,0.0117188) [7,3] 22=(0.0117188,0.015625) [11,3,5] 14=(0.015625,0.0195313) [9,5]
-id=1 1=(0,0.25) [24,2] 7=(0.25,0.375) [24] 13=(0.375,0.5) [18,24] 3=(0.5,0.625) [22,18] 11=(0.625,0.75) [22] 5=(0.75,0.875) [22,14] 9=(0.875,1) [14] id=2 2=(0,0.00390625) [1] 24=(0.00390625,0.0078125) [13,1,7] 18=(0.0078125,0.0117188) [13,3] 22=(0.0117188,0.015625) [11,3,5] 14=(0.015625,0.0195313) [9,5]
-id=1 1=(0,0.125) [2] 15=(0.125,0.25) [2,24] 7=(0.25,0.375) [24] 13=(0.375,0.5) [18,24] 3=(0.5,0.625) [22,18] 11=(0.625,0.75) [22] 5=(0.75,0.875) [22,14] 9=(0.875,1) [14] id=2 2=(0,0.00390625) [15,1] 24=(0.00390625,0.0078125) [15,13,7] 18=(0.0078125,0.0117188) [13,3] 22=(0.0117188,0.015625) [11,3,5] 14=(0.015625,0.0195313) [9,5]
-setPerp t=0.875 cPt=(27.4211186,27.2660834) == oppT=0.0165816271 fPerpPt=(27.4211186,27.2660833)
-setPerp t=1 cPt=(27.4323025,27.2551785) == oppT=0.0189506978 fPerpPt=(27.4323024,27.2551784)
-setPerp t=0.017578125 cPt=(27.4258215,27.2614932) == oppT=0.927578956 fPerpPt=(27.4258215,27.2614932)
-setPerp t=0.75 cPt=(27.409946,27.2770143) == oppT=0.0142126233 fPerpPt=(27.4099459,27.2770142)
-setPerp t=0.875 cPt=(27.4211186,27.2660834) == oppT=0.0165816271 fPerpPt=(27.4211186,27.2660833)
-setPerp t=0.015625 cPt=(27.4166056,27.2704941) == oppT=0.824524193 fPerpPt=(27.4166057,27.2704942)
-setPerp t=0.875 cPt=(27.4211186,27.2660834) == oppT=0.0165816271 fPerpPt=(27.4211186,27.2660833)
-setPerp t=1 cPt=(27.4323025,27.2551785) == oppT=0.0189506978 fPerpPt=(27.4323024,27.2551784)
-setPerp t=0.017578125 cPt=(27.4258215,27.2614932) == oppT=0.927578956 fPerpPt=(27.4258215,27.2614932)
-id=1 1=(0,0.125) [2] 15=(0.125,0.25) [2,24] 7=(0.25,0.375) [24] 13=(0.375,0.5) [18,24] 3=(0.5,0.625) [22,18] 11=(0.625,0.75) [22] 5=(0.75,0.875) [22] id=2 2=(0,0.00390625) [15,1] 24=(0.00390625,0.0078125) [15,13,7] 18=(0.0078125,0.0117188) [13,3] 22=(0.0117188,0.015625) [11,3,5]
-setPerp t=0.625 cPt=(27.3987845,27.2879711) == oppT=0.0118436864 fPerpPt=(27.3987845,27.2879711)
-setPerp t=0.75 cPt=(27.409946,27.2770143) == oppT=0.0142126233 fPerpPt=(27.4099459,27.2770142)
-setPerp t=0.013671875 cPt=(27.4073972,27.279513) == oppT=0.721467031 fPerpPt=(27.4073972,27.279513)
-setPerp t=0.5 cPt=(27.3876343,27.298954) == oppT=0.00947481625 fPerpPt=(27.3876342,27.298954)
-setPerp t=0.625 cPt=(27.3987845,27.2879711) == oppT=0.0118436864 fPerpPt=(27.3987845,27.2879711)
-setPerp t=0.01171875 cPt=(27.3981961,27.2885497) == oppT=0.618407471 fPerpPt=(27.3981962,27.2885497)
-setPerp t=0.75 cPt=(27.409946,27.2770143) == oppT=0.0142126233 fPerpPt=(27.4099459,27.2770142)
-setPerp t=0.875 cPt=(27.4211186,27.2660834) == oppT=0.0165816271 fPerpPt=(27.4211186,27.2660833)
-setPerp t=0.015625 cPt=(27.4166056,27.2704941) == oppT=0.824524193 fPerpPt=(27.4166057,27.2704942)
-setPerp t=0.625 cPt=(27.3987845,27.2879711) == oppT=0.0118436864 fPerpPt=(27.3987845,27.2879711)
-setPerp t=0.75 cPt=(27.409946,27.2770143) == oppT=0.0142126233 fPerpPt=(27.4099459,27.2770142)
-setPerp t=0.013671875 cPt=(27.4073972,27.279513) == oppT=0.721467031 fPerpPt=(27.4073972,27.279513)
-id=1 1=(0,0.125) [2] 15=(0.125,0.25) [2,24] 7=(0.25,0.375) [24] 13=(0.375,0.5) [18,24] 3=(0.5,0.625) [18] id=2 2=(0,0.00390625) [15,1] 24=(0.00390625,0.0078125) [15,13,7] 18=(0.0078125,0.0117188) [13,3]
-setPerp t=0.375 cPt=(27.3764952,27.3099629) == oppT=0.00710601267 fPerpPt=(27.3764952,27.3099628)
-setPerp t=0.5 cPt=(27.3876343,27.298954) == oppT=0.00947481625 fPerpPt=(27.3876342,27.298954)
-setPerp t=0.0078125 cPt=(27.3798163,27.3066767) == oppT=0.412281177 fPerpPt=(27.3798163,27.3066768)
-setPerp t=0.5 cPt=(27.3876343,27.298954) == oppT=0.00947481625 fPerpPt=(27.3876342,27.298954)
-setPerp t=0.625 cPt=(27.3987845,27.2879711) == oppT=0.0118436864 fPerpPt=(27.3987845,27.2879711)
-setPerp t=0.009765625 cPt=(27.3890025,27.2976043) == oppT=0.515345519 fPerpPt=(27.3890025,27.2976043)
-setPerp t=0.5 cPt=(27.3876343,27.298954) == oppT=0.00947481625 fPerpPt=(27.3876342,27.298954)
-setPerp t=0.625 cPt=(27.3987845,27.2879711) == oppT=0.0118436864 fPerpPt=(27.3987845,27.2879711)
-setPerp t=0.009765625 cPt=(27.3890025,27.2976043) == oppT=0.515345519 fPerpPt=(27.3890025,27.2976043)
-setPerp t=0.01171875 cPt=(27.3981961,27.2885497) == oppT=0.618407471 fPerpPt=(27.3981962,27.2885497)
-id=1 1=(0,0.125) [2] 15=(0.125,0.25) [2,24] 7=(0.25,0.375) [24] 13=(0.375,0.5) [24] id=2 2=(0,0.00390625) [15,1] 24=(0.00390625,0.0078125) [15,13,7]
-setPerp t=0.125 cPt=(27.3542508,27.3320585) == oppT=0.00236860468 fPerpPt=(27.3542508,27.3320585)
-setPerp t=0.25 cPt=(27.3653674,27.3209977) == oppT=0.00473727552 fPerpPt=(27.3653674,27.3209977)
-setPerp t=0.00390625 cPt=(27.361466,27.3248753) == oppT=0.206145343 fPerpPt=(27.361466,27.3248753)
-setPerp t=0.25 cPt=(27.3653674,27.3209977) == oppT=0.00473727552 fPerpPt=(27.3653674,27.3209977)
-setPerp t=0.375 cPt=(27.3764952,27.3099629) == oppT=0.00710601267 fPerpPt=(27.3764952,27.3099628)
-setPerp t=0.005859375 cPt=(27.3706374,27.3157671) == oppT=0.309214451 fPerpPt=(27.3706374,27.3157671)
-setPerp t=0.25 cPt=(27.3653674,27.3209977) == oppT=0.00473727552 fPerpPt=(27.3653674,27.3209977)
-setPerp t=0.375 cPt=(27.3764952,27.3099629) == oppT=0.00710601267 fPerpPt=(27.3764952,27.3099628)
-setPerp t=0.005859375 cPt=(27.3706374,27.3157671) == oppT=0.309214451 fPerpPt=(27.3706374,27.3157671)
-setPerp t=0.375 cPt=(27.3764952,27.3099629) == oppT=0.00710601267 fPerpPt=(27.3764952,27.3099628)
-setPerp t=0.5 cPt=(27.3876343,27.298954) == oppT=0.00947481625 fPerpPt=(27.3876342,27.298954)
-setPerp t=0.0078125 cPt=(27.3798163,27.3066767) == oppT=0.412281177 fPerpPt=(27.3798163,27.3066768)
-id=1 1=(0,0.125) [2] 15=(0.125,0.25) [2] id=2 2=(0,0.00390625) [15,1]
-setPerp t=0.125 cPt=(27.3542508,27.3320585) == oppT=0.00236860468 fPerpPt=(27.3542508,27.3320585)
-setPerp t=0.25 cPt=(27.3653674,27.3209977) == oppT=0.00473727552 fPerpPt=(27.3653674,27.3209977)
-setPerp t=0.00390625 cPt=(27.361466,27.3248753) == oppT=0.206145343 fPerpPt=(27.361466,27.3248753)
-id=1 1=(0,0.125) [34,2] id=2 2=(0,0.00195313) [1] 34=(0.00195313,0.00390625) [1]
-id=1 1=(0,0.0625) [2] 17=(0.0625,0.125) [2,34] id=2 2=(0,0.00195313) [17,1] 34=(0.00195313,0.00390625) [17]
-id=1 1=(0,0.0625) [2] 17=(0.0625,0.125) [2,34] id=2 2=(0,0.00195313) [17,1] 34=(0.00195313,0.00292969) [17]
-id=1 1=(0,0.0625) [38,2] 17=(0.0625,0.125) [38,34] id=2 2=(0,0.000976563) [1] 38=(0.000976563,0.00195313) [1,17] 34=(0.00195313,0.00292969) [17]
-setPerp t=0.001953125 cPt=(27.352302,27.3340014) == oppT=0.103073858 fPerpPt=(27.352302,27.3340014)
-setPerp t=0.0029296875 cPt=(27.3568831,27.3294361) == oppT=0.154609898 fPerpPt=(27.3568831,27.3294361)
-setPerp t=0.125 cPt=(27.3542508,27.3320585) == oppT=0.00236860468 fPerpPt=(27.3542508,27.3320585)
-id=1 1=(0,0.0625) [38,2] 17=(0.0625,0.09375) [38] 19=(0.09375,0.125) [38] id=2 2=(0,0.000976563) [1] 38=(0.000976563,0.00195313) [19,1,17]
-id=1 1=(0,0.03125) [2] 21=(0.03125,0.0625) [2,38] 17=(0.0625,0.09375) [38] 19=(0.09375,0.125) [38] id=2 2=(0,0.000976563) [21,1] 38=(0.000976563,0.00195313) [21,19,17]
-setPerp t=0.09375 cPt=(27.3514734,27.3348278) == oppT=0.00177644731 fPerpPt=(27.3514734,27.3348278)
-setPerp t=0.125 cPt=(27.3542508,27.3320585) == oppT=0.00236860468 fPerpPt=(27.3542508,27.3320585)
-setPerp t=0.001953125 cPt=(27.352302,27.3340014) == oppT=0.103073858 fPerpPt=(27.352302,27.3340014)
-id=1 1=(0,0.03125) [2] 21=(0.03125,0.0625) [2,38] 17=(0.0625,0.09375) [40,38] id=2 2=(0,0.000976563) [21,1] 38=(0.000976563,0.00146484) [21,17] 40=(0.00146484,0.00195313) [17]
-id=1 1=(0,0.03125) [42,2] 21=(0.03125,0.0625) [42,38] 17=(0.0625,0.09375) [40,38] id=2 2=(0,0.000488281) [1] 42=(0.000488281,0.000976563) [1,21] 38=(0.000976563,0.00146484) [21,17] 40=(0.00146484,0.00195313) [17]
-setPerp t=0.00146484375 cPt=(27.3500121,27.3362857) == oppT=0.0773056159 fPerpPt=(27.3500121,27.3362857)
-setPerp t=0.001953125 cPt=(27.352302,27.3340014) == oppT=0.103073858 fPerpPt=(27.352302,27.3340014)
-setPerp t=0.078125 cPt=(27.3500849,27.3362131) == oppT=0.00148037018 fPerpPt=(27.3500849,27.3362131)
-setPerp t=0.00146484375 cPt=(27.3500121,27.3362857) == oppT=0.0773056159 fPerpPt=(27.3500121,27.3362857)
-setPerp t=0.001953125 cPt=(27.352302,27.3340014) == oppT=0.103073858 fPerpPt=(27.352302,27.3340014)
-setPerp t=0.078125 cPt=(27.3500849,27.3362131) == oppT=0.00148037018 fPerpPt=(27.3500849,27.3362131)
-setPerp t=0.09375 cPt=(27.3514734,27.3348278) == oppT=0.00177644731 fPerpPt=(27.3514734,27.3348278)
-id=1 1=(0,0.03125) [42,2] 21=(0.03125,0.0625) [42,38] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000488281) [1] 42=(0.000488281,0.000976563) [1,21] 38=(0.000976563,0.00146484) [21,17]
-id=1 1=(0,0.03125) [42,2] 21=(0.03125,0.046875) [42] 25=(0.046875,0.0625) [38,42] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000488281) [1] 42=(0.000488281,0.000976563) [25,1,21] 38=(0.000976563,0.00146484) [25,17]
-id=1 1=(0,0.015625) [2] 27=(0.015625,0.03125) [2,42] 21=(0.03125,0.046875) [42] 25=(0.046875,0.0625) [38,42] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000488281) [27,1] 42=(0.000488281,0.000976563) [27,25,21] 38=(0.000976563,0.00146484) [25,17]
-setPerp t=0.0625 cPt=(27.3486967,27.3375987) == oppT=0.00118429408 fPerpPt=(27.3486967,27.3375987)
-setPerp t=0.078125 cPt=(27.3500849,27.3362131) == oppT=0.00148037018 fPerpPt=(27.3500849,27.3362131)
-setPerp t=0.00122070313 cPt=(27.3488674,27.3374283) == oppT=0.0644214392 fPerpPt=(27.3488674,27.3374283)
-setPerp t=0.00146484375 cPt=(27.3500121,27.3362857) == oppT=0.0773056159 fPerpPt=(27.3500121,27.3362857)
-id=1 1=(0,0.015625) [2] 27=(0.015625,0.03125) [2,42] 21=(0.03125,0.046875) [42] 25=(0.046875,0.0625) [38,42] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000488281) [27,1] 42=(0.000488281,0.000976563) [27,25,21] 38=(0.000976563,0.0012207) [25,17]
-id=1 1=(0,0.015625) [2] 27=(0.015625,0.03125) [2,42] 21=(0.03125,0.046875) [46,42] 25=(0.046875,0.0625) [46,38] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000488281) [27,1] 42=(0.000488281,0.000732422) [27,21] 46=(0.000732422,0.000976563) [21,25] 38=(0.000976563,0.0012207) [25,17]
-id=1 1=(0,0.015625) [48,2] 27=(0.015625,0.03125) [48,42] 21=(0.03125,0.046875) [46,42] 25=(0.046875,0.0625) [46,38] 17=(0.0625,0.078125) [38] id=2 2=(0,0.000244141) [1] 48=(0.000244141,0.000488281) [1,27] 42=(0.000488281,0.000732422) [27,21] 46=(0.000732422,0.000976563) [21,25] 38=(0.000976563,0.0012207) [25,17]
-id=1 1=(0,0.015625) [48,2] 27=(0.015625,0.03125) [48,42] 21=(0.03125,0.046875) [46,42] 25=(0.046875,0.0625) [46,38] 17=(0.0625,0.0703125) [38] id=2 2=(0,0.000244141) [1] 48=(0.000244141,0.000488281) [1,27] 42=(0.000488281,0.000732422) [27,21] 46=(0.000732422,0.000976563) [21,25] 38=(0.000976563,0.0012207) [25,17]
-id=1 1=(0,0.015625) [48,2] 27=(0.015625,0.03125) [48,42] 21=(0.03125,0.046875) [46,42] 25=(0.046875,0.0546875) [46,38] 31=(0.0546875,0.0625) [38] 17=(0.0625,0.0703125) [38] id=2 2=(0,0.000244141) [1] 48=(0.000244141,0.000488281) [1,27] 42=(0.000488281,0.000732422) [27,21] 46=(0.000732422,0.000976563) [21,25] 38=(0.000976563,0.0012207) [31,25,17]
-id=1 1=(0,0.015625) [48,2] 27=(0.015625,0.03125) [48,42] 21=(0.03125,0.0390625) [46,42] 33=(0.0390625,0.046875) [46] 25=(0.046875,0.0546875) [46,38] 31=(0.0546875,0.0625) [38] 17=(0.0625,0.0703125) [38] id=2 2=(0,0.000244141) [1] 48=(0.000244141,0.000488281) [1,27] 42=(0.000488281,0.000732422) [27,21] 46=(0.000732422,0.000976563) [33,21,25] 38=(0.000976563,0.0012207) [31,25,17]
-id=1 1=(0,0.015625) [48,2] 27=(0.015625,0.0234375) [48] 35=(0.0234375,0.03125) [42,48] 21=(0.03125,0.0390625) [46,42] 33=(0.0390625,0.046875) [46] 25=(0.046875,0.0546875) [46,38] 31=(0.0546875,0.0625) [38] 17=(0.0625,0.0703125) [38] id=2 2=(0,0.000244141) [1] 48=(0.000244141,0.000488281) [35,1,27] 42=(0.000488281,0.000732422) [35,21] 46=(0.000732422,0.000976563) [33,21,25] 38=(0.000976563,0.0012207) [31,25,17]
-id=1 1=(0,0.0078125) [2] 37=(0.0078125,0.015625) [2,48] 27=(0.015625,0.0234375) [48] 35=(0.0234375,0.03125) [42,48] 21=(0.03125,0.0390625) [46,42] 33=(0.0390625,0.046875) [46] 25=(0.046875,0.0546875) [46,38] 31=(0.0546875,0.0625) [38] 17=(0.0625,0.0703125) [38] id=2 2=(0,0.000244141) [37,1] 48=(0.000244141,0.000488281) [37,35,27] 42=(0.000488281,0.000732422) [35,21] 46=(0.000732422,0.000976563) [33,21,25] 38=(0.000976563,0.0012207) [31,25,17]
-setPerp t=0.0546875 cPt=(27.3480026,27.3382917) == oppT=0.00103625641 fPerpPt=(27.3480026,27.3382917)
-setPerp t=0.0625 cPt=(27.3486967,27.3375987) == oppT=0.00118429408 fPerpPt=(27.3486967,27.3375987)
-setPerp t=0.00109863281 cPt=(27.3482951,27.3379997) == oppT=0.057979337 fPerpPt=(27.3482951,27.3379997)
-setPerp t=0.046875 cPt=(27.3473086,27.3389848) == oppT=0.00088821901 fPerpPt=(27.3473086,27.3389848)
-setPerp t=0.0546875 cPt=(27.3480026,27.3382917) == oppT=0.00103625641 fPerpPt=(27.3480026,27.3382917)
-setPerp t=0.0009765625 cPt=(27.3477228,27.3385711) == oppT=0.0515372255 fPerpPt=(27.3477228,27.3385711)
-setPerp t=0.0625 cPt=(27.3486967,27.3375987) == oppT=0.00118429408 fPerpPt=(27.3486967,27.3375987)
-setPerp t=0.0703125 cPt=(27.3493908,27.3369058) == oppT=0.001332332 fPerpPt=(27.3493908,27.3369058)
-setPerp t=0.00122070313 cPt=(27.3488674,27.3374283) == oppT=0.0644214392 fPerpPt=(27.3488674,27.3374283)
-setPerp t=0.0546875 cPt=(27.3480026,27.3382917) == oppT=0.00103625641 fPerpPt=(27.3480026,27.3382917)
-setPerp t=0.0625 cPt=(27.3486967,27.3375987) == oppT=0.00118429408 fPerpPt=(27.3486967,27.3375987)
-setPerp t=0.00109863281 cPt=(27.3482951,27.3379997) == oppT=0.057979337 fPerpPt=(27.3482951,27.3379997)
-id=1 1=(0,0.0078125) [2] 37=(0.0078125,0.015625) [2,48] 27=(0.015625,0.0234375) [48] 35=(0.0234375,0.03125) [42,48] 21=(0.03125,0.0390625) [46,42] 33=(0.0390625,0.046875) [46] 25=(0.046875,0.0546875) [46] id=2 2=(0,0.000244141) [37,1] 48=(0.000244141,0.000488281) [37,35,27] 42=(0.000488281,0.000732422) [35,21] 46=(0.000732422,0.000976563) [33,21,25]
-setPerp t=0.046875 cPt=(27.3473086,27.3389848) == oppT=0.00088821901 fPerpPt=(27.3473086,27.3389848)
-setPerp t=0.0546875 cPt=(27.3480026,27.3382917) == oppT=0.00103625641 fPerpPt=(27.3480026,27.3382917)
-setPerp t=0.0009765625 cPt=(27.3477228,27.3385711) == oppT=0.0515372255 fPerpPt=(27.3477228,27.3385711)
-id=1 1=(0,0.0078125) [2] 37=(0.0078125,0.015625) [2,48] 27=(0.015625,0.0234375) [48] 35=(0.0234375,0.03125) [42,48] 21=(0.03125,0.0390625) [46,42] 33=(0.0390625,0.046875) [52,46] id=2 2=(0,0.000244141) [37,1] 48=(0.000244141,0.000488281) [37,35,27] 42=(0.000488281,0.000732422) [35,21] 46=(0.000732422,0.000854492) [33,21] 52=(0.000854492,0.000976563) [33]
-id=1 1=(0,0.0078125) [2] 37=(0.0078125,0.015625) [2,48] 27=(0.015625,0.0234375) [48] 35=(0.0234375,0.03125) [42,48] 21=(0.03125,0.0390625) [54,46,42] 33=(0.0390625,0.046875) [52,46] id=2 2=(0,0.000244141) [37,1] 48=(0.000244141,0.000488281) [37,35,27] 42=(0.000488281,0.000610352) [35,21] 54=(0.000610352,0.000732422) [21] 46=(0.000732422,0.000854492) [33,21] 52=(0.000854492,0.000976563) [33]
-id=1 1=(0,0.0078125) [2] 37=(0.0078125,0.015625) [2,48] 27=(0.015625,0.0234375) [56,48] 35=(0.0234375,0.03125) [56,42] 21=(0.03125,0.0390625) [54,46,42] 33=(0.0390625,0.046875) [52,46] id=2 2=(0,0.000244141) [37,1] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [27,35] 42=(0.000488281,0.000610352) [35,21] 54=(0.000610352,0.000732422) [21] 46=(0.000732422,0.000854492) [33,21] 52=(0.000854492,0.000976563) [33]
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.015625) [58,48] 27=(0.015625,0.0234375) [56,48] 35=(0.0234375,0.03125) [56,42] 21=(0.03125,0.0390625) [54,46,42] 33=(0.0390625,0.046875) [52,46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [1,37] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [27,35] 42=(0.000488281,0.000610352) [35,21] 54=(0.000610352,0.000732422) [21] 46=(0.000732422,0.000854492) [33,21] 52=(0.000854492,0.000976563) [33]
-setPerp t=0.000854492188 cPt=(27.3471505,27.3391427) == oppT=0.0450951047 fPerpPt=(27.3471505,27.3391427)
-setPerp t=0.0009765625 cPt=(27.3477228,27.3385711) == oppT=0.0515372255 fPerpPt=(27.3477228,27.3385711)
-setPerp t=0.046875 cPt=(27.3473086,27.3389848) == oppT=0.00088821901 fPerpPt=(27.3473086,27.3389848)
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.015625) [58,48] 27=(0.015625,0.0234375) [56,48] 35=(0.0234375,0.03125) [56,42] 21=(0.03125,0.0390625) [54,46,42] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [1,37] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [27,35] 42=(0.000488281,0.000610352) [35,21] 54=(0.000610352,0.000732422) [21] 46=(0.000732422,0.000854492) [39,33,21]
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.015625) [58,48] 27=(0.015625,0.0234375) [56,48] 35=(0.0234375,0.03125) [56,42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [46,54] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [1,37] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [27,35] 42=(0.000488281,0.000610352) [35,21] 54=(0.000610352,0.000732422) [41,21] 46=(0.000732422,0.000854492) [41,39,33]
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.015625) [58,48] 27=(0.015625,0.0234375) [56,48] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [46,54] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [1,37] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000732422) [41,21] 46=(0.000732422,0.000854492) [41,39,33]
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.015625) [58,48] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [46,54] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [1,37] 48=(0.000244141,0.000366211) [37,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000732422) [41,21] 46=(0.000732422,0.000854492) [41,39,33]
-id=1 1=(0,0.0078125) [58,2] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [46,54] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [1] 58=(0.00012207,0.000244141) [47,1,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000732422) [41,21] 46=(0.000732422,0.000854492) [41,39,33]
-id=1 1=(0,0.00390625) [2] 49=(0.00390625,0.0078125) [2,58] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [46,54] 33=(0.0390625,0.0429688) [46] 39=(0.0429688,0.046875) [46] id=2 2=(0,0.00012207) [49,1] 58=(0.00012207,0.000244141) [49,47,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000732422) [41,21] 46=(0.000732422,0.000854492) [41,39,33]
-setPerp t=0.03515625 cPt=(27.3462676,27.3400246) == oppT=0.000666163387 fPerpPt=(27.3462676,27.3400246)
-setPerp t=0.0390625 cPt=(27.3466146,27.3396779) == oppT=0.000740181863 fPerpPt=(27.3466146,27.3396779)
-setPerp t=0.000732421875 cPt=(27.3465782,27.3397143) == oppT=0.0386529746 fPerpPt=(27.3465782,27.3397143)
-setPerp t=0.0390625 cPt=(27.3466146,27.3396779) == oppT=0.000740181863 fPerpPt=(27.3466146,27.3396779)
-setPerp t=0.04296875 cPt=(27.3469616,27.3393313) == oppT=0.000814200404 fPerpPt=(27.3469616,27.3393313)
-setPerp t=0.000793457031 cPt=(27.3468644,27.3394285) == oppT=0.0418740408 fPerpPt=(27.3468644,27.3394285)
-setPerp t=0.0390625 cPt=(27.3466146,27.3396779) == oppT=0.000740181863 fPerpPt=(27.3466146,27.3396779)
-setPerp t=0.04296875 cPt=(27.3469616,27.3393313) == oppT=0.000814200404 fPerpPt=(27.3469616,27.3393313)
-setPerp t=0.000793457031 cPt=(27.3468644,27.3394285) == oppT=0.0418740408 fPerpPt=(27.3468644,27.3394285)
-setPerp t=0.04296875 cPt=(27.3469616,27.3393313) == oppT=0.000814200404 fPerpPt=(27.3469616,27.3393313)
-setPerp t=0.046875 cPt=(27.3473086,27.3389848) == oppT=0.00088821901 fPerpPt=(27.3473086,27.3389848)
-setPerp t=0.000854492188 cPt=(27.3471505,27.3391427) == oppT=0.0450951047 fPerpPt=(27.3471505,27.3391427)
-id=1 1=(0,0.00390625) [2] 49=(0.00390625,0.0078125) [2,58] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] 41=(0.0351563,0.0390625) [54] id=2 2=(0,0.00012207) [49,1] 58=(0.00012207,0.000244141) [49,47,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000732422) [41,21]
-setPerp t=0.03515625 cPt=(27.3462676,27.3400246) == oppT=0.000666163387 fPerpPt=(27.3462676,27.3400246)
-setPerp t=0.0390625 cPt=(27.3466146,27.3396779) == oppT=0.000740181863 fPerpPt=(27.3466146,27.3396779)
-setPerp t=0.000671386719 cPt=(27.3462921,27.3400001) == oppT=0.0354319062 fPerpPt=(27.3462921,27.3400001)
-setPerp t=0.03515625 cPt=(27.3462676,27.3400246) == oppT=0.000666163387 fPerpPt=(27.3462676,27.3400246)
-setPerp t=0.0390625 cPt=(27.3466146,27.3396779) == oppT=0.000740181863 fPerpPt=(27.3466146,27.3396779)
-setPerp t=0.000671386719 cPt=(27.3462921,27.3400001) == oppT=0.0354319062 fPerpPt=(27.3462921,27.3400001)
-setPerp t=0.000732421875 cPt=(27.3465782,27.3397143) == oppT=0.0386529746 fPerpPt=(27.3465782,27.3397143)
-id=1 1=(0,0.00390625) [2] 49=(0.00390625,0.0078125) [2,58] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [42] 21=(0.03125,0.0351563) [54,42] id=2 2=(0,0.00012207) [49,1] 58=(0.00012207,0.000244141) [49,47,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000610352) [43,35,21] 54=(0.000610352,0.000671387) [21]
-id=1 1=(0,0.00390625) [2] 49=(0.00390625,0.0078125) [2,58] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [56] 35=(0.0234375,0.0273438) [56,42] 43=(0.0273438,0.03125) [64,42] 21=(0.03125,0.0351563) [64,54] id=2 2=(0,0.00012207) [49,1] 58=(0.00012207,0.000244141) [49,47,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000488281) [45,27,35] 42=(0.000488281,0.000549316) [43,35] 64=(0.000549316,0.000610352) [21,43] 54=(0.000610352,0.000671387) [21]
-id=1 1=(0,0.00390625) [2] 49=(0.00390625,0.0078125) [2,58] 37=(0.0078125,0.0117188) [58] 47=(0.0117188,0.015625) [48,58] 27=(0.015625,0.0195313) [56,48] 45=(0.0195313,0.0234375) [66,56] 35=(0.0234375,0.0273438) [66,42] 43=(0.0273438,0.03125) [64,42] 21=(0.03125,0.0351563) [64,54] id=2 2=(0,0.00012207) [49,1] 58=(0.00012207,0.000244141) [49,47,37] 48=(0.000244141,0.000366211) [47,27] 56=(0.000366211,0.000427246) [45,27] 66=(0.000427246,0.000488281) [35,45] 42=(0.000488281,0.000549316) [43,35] 64=(0.000549316,0.000610352) [21,43] 54=(0.000610352,0.000671387) [21]
-setPerp t=0 cPt=(27.3431454,27.3431454) == oppT=0 fPerpPt=(27.3431454,27.3431454)
-setPerp t=0.00390625 cPt=(27.3434922,27.3427985) == oppT=7.40178961e-05 fPerpPt=(27.3434922,27.3427985)
-setPerp t=0.0078125 cPt=(27.3438391,27.3424517) == oppT=0.000148035857 fPerpPt=(27.3438391,27.3424517)
-setPerp t=0.01171875 cPt=(27.344186,27.3421049) == oppT=0.000222053882 fPerpPt=(27.344186,27.3421049)
-setPerp t=0.015625 cPt=(27.3445329,27.3417581) == oppT=0.000296071971 fPerpPt=(27.3445329,27.3417581)
-setPerp t=0.01953125 cPt=(27.3448799,27.3414113) == oppT=0.000370090126 fPerpPt=(27.3448799,27.3414113)
-setPerp t=0.0234375 cPt=(27.3452268,27.3410646) == oppT=0.000444108344 fPerpPt=(27.3452268,27.3410646)
-setPerp t=0.02734375 cPt=(27.3455737,27.3407179) == oppT=0.000518126627 fPerpPt=(27.3455737,27.3407179)
-setPerp t=0.03125 cPt=(27.3459207,27.3403712) == oppT=0.000592144975 fPerpPt=(27.3459207,27.3403712)
-setPerp t=0.03515625 cPt=(27.3462676,27.3400246) == oppT=0.000666163387 fPerpPt=(27.3462676,27.3400246)
-setPerp t=0 cPt=(27.3431454,27.3431454) == oppT=0 fPerpPt=(27.3431454,27.3431454)
-setPerp t=0.03515625 cPt=(27.3462676,27.3400246) == oppT=0.000666163387 fPerpPt=(27.3462676,27.3400246)
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection wtTs[0]=0 {{{27.3431454,27.3431454}, {27.3875446,27.2987461}, {27.4323025,27.2551785}}} {{27.3431454,27.3431454}} wtTs[1]=0.03515625 {{27.3462677,27.3400249}} wnTs[0]=0 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}} wnTs[1]=0.000666163387
-SkOpSegment::addT insert t=0.03515625 segID=20 spanID=49
-SkOpSegment::addT insert t=0.000666163387 segID=6 spanID=50
-id=1 1=(0,1) [2] id=2 2=(0,0.5) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.25) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.125) [1]
-id=1 1=(0,1) [2] id=2 2=(0,0.0625) [1]
-id=1 1=(0,1) [12,2] id=2 2=(0,0.03125) [1] 12=(0.03125,0.0625) [1]
-id=1 1=(0,1) [12,2] id=2 2=(0,0.03125) [1] 12=(0.03125,0.046875) [1]
-id=1 1=(0,1) [16,12] id=2 16=(0.015625,0.03125) [1] 12=(0.03125,0.046875) [1]
-id=1 1=(0,0.5) [16] 3=(0.5,1) [16] id=2 16=(0.015625,0.03125) [3,1]
-id=1 1=(0,0.5) [18,16] id=2 16=(0.015625,0.0234375) [1] 18=(0.0234375,0.03125) [1]
-id=1 1=(0,0.25) [16] id=2 16=(0.015625,0.0234375) [1]
-id=1 1=(0,0.25) [20,16] id=2 16=(0.015625,0.0195313) [1] 20=(0.0195313,0.0234375) [1]
-id=1 1=(0,0.125) [20,16] id=2 16=(0.015625,0.0195313) [1] 20=(0.0195313,0.0234375) [1]
-setPerp t=0 cPt=(27.4323025,27.2551785) == oppT=0.0189506973 fPerpPt=(27.4323024,27.2551784)
-setPerp t=0.125 cPt=(27.4431369,27.243922) != oppT=0.0213231007 fPerpPt=(27.4435129,27.2442845)
-setPerp t=0.01953125 cPt=(27.4350447,27.2525101) != oppT=0.0306377854 fPerpPt=(27.4349556,27.2524185)
-id=1 1=(0,0.125) [16] id=2 16=(0.015625,0.0195313) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{27.4323025,27.2551785}, {27.4755878,27.2101307}, {27.5197105,27.165432}}} {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-debugShowQuadIntersection no intersect {{{27.5197105,27.165432}, {27.541851,27.1430035}, {27.5638676,27.1209965}}} {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{27.5638676,27.1209965}, {27.5855064,27.0986347}, {27.6075668,27.0761414}}} {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-id=1 1=(0,1) [4,2] id=2 2=(0,0.5) [1] 4=(0.5,1) [1]
-id=1 1=(0,0.5) [2] 3=(0.5,1) [4] id=2 2=(0,0.5) [1] 4=(0.5,1) [3]
-id=1 1=(0,0.5) [2] id=2 2=(0,0.5) [1]
-id=1 1=(0,0.5) [8,2] id=2 2=(0,0.25) [1] 8=(0.25,0.5) [1]
-id=1 1=(0,0.25) [2] id=2 2=(0,0.25) [1]
-id=1 1=(0,0.25) [10,2] id=2 2=(0,0.125) [1] 10=(0.125,0.25) [1]
-id=1 (empty) id=2 (empty)
-debugShowQuadIntersection no intersect {{{27.6075668,27.0761414}, {29.9278316,24.7103367}, {33.2413864,24.6781349}}} {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-debugShowQuadIntersection no intersect {{{27.6075668,27.0761414}, {29.9278316,24.7103367}, {33.2413864,24.6781349}}} {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}} {{38.6568527,38.6568527}} wnTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}}
-debugShowQuadIntersection wtTs[0]=0 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}} {{41,33}} wnTs[0]=1 {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{33,41}} wnTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{27.3431454,38.6568527}} wnTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{25,33}} wnTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{27.3431454,27.3431454}} wnTs[0]=0 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}} {{33,25}} wnTs[0]=0 {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}} {{38.6568527,27.3431454}} wnTs[0]=0 {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-SkOpSegment::markDone id=6 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [11] (27.3431454,27.3431454) tEnd=0.000666163387 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::markDone id=5 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 [9] (25,33) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::markDone id=4 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [7] (27.3431454,38.6568527) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::markDone id=3 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 [5] (33,41) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::markDone id=2 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [3] (38.6568527,38.6568527) tEnd=1 newWindSum=? newOppSum=? oppSum=? windSum=? windValue=0 oppValue=0
-SkOpSegment::sortAngles [15] tStart=1 [30]
-SkOpAngle::after [15/1] 4/5 tStart=1 tEnd=0 < [16/2] 21/17 tStart=0 tEnd=1 < [1/13] 1/5 tStart=1 tEnd=0 T 5
-SkOpAngle::afterPart {{{38.6568527,38.6568527}, {38.7196693,38.5940361}, {38.7809143,38.5304031}}} id=15
-SkOpAngle::afterPart {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} id=16
-SkOpAngle::afterPart {{{38.6568527,38.6568527}, {41,36.3137093}, {41,33}}} id=1
-SkOpSegment::sortAngles [16] tStart=0 [31]
-SkOpSegment::sortAngles [16] tStart=1 [32]
-SkOpSegment::sortAngles [17] tStart=0 [33]
-SkOpSegment::sortAngles [17] tStart=1 [34]
-SkOpSegment::sortAngles [18] tStart=0 [35]
-SkOpSegment::sortAngles [18] tStart=1 [36]
-SkOpSegment::sortAngles [19] tStart=0 [37]
-SkOpSegment::sortAngles [19] tStart=1 [38]
-SkOpSegment::sortAngles [20] tStart=0 [39]
-SkOpSegment::sortAngles [20] tStart=0.03515625 [49]
-SkOpAngle::after [20/11] 17/17 tStart=0.03515625 tEnd=0 < [6/14] 1/1 tStart=0.000666163387 tEnd=1 < [20/12] 1/1 tStart=0.03515625 tEnd=1 F 11
-SkOpAngle::afterPart {{{27.3462677,27.3400249}, {27.3447063,27.3415846}, {27.3431454,27.3431454}}} id=20
-SkOpAngle::afterPart {{{27.3462677,27.3400249}, {29.6884986,25}, {33,25}}} id=6
-SkOpAngle::afterPart {{{27.3462677,27.3400249}, {27.3891352,27.2971979}, {27.4323025,27.2551785}}} id=20
+<div id="cubics_d3">
+seg=1 {{{3, 4}, {1.5f, 5}, {2.25f, 4.25f}, {3.125f, 3.375f}}}
+seg=2 {{{3.125f, 3.375f}, {4, 2.5f}, {5, 1.5f}, {4, 2}}}
+seg=3 {{{4, 2}, {3, 4}}}
+op diff
+seg=4 {{{1, 6}, {1.5f, 5}, {2.25f, 4.25f}, {3.125f, 3.375f}}}
+seg=5 {{{3.125f, 3.375f}, {4, 2.5f}, {5, 1.5f}, {6, 0}}}
+seg=6 {{{6, 0}, {1, 6}}}
+debugShowCubicIntersection wtTs[0]=1 {{{1,6}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{3.125,3.375}} wnTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {6,0}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{1,6}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{1,6}} wnTs[0]=1 {{{6,0}, {1,6}}}
+debugShowCubicLineIntersection wtTs[0]=0.142857143 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {6,0}}} {{3.50728869,2.99125361}} wtTs[1]=1 {{6,0}} wnTs[0]=0.498542 {{{6,0}, {1,6}}} wnTs[1]=0
+SkOpSegment::addT insert t=0.142857143 segID=5 spanID=13
+SkOpSegment::addT insert t=0.498542274 segID=6 spanID=14
+debugShowCubicIntersection wtTs[0]=1 {{{1,6}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{3.125,3.375}} wnTs[0]=1 {{{3,4}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}}
+debugShowCubicIntersection wtTs[0]=1 {{{1,6}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{3.125,3.375}} wnTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {4,2}}}
+debugShowCubicLineIntersection no intersect {{{1,6}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{{4,2}, {3,4}}}
+debugShowCubicIntersection wtTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {6,0}}} {{3.125,3.375}} wnTs[0]=1 {{{3,4}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}}
+debugShowCubicIntersection wtTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {6,0}}} {{3.125,3.375}} wnTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {4,2}}}
+debugShowCubicLineIntersection wtTs[0]=0.140692452 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {6,0}}} {{3.50139236,2.99721503}} wnTs[0]=0.498608 {{{4,2}, {3,4}}}
+SkOpSegment::addT insert t=0.140692452 segID=5 spanID=15
+SkOpSegment::addT insert t=0.498607541 segID=3 spanID=16
+debugShowCubicLineIntersection wtTs[0]=0.220070773 {{{3,4}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{2.31394291,4.42326832}} wnTs[0]=0.737211 {{{6,0}, {1,6}}}
+SkOpSegment::addT insert t=0.737211419 segID=6 spanID=17
+SkOpSegment::addT insert t=0.220070773 segID=1 spanID=18
+debugShowCubicLineIntersection wtTs[0]=0.145241853 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {4,2}}} {{3.50765967,2.99080825}} wtTs[1]=0.715768455 {{4.41676426,1.89988291}} wnTs[0]=0.498468 {{{6,0}, {1,6}}} wnTs[1]=0.316647149
+SkOpSegment::addT insert t=0.498468047 segID=6 spanID=19
+SkOpSegment::addT insert t=0.145241853 segID=2 spanID=20
+SkOpSegment::addT insert t=0.316647149 segID=6 spanID=21
+SkOpSegment::addT insert t=0.715768455 segID=2 spanID=22
+debugShowLineIntersection wtTs[0]=0.5 {{{6,0}, {1,6}}} {{3.5,3}} wnTs[0]=0.5 {{{4,2}, {3,4}}}
+SkOpSegment::addT insert t=0.5 segID=6 spanID=23
+SkOpSegment::addT insert t=0.5 segID=3 spanID=24
+debugShowCubicIntersection wtTs[0]=1 {{{3,4}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{3.125,3.375}} wnTs[0]=0 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {4,2}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{3,4}, {1.5,5}, {2.25,4.25}, {3.125,3.375}}} {{3,4}} wnTs[0]=1 {{{4,2}, {3,4}}}
+debugShowCubicLineIntersection wtTs[0]=0.142857143 {{{3.125,3.375}, {4,2.5}, {5,1.5}, {4,2}}} {{3.50145769,2.99708462}} wtTs[1]=1 {{4,2}} wnTs[0]=0.498542 {{{4,2}, {3,4}}} wnTs[1]=0
+SkOpSegment::addT insert t=0.142857143 segID=2 spanID=25
+SkOpSegment::addT insert t=0.498542274 segID=3 spanID=26
+--------------------------------- start
+active after start:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- addExpanded
+active after addExpanded:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- moveMultiples
+active after moveMultiples:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- moveNearby
+active after moveNearby:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- addEndMovedSpans
+active after addEndMovedSpans:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- addMissing2
+active after addMissing2:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- moveNearby2
+active after moveNearby2:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- expand2
+active after expand2:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- addExpanded3
+active after addExpanded3:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- mark1
+active after mark1:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- missingCoincidence2
+active after missingCoincidence2:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- missingCoincidence3
+active after missingCoincidence3:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- coincidence.reorder
+active after coincidence.reorder:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- pairs->apply
+active after pairs->apply:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+--------------------------------- pairs->findOverlaps
+active after pairs->findOverlaps:
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+SkOpSegment::sortAngles [4] tStart=1 [8]
+SkOpAngle::after [4/1] 20/21 tStart=1 tEnd=0 < [1/19] 20/21 tStart=1 tEnd=0.220070773 < [2/20] 4/5 tStart=0 tEnd=0.142857143 T 12
+SkOpAngle::afterPart {{{3.125,3.375}, {2.25,4.25}, {1.5,5}, {1,6}}} id=4
+SkOpAngle::afterPart {{{3.125,3.375}, {2.44256193,4.05743807}, {1.83616005,4.66383975}, {2.31394291,4.42326832}}} id=1
+SkOpAngle::afterPart {{{3.125,3.375}, {3.25,3.25}, {3.37755099,3.12244905}, {3.50145769,2.99708462}}} id=2
+SkOpAngle::after [4/1] 20/21 tStart=1 tEnd=0 < [5/2] 4/5 tStart=0 tEnd=0.140692452 < [1/19] 20/21 tStart=1 tEnd=0.220070773 F 5
+SkOpAngle::afterPart {{{3.125,3.375}, {2.25,4.25}, {1.5,5}, {1,6}}} id=4
+SkOpAngle::afterPart {{{3.125,3.375}, {3.2481059,3.2518941}, {3.37368599,3.12631386}, {3.50139236,2.99721503}}} id=5
+SkOpAngle::afterPart {{{3.125,3.375}, {2.44256193,4.05743807}, {1.83616005,4.66383975}, {2.31394291,4.42326832}}} id=1
+SkOpAngle::after [1/19] 20/21 tStart=1 tEnd=0.220070773 < [5/2] 4/5 tStart=0 tEnd=0.140692452 < [2/20] 4/5 tStart=0 tEnd=0.142857143 T 11
+SkOpAngle::afterPart {{{3.125,3.375}, {2.44256193,4.05743807}, {1.83616005,4.66383975}, {2.31394291,4.42326832}}} id=1
+SkOpAngle::afterPart {{{3.125,3.375}, {3.2481059,3.2518941}, {3.37368599,3.12631386}, {3.50139236,2.99721503}}} id=5
+SkOpAngle::afterPart {{{3.125,3.375}, {3.25,3.25}, {3.37755099,3.12244905}, {3.50145769,2.99708462}}} id=2
+SkOpSegment::sortAngles [5] tStart=0 [9]
+SkOpSegment::sortAngles [5] tStart=0.140692452 [15]
+SkOpAngle::after [5/3] 21/21 tStart=0.140692452 tEnd=0 < [3/29] 5/5 tStart=0.498607541 tEnd=0.498542274 < [5/4] 5/5 tStart=0.140692452 tEnd=0.142857143 T 11
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.37368599,3.12631386}, {3.2481059,3.2518941}, {3.125,3.375}}} id=5
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.50145769,2.99708462}}} id=3
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.50335725,2.99522872}, {3.5053228,2.9932416}, {3.50728869,2.99125361}}} id=5
+SkOpAngle::after [5/3] 21/21 tStart=0.140692452 tEnd=0 < [3/30] 21/21 tStart=0.498607541 tEnd=0.5 < [3/29] 5/5 tStart=0.498607541 tEnd=0.498542274 T 12
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.37368599,3.12631386}, {3.2481059,3.2518941}, {3.125,3.375}}} id=5
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.5,3}}} id=3
+SkOpAngle::afterPart {{{3.50139236,2.99721503}, {3.50145769,2.99708462}}} id=3
+SkOpSegment::sortAngles [5] tStart=0.142857143 [13]
+SkOpAngle::after [5/5] 21/21 tStart=0.142857143 tEnd=0.140692452 < [6/11] 5/5 tStart=0.498542274 tEnd=0.498468047 < [5/6] 5/5 tStart=0.142857143 tEnd=1 F 11
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {3.5053228,2.9932416}, {3.50335725,2.99522872}, {3.50139236,2.99721503}}} id=5
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {3.50765967,2.99080825}}} id=6
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {4.28571435,2.2040816}, {5.14285714,1.28571429}, {6,0}}} id=5
+SkOpAngle::after [5/5] 21/21 tStart=0.142857143 tEnd=0.140692452 < [6/12] 21/21 tStart=0.498542274 tEnd=0.5 < [5/6] 5/5 tStart=0.142857143 tEnd=1 T 12
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {3.5053228,2.9932416}, {3.50335725,2.99522872}, {3.50139236,2.99721503}}} id=5
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {3.5,3}}} id=6
+SkOpAngle::afterPart {{{3.50728869,2.99125361}, {4.28571435,2.2040816}, {5.14285714,1.28571429}, {6,0}}} id=5
+SkOpSegment::sortAngles [6] tStart=0.316647149 [21]
+SkOpAngle::after [6/7] 5/5 tStart=0.316647149 tEnd=0 < [2/25] 25/21 tStart=0.715768455 tEnd=0.145241853 < [6/8] 21/21 tStart=0.316647149 tEnd=0.498468047 F 11
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {6,0}}} id=6
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {4.43658858,2.02620596}, {4.00201137,2.49043886}, {3.50765967,2.99080825}}} id=2
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {3.50765967,2.99080825}}} id=6
+SkOpAngle::after [6/7] 5/5 tStart=0.316647149 tEnd=0 < [2/26] 9/17 tStart=0.715768455 tEnd=1 < [6/8] 21/21 tStart=0.316647149 tEnd=0.498468047 T 4
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {6,0}}} id=6
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {4.40688795,1.83694983}, {4.28423154,1.85788423}, {4,2}}} id=2
+SkOpAngle::afterPart {{{4.41676426,1.89988291}, {3.50765967,2.99080825}}} id=6
+SkOpSegment::sortAngles [6] tStart=0.498468047 [19]
+SkOpAngle::after [6/9] 5/5 tStart=0.498468047 tEnd=0.316647149 < [2/23] 21/21 tStart=0.145241853 tEnd=0.142857143 < [6/10] 21/21 tStart=0.498468047 tEnd=0.498542274 T 11
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.41676426,1.89988291}}} id=6
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50559336,2.99289971}, {3.50352606,2.99499191}, {3.50145769,2.99708462}}} id=2
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50728869,2.99125361}}} id=6
+SkOpAngle::after [6/9] 5/5 tStart=0.498468047 tEnd=0.316647149 < [2/24] 5/5 tStart=0.145241853 tEnd=0.715768455 < [2/23] 21/21 tStart=0.145241853 tEnd=0.142857143 F 12
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.41676426,1.89988291}}} id=6
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.00201137,2.49043886}, {4.43658858,2.02620596}, {4.41676426,1.89988291}}} id=2
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50559336,2.99289971}, {3.50352606,2.99499191}, {3.50145769,2.99708462}}} id=2
+SkOpAngle::after [2/23] 21/21 tStart=0.145241853 tEnd=0.142857143 < [2/24] 5/5 tStart=0.145241853 tEnd=0.715768455 < [6/10] 21/21 tStart=0.498468047 tEnd=0.498542274 F 5
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50559336,2.99289971}, {3.50352606,2.99499191}, {3.50145769,2.99708462}}} id=2
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.00201137,2.49043886}, {4.43658858,2.02620596}, {4.41676426,1.89988291}}} id=2
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50728869,2.99125361}}} id=6
+SkOpAngle::after [6/10] 21/21 tStart=0.498468047 tEnd=0.498542274 < [2/24] 5/5 tStart=0.145241853 tEnd=0.715768455 < [6/9] 5/5 tStart=0.498468047 tEnd=0.316647149 T 11
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {3.50728869,2.99125361}}} id=6
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.00201137,2.49043886}, {4.43658858,2.02620596}, {4.41676426,1.89988291}}} id=2
+SkOpAngle::afterPart {{{3.50765967,2.99080825}, {4.41676426,1.89988291}}} id=6
+SkOpSegment::sortAngles [6] tStart=0.498542274 [14]
+SkOpSegment::sortAngles [6] tStart=0.5 [23]
+SkOpAngle::after [6/13] 5/5 tStart=0.5 tEnd=0.498542274 < [3/31] 5/5 tStart=0.5 tEnd=0.498607541 < [6/14] 21/21 tStart=0.5 tEnd=0.737211419 T 12
+SkOpAngle::afterPart {{{3.5,3}, {3.50728869,2.99125361}}} id=6
+SkOpAngle::afterPart {{{3.5,3}, {3.50139236,2.99721503}}} id=3
+SkOpAngle::afterPart {{{3.5,3}, {2.31394291,4.42326832}}} id=6
+SkOpAngle::after [6/13] 5/5 tStart=0.5 tEnd=0.498542274 < [3/32] 21/21 tStart=0.5 tEnd=1 < [3/31] 5/5 tStart=0.5 tEnd=0.498607541 F 5
+SkOpAngle::afterPart {{{3.5,3}, {3.50728869,2.99125361}}} id=6
+SkOpAngle::afterPart {{{3.5,3}, {3,4}}} id=3
+SkOpAngle::afterPart {{{3.5,3}, {3.50139236,2.99721503}}} id=3
+SkOpAngle::after [3/31] 5/5 tStart=0.5 tEnd=0.498607541 < [3/32] 21/21 tStart=0.5 tEnd=1 < [6/14] 21/21 tStart=0.5 tEnd=0.737211419 F 11
+SkOpAngle::afterPart {{{3.5,3}, {3.50139236,2.99721503}}} id=3
+SkOpAngle::afterPart {{{3.5,3}, {3,4}}} id=3
+SkOpAngle::afterPart {{{3.5,3}, {2.31394291,4.42326832}}} id=6
+SkOpAngle::after [6/14] 21/21 tStart=0.5 tEnd=0.737211419 < [3/32] 21/21 tStart=0.5 tEnd=1 < [6/13] 5/5 tStart=0.5 tEnd=0.498542274 T 12
+SkOpAngle::afterPart {{{3.5,3}, {2.31394291,4.42326832}}} id=6
+SkOpAngle::afterPart {{{3.5,3}, {3,4}}} id=3
+SkOpAngle::afterPart {{{3.5,3}, {3.50728869,2.99125361}}} id=6
+SkOpSegment::sortAngles [6] tStart=0.737211419 [17]
+SkOpAngle::after [6/15] 5/5 tStart=0.737211419 tEnd=0.5 < [1/17] 1/1 tStart=0.220070773 tEnd=0 < [6/16] 21/21 tStart=0.737211419 tEnd=1 F 4
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {3.5,3}}} id=6
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {2.44875776,4.35538685}, {2.66989384,4.22007077}, {3,4}}} id=1
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {1,6}}} id=6
+SkOpAngle::after [6/15] 5/5 tStart=0.737211419 tEnd=0.5 < [1/18] 17/5 tStart=0.220070773 tEnd=1 < [6/16] 21/21 tStart=0.737211419 tEnd=1 T 12
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {3.5,3}}} id=6
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {1.83616005,4.66383975}, {2.44256193,4.05743807}, {3.125,3.375}}} id=1
+SkOpAngle::afterPart {{{2.31394291,4.42326832}, {1,6}}} id=6
+SkOpSegment::sortAngles [1] tStart=0.220070773 [18]
SkOpSegment::sortAngles [1] tStart=1 [2]
-SkOpSegment::sortAngles [6] tStart=0.000666163387 [50]
-SkOpCoincidence::debugShowCoincidence - id=20 t=0 tEnd=0.03515625
-SkOpCoincidence::debugShowCoincidence + id=6 t=0 tEnd=0.000666163387
-SkOpCoincidence::debugShowCoincidence - id=19 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence + id=5 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence - id=18 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence + id=4 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence - id=17 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence + id=3 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence - id=16 t=0 tEnd=1
-SkOpCoincidence::debugShowCoincidence + id=2 t=0 tEnd=1
-SkOpSegment::debugShowActiveSpans id=9 (33.2413864,24.6781349 36.5549393,24.6459332 38.920742,26.966198) t=0 (33.2413864,24.6781349) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=10 (38.920742,26.966198 41.2865486,29.2864628 41.3187523,32.6000175) t=0 (38.920742,26.966198) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=11 (41.3187523,32.6000175 41.3509521,35.9135704 39.0306854,38.2793732) t=0 (41.3187523,32.6000175) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=12 (39.0306854,38.2793732 38.9995995,38.3110695 38.9681816,38.3424988) t=0 (39.0306854,38.2793732) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=13 (38.9681816,38.3424988 38.9374619,38.3742142 38.9064751,38.4056053) t=0 (38.9681816,38.3424988) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=14 (38.9064751,38.4056053 38.8441086,38.4687881 38.7809143,38.5304031) t=0 (38.9064751,38.4056053) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=15 (38.7809143,38.5304031 38.7196693,38.5940361 38.6568527,38.6568527) t=0 (38.7809143,38.5304031) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=16 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 (38.6568527,38.6568527) tEnd=1 windSum=? windValue=2
-SkOpSegment::debugShowActiveSpans id=17 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 windSum=? windValue=2
-SkOpSegment::debugShowActiveSpans id=18 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 windSum=? windValue=2
-SkOpSegment::debugShowActiveSpans id=19 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 (25,33) tEnd=1 windSum=? windValue=2
-SkOpSegment::debugShowActiveSpans id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0 (27.3431454,27.3431454) tEnd=0.03515625 windSum=? windValue=2
-SkOpSegment::debugShowActiveSpans id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0.03515625 (27.3462677,27.3400249) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=21 (27.4323025,27.2551785 27.4755878,27.2101307 27.5197105,27.165432) t=0 (27.4323025,27.2551785) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=22 (27.5197105,27.165432 27.541851,27.1430035 27.5638676,27.1209965) t=0 (27.5197105,27.165432) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=23 (27.5638676,27.1209965 27.5855064,27.0986347 27.6075668,27.0761414) t=0 (27.5638676,27.1209965) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=24 (27.6075668,27.0761414 29.9278316,24.7103367 33.2413864,24.6781349) t=0 (27.6075668,27.0761414) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=1 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 (41,33) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=6 (27.3431454,27.3431454 29.6862907,25 33,25) t=0.000666163387 (27.3462677,27.3400249) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=7 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 (33,25) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=8 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 (38.6568527,27.3431454) tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kTop seg=9 t=0.5 pt=(36.3180008,25.2340508)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=17 ccw=1 seg=9 {{{33.2413864f, 24.6781349f}, {36.5549393f, 24.6459332f}, {38.920742f, 26.966198f}}} t=0.5 pt=(36.3180008,25.2340508) slope=(2.83967781,1.14403152)
-SkOpSegment::markWinding id=9 (33.2413864,24.6781349 36.5549393,24.6459332 38.920742,26.966198) t=0 [17] (33.2413864,24.6781349) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=10 (38.920742,26.966198 41.2865486,29.2864628 41.3187523,32.6000175) t=0 [19] (38.920742,26.966198) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=11 (41.3187523,32.6000175 41.3509521,35.9135704 39.0306854,38.2793732) t=0 [21] (41.3187523,32.6000175) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=12 (39.0306854,38.2793732 38.9995995,38.3110695 38.9681816,38.3424988) t=0 [23] (39.0306854,38.2793732) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=13 (38.9681816,38.3424988 38.9374619,38.3742142 38.9064751,38.4056053) t=0 [25] (38.9681816,38.3424988) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=14 (38.9064751,38.4056053 38.8441086,38.4687881 38.7809143,38.5304031) t=0 [27] (38.9064751,38.4056053) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=15 (38.7809143,38.5304031 38.7196693,38.5940361 38.6568527,38.6568527) t=0 [29] (38.7809143,38.5304031) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=9 (33.2413864,24.6781349 36.5549393,24.6459332 38.920742,26.966198) t=0 [17] (33.2413864,24.6781349) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=24 (27.6075668,27.0761414 29.9278316,24.7103367 33.2413864,24.6781349) t=0 [47] (27.6075668,27.0761414) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=23 (27.5638676,27.1209965 27.5855064,27.0986347 27.6075668,27.0761414) t=0 [45] (27.5638676,27.1209965) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=22 (27.5197105,27.165432 27.541851,27.1430035 27.5638676,27.1209965) t=0 [43] (27.5197105,27.165432) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=21 (27.4323025,27.2551785 27.4755878,27.2101307 27.5197105,27.165432) t=0 [41] (27.4323025,27.2551785) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0.03515625 [49] (27.3462677,27.3400249) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=9 (33.2413864,24.6781349 36.5549393,24.6459332 38.920742,26.966198) t=0 [17] (33.2413864,24.6781349) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=9 from=(38.920742,26.966198) to=(33.2413864,24.6781349)
-path.moveTo(38.920742,26.966198);
-path.quadTo(36.5549393,24.6459332, 33.2413864,24.6781349);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=24 (27.6075668,27.0761414 29.9278316,24.7103367 33.2413864,24.6781349) t=0 [47] (27.6075668,27.0761414) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=24 from=(33.2413864,24.6781349) to=(27.6075668,27.0761414)
-path.quadTo(29.9278316,24.7103367, 27.6075668,27.0761414);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=23 (27.5638676,27.1209965 27.5855064,27.0986347 27.6075668,27.0761414) t=0 [45] (27.5638676,27.1209965) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=23 from=(27.6075668,27.0761414) to=(27.5638676,27.1209965)
-path.quadTo(27.5855064,27.0986347, 27.5638676,27.1209965);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=22 (27.5197105,27.165432 27.541851,27.1430035 27.5638676,27.1209965) t=0 [43] (27.5197105,27.165432) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=22 from=(27.5638676,27.1209965) to=(27.5197105,27.165432)
-path.quadTo(27.541851,27.1430035, 27.5197105,27.165432);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=21 (27.4323025,27.2551785 27.4755878,27.2101307 27.5197105,27.165432) t=0 [41] (27.4323025,27.2551785) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=21 from=(27.5197105,27.165432) to=(27.4323025,27.2551785)
-path.quadTo(27.4755878,27.2101307, 27.4323025,27.2551785);
-SkOpSegment::markWinding id=6 (27.3431454,27.3431454 29.6862907,25 33,25) t=0.000666163387 [50] (27.3462677,27.3400249) tEnd=1 newWindSum=1 windSum=? windValue=1
-SkOpSegment::markWinding id=7 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [13] (33,25) tEnd=1 newWindSum=1 windSum=? windValue=1
-SkOpSegment::markWinding id=8 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [15] (38.6568527,27.3431454) tEnd=1 newWindSum=1 windSum=? windValue=1
-SkOpSegment::markWinding id=1 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=1 windSum=? windValue=1
-SkOpSegment::markAngle last seg=1 span=2
-SkOpSegment::markWinding id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0 [39] (27.3431454,27.3431454) tEnd=0.03515625 newWindSum=1 windSum=? windValue=2
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=19 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 [37] (25,33) tEnd=1 newWindSum=1 windSum=? windValue=2
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=18 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [35] (27.3431454,38.6568527) tEnd=1 newWindSum=1 windSum=? windValue=2
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=17 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 [33] (33,41) tEnd=1 newWindSum=1 windSum=? windValue=2
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=16 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [31] (38.6568527,38.6568527) tEnd=1 newWindSum=1 windSum=? windValue=2
-SkOpSegment::markAngle last seg=16 span=31 windSum=1
-SkOpSegment::findNextWinding
-SkOpAngle::dumpOne [20/12] next=6/14 sect=1/1 s=0.03515625 [49] e=1 [40] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
-SkOpAngle::dumpOne [6/14] next=20/11 sect=1/1 s=0.000666163387 [50] e=1 [12] sgn=-1 windVal=1 windSum=1
-SkOpAngle::dumpOne [20/11] next=20/12 sect=17/17 s=0.03515625 [49] e=0 [39] sgn=1 windVal=2 windSum=1
-SkOpSegment::findNextWinding chase.append segment=1 span=2
-SkOpSegment::markDone id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0 [39] (27.3431454,27.3431454) tEnd=0.03515625 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=2 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markDone id=19 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 [37] (25,33) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=2 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markDone id=18 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [35] (27.3431454,38.6568527) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=2 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markDone id=17 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 [33] (33,41) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=2 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markDone id=16 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [31] (38.6568527,38.6568527) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=2 oppValue=0
-SkOpSegment::findNextWinding chase.append segment=16 span=31 windSum=1
-SkOpSegment::markDone id=20 (27.3431454,27.3431454 27.3875446,27.2987461 27.4323025,27.2551785) t=0.03515625 [49] (27.3462677,27.3400249) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextWinding from:[20] to:[6] start=5584652 end=5579668
-bridgeWinding current id=20 from=(27.4323025,27.2551785) to=(27.3462677,27.3400249)
-path.quadTo(27.3891354,27.2971973, 27.3462677,27.3400249);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=6 (27.3431454,27.3431454 29.6862907,25 33,25) t=0.000666163387 [50] (27.3462677,27.3400249) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0
-bridgeWinding current id=6 from=(27.3462677,27.3400249) to=(33,25)
-path.quadTo(29.6884995,25, 33,25);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=7 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [13] (33,25) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0
-bridgeWinding current id=7 from=(33,25) to=(38.6568527,27.3431454)
-path.quadTo(36.3137093,25, 38.6568527,27.3431454);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=8 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [15] (38.6568527,27.3431454) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0
-bridgeWinding current id=8 from=(38.6568527,27.3431454) to=(41,33)
-path.quadTo(41,29.6862907, 41,33);
-SkOpSegment::findNextWinding
-SkOpAngle::dumpOne [1/13] next=15/1 sect=1/5 s=1 [2] e=0 [1] sgn=1 windVal=1 windSum=1
-SkOpAngle::dumpOne [15/1] next=16/2 sect=4/5 s=1 [30] e=0 [29] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0
-SkOpAngle::dumpOne [16/2] next=1/13 sect=21/17 s=0 [31] e=1 [32] sgn=-1 windVal=2 windSum=1 done
-SkOpSegment::markDone id=1 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=1 newOppSum=? oppSum=? windSum=1 windValue=1 oppValue=0
-SkOpSegment::findNextWinding from:[1] to:[15] start=5581892 end=5581788
-bridgeWinding current id=1 from=(41,33) to=(38.6568527,38.6568527)
-path.quadTo(41,36.3137093, 38.6568527,38.6568527);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=15 (38.7809143,38.5304031 38.7196693,38.5940361 38.6568527,38.6568527) t=0 [29] (38.7809143,38.5304031) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=15 from=(38.6568527,38.6568527) to=(38.7809143,38.5304031)
-path.quadTo(38.7196693,38.5940361, 38.7809143,38.5304031);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=14 (38.9064751,38.4056053 38.8441086,38.4687881 38.7809143,38.5304031) t=0 [27] (38.9064751,38.4056053) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=14 from=(38.7809143,38.5304031) to=(38.9064751,38.4056053)
-path.quadTo(38.8441086,38.4687881, 38.9064751,38.4056053);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=13 (38.9681816,38.3424988 38.9374619,38.3742142 38.9064751,38.4056053) t=0 [25] (38.9681816,38.3424988) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=13 from=(38.9064751,38.4056053) to=(38.9681816,38.3424988)
-path.quadTo(38.9374619,38.3742142, 38.9681816,38.3424988);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=12 (39.0306854,38.2793732 38.9995995,38.3110695 38.9681816,38.3424988) t=0 [23] (39.0306854,38.2793732) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=12 from=(38.9681816,38.3424988) to=(39.0306854,38.2793732)
-path.quadTo(38.9995995,38.3110695, 39.0306854,38.2793732);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=11 (41.3187523,32.6000175 41.3509521,35.9135704 39.0306854,38.2793732) t=0 [21] (41.3187523,32.6000175) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=11 from=(39.0306854,38.2793732) to=(41.3187523,32.6000175)
-path.quadTo(41.3509521,35.9135704, 41.3187523,32.6000175);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=10 (38.920742,26.966198 41.2865486,29.2864628 41.3187523,32.6000175) t=0 [19] (38.920742,26.966198) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=10 from=(41.3187523,32.6000175) to=(38.920742,26.966198)
-path.quadTo(41.2865486,29.2864628, 38.920742,26.966198);
-path.close();
-</div>
-
-<div id="fuzz763_4713parts">
-seg=1 {{{-33.1326447f, -40.8928833f}, {-29.8189526f, -40.9036179f}, {-27.4682293f, -38.5680733f}}}
-seg=2 {{{-27.4682293f, -38.5680733f}, {-25.117506f, -36.2325325f}, {-25.1067715f, -32.9188423f}}}
-seg=3 {{{-25.1067715f, -32.9188423f}, {-25.0960369f, -29.6051483f}, {-27.4315796f, -27.254425f}}}
-seg=4 {{{-27.4315796f, -27.254425f}, {-29.7671204f, -24.9036999f}, {-33.0808144f, -24.8929653f}}}
-seg=5 {{{-33.0808144f, -24.8929653f}, {-36.3945045f, -24.8822308f}, {-38.7452278f, -27.2177753f}}}
-seg=6 {{{-38.7452278f, -27.2177753f}, {-41.0959549f, -29.5533161f}, {-41.1066895f, -32.867012f}}}
-seg=7 {{{-41.1066895f, -32.867012f}, {-41.117424f, -36.1807022f}, {-38.7818794f, -38.5314217f}}}
-seg=8 {{{-38.7818794f, -38.5314217f}, {-36.4463348f, -40.8821487f}, {-33.1326447f, -40.8928833f}}}
-op union
-seg=9 {{{41, 33}, {41, 36.3137093f}, {38.6568527f, 38.6568527f}}}
-seg=10 {{{38.6568527f, 38.6568527f}, {36.3137093f, 41}, {33, 41}}}
-seg=11 {{{33, 41}, {29.6862907f, 41}, {27.3431454f, 38.6568527f}}}
-seg=12 {{{27.3431454f, 38.6568527f}, {25, 36.3137093f}, {25, 33}}}
-seg=13 {{{25, 33}, {25, 29.6862907f}, {27.3431454f, 27.3431454f}}}
-seg=14 {{{27.3431454f, 27.3431454f}, {29.6862907f, 25}, {33, 25}}}
-seg=15 {{{33, 25}, {36.3137093f, 25}, {38.6568527f, 27.3431454f}}}
-seg=16 {{{38.6568527f, 27.3431454f}, {41, 29.6862907f}, {41, 33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-33.1326447,-40.8928833}, {-29.8189526,-40.9036179}, {-27.4682293,-38.5680733}}} {{-27.4682293,-38.5680733}} wnTs[0]=0 {{{-27.4682293,-38.5680733}, {-25.117506,-36.2325325}, {-25.1067715,-32.9188423}}}
-debugShowQuadIntersection wtTs[0]=0 {{{-33.1326447,-40.8928833}, {-29.8189526,-40.9036179}, {-27.4682293,-38.5680733}}} {{-33.1326447,-40.8928833}} wnTs[0]=1 {{{-38.7818794,-38.5314217}, {-36.4463348,-40.8821487}, {-33.1326447,-40.8928833}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-27.4682293,-38.5680733}, {-25.117506,-36.2325325}, {-25.1067715,-32.9188423}}} {{-25.1067715,-32.9188423}} wnTs[0]=0 {{{-25.1067715,-32.9188423}, {-25.0960369,-29.6051483}, {-27.4315796,-27.254425}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-25.1067715,-32.9188423}, {-25.0960369,-29.6051483}, {-27.4315796,-27.254425}}} {{-27.4315796,-27.254425}} wnTs[0]=0 {{{-27.4315796,-27.254425}, {-29.7671204,-24.9036999}, {-33.0808144,-24.8929653}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-27.4315796,-27.254425}, {-29.7671204,-24.9036999}, {-33.0808144,-24.8929653}}} {{-33.0808144,-24.8929653}} wnTs[0]=0 {{{-33.0808144,-24.8929653}, {-36.3945045,-24.8822308}, {-38.7452278,-27.2177753}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-33.0808144,-24.8929653}, {-36.3945045,-24.8822308}, {-38.7452278,-27.2177753}}} {{-38.7452278,-27.2177753}} wnTs[0]=0 {{{-38.7452278,-27.2177753}, {-41.0959549,-29.5533161}, {-41.1066895,-32.867012}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-38.7452278,-27.2177753}, {-41.0959549,-29.5533161}, {-41.1066895,-32.867012}}} {{-41.1066895,-32.867012}} wnTs[0]=0 {{{-41.1066895,-32.867012}, {-41.117424,-36.1807022}, {-38.7818794,-38.5314217}}}
-debugShowQuadIntersection wtTs[0]=1 {{{-41.1066895,-32.867012}, {-41.117424,-36.1807022}, {-38.7818794,-38.5314217}}} {{-38.7818794,-38.5314217}} wnTs[0]=0 {{{-38.7818794,-38.5314217}, {-36.4463348,-40.8821487}, {-33.1326447,-40.8928833}}}
-debugShowQuadIntersection wtTs[0]=1 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}} {{38.6568527,38.6568527}} wnTs[0]=0 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}}
-debugShowQuadIntersection wtTs[0]=0 {{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}} {{41,33}} wnTs[0]=1 {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}} {{33,41}} wnTs[0]=0 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}} {{27.3431454,38.6568527}} wnTs[0]=0 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}} {{25,33}} wnTs[0]=0 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}} {{27.3431454,27.3431454}} wnTs[0]=0 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}}
-debugShowQuadIntersection wtTs[0]=1 {{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}} {{33,25}} wnTs[0]=0 {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}}
-debugShowQuadIntersection wtTs[0]=1 {{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}} {{38.6568527,27.3431454}} wnTs[0]=0 {{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}}
-SkOpSegment::debugShowActiveSpans id=1 (-33.1326447,-40.8928833 -29.8189526,-40.9036179 -27.4682293,-38.5680733) t=0 (-33.1326447,-40.8928833) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=2 (-27.4682293,-38.5680733 -25.117506,-36.2325325 -25.1067715,-32.9188423) t=0 (-27.4682293,-38.5680733) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=3 (-25.1067715,-32.9188423 -25.0960369,-29.6051483 -27.4315796,-27.254425) t=0 (-25.1067715,-32.9188423) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=4 (-27.4315796,-27.254425 -29.7671204,-24.9036999 -33.0808144,-24.8929653) t=0 (-27.4315796,-27.254425) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=5 (-33.0808144,-24.8929653 -36.3945045,-24.8822308 -38.7452278,-27.2177753) t=0 (-33.0808144,-24.8929653) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=6 (-38.7452278,-27.2177753 -41.0959549,-29.5533161 -41.1066895,-32.867012) t=0 (-38.7452278,-27.2177753) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=7 (-41.1066895,-32.867012 -41.117424,-36.1807022 -38.7818794,-38.5314217) t=0 (-41.1066895,-32.867012) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=8 (-38.7818794,-38.5314217 -36.4463348,-40.8821487 -33.1326447,-40.8928833) t=0 (-38.7818794,-38.5314217) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=9 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 (41,33) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=10 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 (38.6568527,38.6568527) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=11 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=12 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=13 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 (25,33) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=14 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 (27.3431454,27.3431454) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=15 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 (33,25) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=16 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 (38.6568527,27.3431454) tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(-30.0596943,-40.3170471)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=1 seg=1 {{{-33.1326447f, -40.8928833f}, {-29.8189526f, -40.9036179f}, {-27.4682293f, -38.5680733f}}} t=0.5 pt=(-30.0596943,-40.3170471) slope=(2.83220768,1.16240501)
-SkOpSegment::markWinding id=1 (-33.1326447,-40.8928833 -29.8189526,-40.9036179 -27.4682293,-38.5680733) t=0 [1] (-33.1326447,-40.8928833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=2 (-27.4682293,-38.5680733 -25.117506,-36.2325325 -25.1067715,-32.9188423) t=0 [3] (-27.4682293,-38.5680733) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=3 (-25.1067715,-32.9188423 -25.0960369,-29.6051483 -27.4315796,-27.254425) t=0 [5] (-25.1067715,-32.9188423) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=4 (-27.4315796,-27.254425 -29.7671204,-24.9036999 -33.0808144,-24.8929653) t=0 [7] (-27.4315796,-27.254425) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=5 (-33.0808144,-24.8929653 -36.3945045,-24.8822308 -38.7452278,-27.2177753) t=0 [9] (-33.0808144,-24.8929653) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=6 (-38.7452278,-27.2177753 -41.0959549,-29.5533161 -41.1066895,-32.867012) t=0 [11] (-38.7452278,-27.2177753) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=7 (-41.1066895,-32.867012 -41.117424,-36.1807022 -38.7818794,-38.5314217) t=0 [13] (-41.1066895,-32.867012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=8 (-38.7818794,-38.5314217 -36.4463348,-40.8821487 -33.1326447,-40.8928833) t=0 [15] (-38.7818794,-38.5314217) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (-33.1326447,-40.8928833 -29.8189526,-40.9036179 -27.4682293,-38.5680733) t=0 [1] (-33.1326447,-40.8928833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::activeOp id=1 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=1 (-33.1326447,-40.8928833 -29.8189526,-40.9036179 -27.4682293,-38.5680733) t=0 [1] (-33.1326447,-40.8928833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=1 from=(-27.4682293,-38.5680733) to=(-33.1326447,-40.8928833)
-path.moveTo(-27.4682293,-38.5680733);
-path.quadTo(-29.8189526,-40.9036179, -33.1326447,-40.8928833);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=8 (-38.7818794,-38.5314217 -36.4463348,-40.8821487 -33.1326447,-40.8928833) t=0 [15] (-38.7818794,-38.5314217) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=8 from=(-33.1326447,-40.8928833) to=(-38.7818794,-38.5314217)
-path.quadTo(-36.4463348,-40.8821487, -38.7818794,-38.5314217);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=7 (-41.1066895,-32.867012 -41.117424,-36.1807022 -38.7818794,-38.5314217) t=0 [13] (-41.1066895,-32.867012) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=7 from=(-38.7818794,-38.5314217) to=(-41.1066895,-32.867012)
-path.quadTo(-41.117424,-36.1807022, -41.1066895,-32.867012);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=6 (-38.7452278,-27.2177753 -41.0959549,-29.5533161 -41.1066895,-32.867012) t=0 [11] (-38.7452278,-27.2177753) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=6 from=(-41.1066895,-32.867012) to=(-38.7452278,-27.2177753)
-path.quadTo(-41.0959549,-29.5533161, -38.7452278,-27.2177753);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=5 (-33.0808144,-24.8929653 -36.3945045,-24.8822308 -38.7452278,-27.2177753) t=0 [9] (-33.0808144,-24.8929653) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=5 from=(-38.7452278,-27.2177753) to=(-33.0808144,-24.8929653)
-path.quadTo(-36.3945045,-24.8822308, -33.0808144,-24.8929653);
+SkOpSegment::sortAngles [2] tStart=0 [3]
+SkOpSegment::sortAngles [2] tStart=0.142857143 [25]
+SkOpAngle::after [2/21] 21/21 tStart=0.142857143 tEnd=0 < [3/27] 5/5 tStart=0.498542274 tEnd=0 < [2/22] 5/5 tStart=0.142857143 tEnd=0.145241853 F 11
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {3.37755099,3.12244905}, {3.25,3.25}, {3.125,3.375}}} id=2
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {4,2}}} id=3
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {3.50352606,2.99499191}, {3.50559336,2.99289971}, {3.50765967,2.99080825}}} id=2
+SkOpAngle::after [2/21] 21/21 tStart=0.142857143 tEnd=0 < [3/28] 21/21 tStart=0.498542274 tEnd=0.498607541 < [2/22] 5/5 tStart=0.142857143 tEnd=0.145241853 T 12
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {3.37755099,3.12244905}, {3.25,3.25}, {3.125,3.375}}} id=2
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {3.50139236,2.99721503}}} id=3
+SkOpAngle::afterPart {{{3.50145769,2.99708462}, {3.50352606,2.99499191}, {3.50559336,2.99289971}, {3.50765967,2.99080825}}} id=2
+SkOpSegment::sortAngles [2] tStart=0.145241853 [20]
+SkOpSegment::sortAngles [2] tStart=0.715768455 [22]
+SkOpSegment::sortAngles [3] tStart=0.498542274 [26]
+SkOpSegment::sortAngles [3] tStart=0.498607541 [16]
+SkOpSegment::sortAngles [3] tStart=0.5 [24]
+SkOpSegment::debugShowActiveSpans id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (2.31394291,4.42326832 1,6) t=0.737211419 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=? windValue=1
+SkOpSpan::sortableTop dir=kLeft seg=4 t=0.5 pt=(1.921875,4.640625)
+SkOpSpan::sortableTop [0] valid=1 operand=1 span=7 ccw=1 seg=4 {{{1, 6}, {1.5f, 5}, {2.25f, 4.25f}, {3.125f, 3.375f}}} t=0.5 pt=(1.921875,4.640625) slope=(2.15625,-2.53125)
+SkOpSegment::markWinding id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 [7] (1,6) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 [7] (1,6) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0.737211419 [17] (2.31394291,4.42326832) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::activeOp id=4 t=1 tEnd=0 op=diff miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
+SkOpSegment::markDone id=4 (1,6 1.5,5 2.25,4.25 3.125,3.375) t=0 [7] (1,6) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=6 (6,0 1,6) t=0.737211419 [17] (2.31394291,4.42326832) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp chase.append id=6 windSum=-1
+SkOpSegment::markWinding id=1 (3,4 1.5,5 2.25,4.25 3.125,3.375) t=0 [1] (3,4) tEnd=0.220070773 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=3 (4,2 3,4) t=0.5 [24] (3.5,3) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=3 span=24 windSum=-1
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0.5 [23] (3.5,3) tEnd=0.737211419 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=6 span=23 windSum=-1
+SkOpSegment::markWinding id=1 (3,4 1.5,5 2.25,4.25 3.125,3.375) t=0.220070773 [18] (2.31394291,4.42326832) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=1 span=2
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50728869,2.99125361 3.5,3) t=0.498542274 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.5,3 2.31394291,4.42326832) t=0.5 tEnd=0.737211419 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=1 (3,4 2.66989384,4.22007077 2.44875776,4.35538685 2.31394291,4.42326832) t=0 tEnd=0.220070773 windSum=-1 oppSum=0 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=1 (2.31394291,4.42326832 1.83616005,4.66383975 2.44256193,4.05743807 3.125,3.375) t=0.220070773 tEnd=1 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50139236,2.99721503 3.5,3) t=0.498607541 tEnd=0.5 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.5,3 3,4) t=0.5 tEnd=1 windSum=-1 oppSum=0 windValue=1 oppValue=0
+SkOpSegment::activeOp id=1 t=0.220070773 tEnd=0 op=diff miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=4 (-27.4315796,-27.254425 -29.7671204,-24.9036999 -33.0808144,-24.8929653) t=0 [7] (-27.4315796,-27.254425) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=4 from=(-33.0808144,-24.8929653) to=(-27.4315796,-27.254425)
-path.quadTo(-29.7671204,-24.9036999, -27.4315796,-27.254425);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=3 (-25.1067715,-32.9188423 -25.0960369,-29.6051483 -27.4315796,-27.254425) t=0 [5] (-25.1067715,-32.9188423) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=3 from=(-27.4315796,-27.254425) to=(-25.1067715,-32.9188423)
-path.quadTo(-25.0960369,-29.6051483, -25.1067715,-32.9188423);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=2 (-27.4682293,-38.5680733 -25.117506,-36.2325325 -25.1067715,-32.9188423) t=0 [3] (-27.4682293,-38.5680733) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=2 from=(-25.1067715,-32.9188423) to=(-27.4682293,-38.5680733)
-path.quadTo(-25.117506,-36.2325325, -27.4682293,-38.5680733);
+SkOpSegment::markDone id=1 (3,4 1.5,5 2.25,4.25 3.125,3.375) t=0 [1] (3,4) tEnd=0.220070773 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=1 from=(2.31394291,4.42326832) to=(3,4)
+path.moveTo(2.31394291,4.42326832);
+path.cubicTo(2.44875765,4.35538673, 2.66989374,4.22007084, 3,4);
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0.498542274 [14] (3.50728869,2.99125361) tEnd=0.5 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=6 span=14 windSum=-1
+SkOpSegment::markWinding id=3 (4,2 3,4) t=0.498607541 [16] (3.50139236,2.99721503) tEnd=0.5 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=3 span=16 windSum=-1
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [3/32] next=6/13 sect=21/21 s=0.5 [24] e=1 [6] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [6/13] next=3/31 sect=5/5 s=0.5 [23] e=0.498542274 [14] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [3/31] next=6/14 sect=5/5 s=0.5 [24] e=0.498607541 [16] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [6/14] next=3/32 sect=21/21 s=0.5 [23] e=0.737211419 [17] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
+SkOpSegment::activeOp id=6 t=0.5 tEnd=0.498542274 op=diff miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
+SkOpSegment::markDone id=6 (6,0 1,6) t=0.498542274 [14] (3.50728869,2.99125361) tEnd=0.5 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append segment=6 span=14 windSum=-1
+SkOpSegment::activeOp id=3 t=0.5 tEnd=0.498607541 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=3 (4,2 3,4) t=0.498607541 [16] (3.50139236,2.99721503) tEnd=0.5 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append segment=3 span=16 windSum=-1
+SkOpSegment::activeOp id=6 t=0.5 tEnd=0.737211419 op=diff miFrom=1 miTo=1 suFrom=1 suTo=0 result=1
+SkOpSegment::markDone id=3 (4,2 3,4) t=0.5 [24] (3.5,3) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[3] to:[6] start=19488848 end=19488176
+bridgeOp current id=3 from=(3,4) to=(3.5,3)
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [6/15] next=1/18 sect=5/5 s=0.737211419 [17] e=0.5 [23] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
+SkOpAngle::dumpOne [1/18] next=6/16 sect=17/5 s=0.220070773 [18] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [6/16] next=1/17 sect=21/21 s=0.737211419 [17] e=1 [12] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done operand
+SkOpAngle::dumpOne [1/17] next=6/15 sect=1/1 s=0.220070773 [18] e=0 [1] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done
+SkOpSegment::activeOp id=1 t=0.220070773 tEnd=1 op=diff miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=1 (3,4 1.5,5 2.25,4.25 3.125,3.375) t=0.220070773 [18] (2.31394291,4.42326832) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=6 t=0.737211419 tEnd=1 op=diff miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
+SkOpSegment::activeOp id=1 t=0.220070773 tEnd=0 op=diff miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
+SkOpSegment::markDone id=6 (6,0 1,6) t=0.5 [23] (3.5,3) tEnd=0.737211419 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[6] to:[1] start=19488288 end=123925160
+bridgeOp current id=6 from=(3.5,3) to=(2.31394291,4.42326832)
+path.lineTo(3.5,3);
+path.lineTo(2.31394291,4.42326832);
path.close();
-SkOpSegment::debugShowActiveSpans id=9 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 (41,33) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=10 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 (38.6568527,38.6568527) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=11 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=12 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=13 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 (25,33) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=14 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 (27.3431454,27.3431454) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=15 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 (33,25) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=16 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 (38.6568527,27.3431454) tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kLeft seg=9 t=0.5 pt=(40.4142151,36.0710678)
-SkOpSpan::sortableTop [0] valid=1 operand=1 span=23 ccw=1 seg=12 {{{27.3431454f, 38.6568527f}, {25, 36.3137093f}, {25, 33}}} t=0.5 pt=(25.5857868,36.0710678) slope=(-1.17157269,-2.82842636)
-SkOpSpan::sortableTop [1] valid=1 operand=1 span=17 ccw=0 seg=9 {{{41, 33}, {41, 36.3137093f}, {38.6568527f, 38.6568527f}}} t=0.5 pt=(40.4142151,36.0710678) slope=(-1.17157364,2.82842636)
-SkOpSegment::markWinding id=12 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [23] (27.3431454,38.6568527) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=13 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 [25] (25,33) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=14 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [27] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=15 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [29] (33,25) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=16 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [31] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=9 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [17] (41,33) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=10 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [19] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=11 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 [21] (33,41) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=12 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [23] (27.3431454,38.6568527) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::activeOp id=9 t=1 tEnd=0 op=union miFrom=0 miTo=0 suFrom=0 suTo=1 result=1
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=9 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [17] (41,33) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=9 from=(38.6568527,38.6568527) to=(41,33)
-path.moveTo(38.6568527,38.6568527);
-path.quadTo(41,36.3137093, 41,33);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=16 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [31] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=16 from=(41,33) to=(38.6568527,27.3431454)
-path.quadTo(41,29.6862907, 38.6568527,27.3431454);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=15 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [29] (33,25) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=15 from=(38.6568527,27.3431454) to=(33,25)
-path.quadTo(36.3137093,25, 33,25);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=14 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [27] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=14 from=(33,25) to=(27.3431454,27.3431454)
-path.quadTo(29.6862907,25, 27.3431454,27.3431454);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=13 (25,33 25,29.6862907 27.3431454,27.3431454) t=0 [25] (25,33) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=13 from=(27.3431454,27.3431454) to=(25,33)
-path.quadTo(25,29.6862907, 25,33);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=12 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 [23] (27.3431454,38.6568527) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=12 from=(25,33) to=(27.3431454,38.6568527)
-path.quadTo(25,36.3137093, 27.3431454,38.6568527);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=11 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 [21] (33,41) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=11 from=(27.3431454,38.6568527) to=(33,41)
-path.quadTo(29.6862907,41, 33,41);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDone id=10 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [19] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=10 from=(33,41) to=(38.6568527,38.6568527)
-path.quadTo(36.3137093,41, 38.6568527,38.6568527);
+SkOpSegment::markWinding id=3 (4,2 3,4) t=0.498542274 [26] (3.50145769,2.99708462) tEnd=0.498607541 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=3 span=26 windSum=-1
+SkOpSegment::markWinding id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0.140692452 [15] (3.50139236,2.99721503) tEnd=0.142857143 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=5 span=13 windSum=?
+SkOpSegment::markWinding id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0 [9] (3.125,3.375) tEnd=0.140692452 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=5 span=9 windSum=-1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (3.50145769,2.99708462 3.50139236,2.99721503) t=0.498542274 tEnd=0.498607541 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=3 t=0.498607541 tEnd=0.498542274 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=3 (4,2 3,4) t=0.498542274 [26] (3.50145769,2.99708462) tEnd=0.498607541 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+bridgeOp chase.append id=3 windSum=-1
+SkOpSegment::markWinding id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.142857143 [25] (3.50145769,2.99708462) tEnd=0.145241853 newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=2 span=20 windSum=?
+SkOpSegment::markWinding id=3 (4,2 3,4) t=0 [5] (4,2) tEnd=0.498542274 newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.715768455 [22] (4.41676426,1.89988291) tEnd=1 newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=2 span=22 windSum=1
+SkOpSegment::markWinding id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0 [3] (3.125,3.375) tEnd=0.142857143 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=2 span=3 windSum=-1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.50145769,2.99708462 3.50352606,2.99499191 3.50559336,2.99289971 3.50765967,2.99080825) t=0.142857143 tEnd=0.145241853 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=2 t=0.142857143 tEnd=0.145241853 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.142857143 [25] (3.50145769,2.99708462) tEnd=0.145241853 newWindSum=1 newOppSum=-1 oppSum=-1 windSum=1 windValue=1 oppValue=0
+bridgeOp chase.append id=2 windSum=-2147483647
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0.498468047 [19] (3.50765967,2.99080825) tEnd=0.498542274 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=6 span=14 windSum=-1
+SkOpSegment::markWinding id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.145241853 [20] (3.50765967,2.99080825) tEnd=0.715768455 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=2 span=22 windSum=1
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0.316647149 [21] (4.41676426,1.89988291) tEnd=0.498468047 newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=6 span=21 windSum=-1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=-1 oppSum=1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (3.50765967,2.99080825 3.50728869,2.99125361) t=0.498468047 tEnd=0.498542274 windSum=-1 oppSum=0 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=1 oppSum=0 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=6 t=0.498468047 tEnd=0.498542274 op=diff miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
+SkOpSegment::markDone id=6 (6,0 1,6) t=0.498468047 [19] (3.50765967,2.99080825) tEnd=0.498542274 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50728869,2.99125361 4.28571435,2.2040816 5.14285714,1.28571429 6,0) t=0.142857143 tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (6,0 4.41676426,1.89988291) t=0 tEnd=0.316647149 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (4.41676426,1.89988291 3.50765967,2.99080825) t=0.316647149 tEnd=0.498468047 windSum=-1 oppSum=1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.50765967,2.99080825 4.00201137,2.49043886 4.43658858,2.02620596 4.41676426,1.89988291) t=0.145241853 tEnd=0.715768455 windSum=1 oppSum=0 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (4.41676426,1.89988291 4.40688795,1.83694983 4.28423154,1.85788423 4,2) t=0.715768455 tEnd=1 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=3 (4,2 3.50145769,2.99708462) t=0 tEnd=0.498542274 windSum=1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=6 t=0.498468047 tEnd=0.316647149 op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
+SkOpSegment::markWinding id=6 (6,0 1,6) t=0 [11] (6,0) tEnd=0.316647149 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0.142857143 [13] (3.50728869,2.99125361) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=5 span=13 windSum=-1
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [6/8] next=2/25 sect=21/21 s=0.316647149 [21] e=0.498468047 [19] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=1 operand
+SkOpAngle::dumpOne [2/25] next=6/7 sect=25/21 s=0.715768455 [22] e=0.145241853 [20] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [6/7] next=2/26 sect=5/5 s=0.316647149 [21] e=0 [11] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [2/26] next=6/8 sect=9/17 s=0.715768455 [22] e=1 [4] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=-1
+SkOpSegment::activeOp id=2 t=0.715768455 tEnd=0.145241853 op=diff miFrom=1 miTo=0 suFrom=0 suTo=0 result=1
+SkOpSegment::activeOp id=6 t=0.316647149 tEnd=0 op=diff miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
+SkOpSegment::markDone id=6 (6,0 1,6) t=0 [11] (6,0) tEnd=0.316647149 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0.142857143 [13] (3.50728869,2.99125361) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append segment=5 span=13 windSum=-1
+SkOpSegment::activeOp id=2 t=0.715768455 tEnd=1 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.715768455 [22] (4.41676426,1.89988291) tEnd=1 newWindSum=1 newOppSum=-1 oppSum=-1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markDone id=3 (4,2 3,4) t=0 [5] (4,2) tEnd=0.498542274 newWindSum=1 newOppSum=-1 oppSum=-1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::markDone id=6 (6,0 1,6) t=0.316647149 [21] (4.41676426,1.89988291) tEnd=0.498468047 newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[6] to:[2] start=19488736 end=19488512
+bridgeOp current id=6 from=(3.50765967,2.99080825) to=(4.41676426,1.89988291)
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [2/24] next=6/9 sect=5/5 s=0.145241853 [20] e=0.715768455 [22] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [6/9] next=2/23 sect=5/5 s=0.498468047 [19] e=0.316647149 [21] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=1 done operand
+SkOpAngle::dumpOne [2/23] next=6/10 sect=21/21 s=0.145241853 [20] e=0.142857143 [25] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=-1 done
+SkOpAngle::dumpOne [6/10] next=2/24 sect=21/21 s=0.498468047 [19] e=0.498542274 [14] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done operand
+SkOpSegment::activeOp id=6 t=0.498468047 tEnd=0.316647149 op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
+SkOpSegment::activeOp id=2 t=0.145241853 tEnd=0.142857143 op=diff miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::activeOp id=6 t=0.498468047 tEnd=0.498542274 op=diff miFrom=0 miTo=0 suFrom=1 suTo=0 result=0
+SkOpSegment::markDone id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0.145241853 [20] (3.50765967,2.99080825) tEnd=0.715768455 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[2] to:[6] start=19488400 end=19488624
+bridgeOp current id=2 from=(4.41676426,1.89988291) to=(3.50765967,2.99080825)
+path.moveTo(3.50765967,2.99080825);
+path.lineTo(4.41676426,1.89988291);
+path.cubicTo(4.43658876,2.02620602, 4.0020113,2.49043894, 3.50765967,2.99080825);
path.close();
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.50139236,2.99721503 3.50335725,2.99522872 3.5053228,2.9932416 3.50728869,2.99125361) t=0.140692452 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=5 t=0.142857143 tEnd=0.140692452 op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
+SkOpSegment::markDone id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0.140692452 [15] (3.50139236,2.99721503) tEnd=0.142857143 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (3.125,3.375 3.25,3.25 3.37755099,3.12244905 3.50145769,2.99708462) t=0 tEnd=0.142857143 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=2 t=0.142857143 tEnd=0 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=2 (3.125,3.375 4,2.5 5,1.5 4,2) t=0 [3] (3.125,3.375) tEnd=0.142857143 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+bridgeOp chase.append id=2 windSum=-1
+SkOpSegment::debugShowActiveSpans id=5 (3.125,3.375 3.2481059,3.2518941 3.37368599,3.12631386 3.50139236,2.99721503) t=0 tEnd=0.140692452 windSum=-1 oppSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=5 t=0.140692452 tEnd=0 op=diff miFrom=1 miTo=1 suFrom=0 suTo=1 result=1
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [5/2] next=2/20 sect=4/5 s=0 [9] e=0.140692452 [15] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
+SkOpAngle::dumpOne [2/20] next=4/1 sect=4/5 s=0 [3] e=0.142857143 [25] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done
+SkOpAngle::dumpOne [4/1] next=1/19 sect=20/21 s=1 [8] e=0 [7] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done operand
+SkOpAngle::dumpOne [1/19] next=5/2 sect=20/21 s=1 [2] e=0.220070773 [18] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done
+SkOpSegment::activeOp id=2 t=0 tEnd=0.142857143 op=diff miFrom=1 miTo=0 suFrom=0 suTo=0 result=1
+SkOpSegment::activeOp id=4 t=1 tEnd=0 op=diff miFrom=0 miTo=0 suFrom=0 suTo=1 result=0
+SkOpSegment::activeOp id=1 t=1 tEnd=0.220070773 op=diff miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=5 (3.125,3.375 4,2.5 5,1.5 6,0) t=0 [9] (3.125,3.375) tEnd=0.140692452 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[5] to:[2] start=19486400 end=19489072
+bridgeOp current id=5 from=(3.50139236,2.99721503) to=(3.125,3.375)
+path.moveTo(3.50139236,2.99721503);
+path.cubicTo(3.37368608,3.12631392, 3.248106,3.251894, 3.125,3.375);
</div>
</div>
<script type="text/javascript">
-var testDivs = [
- fuzz763_4713_b,
- fuzz763_4713parts,
+ var testDivs = [
+ cubics_d3,
];
var decimal_places = 3; // make this 3 to show more precision
@@ -967,11 +787,9 @@ var SPAN_Y1 = SPAN_X1 + 1;
var SPAN_X2 = SPAN_Y1 + 1;
var SPAN_Y2 = SPAN_X2 + 1;
-var SPAN_L_T = SPAN_Y2 + 1;
-var SPAN_L_TX = SPAN_L_T + 1;
+var SPAN_L_TX = SPAN_Y2 + 1;
var SPAN_L_TY = SPAN_L_TX + 1;
-var SPAN_L_TEND = SPAN_L_TY + 1;
-var SPAN_L_OTHER = SPAN_L_TEND + 1;
+var SPAN_L_OTHER = SPAN_L_TY + 1;
var SPAN_L_OTHERT = SPAN_L_OTHER + 1;
var SPAN_L_OTHERI = SPAN_L_OTHERT + 1;
var SPAN_L_SUM = SPAN_L_OTHERI + 1;
@@ -981,11 +799,9 @@ var SPAN_L_OPP = SPAN_L_VAL + 1;
var SPAN_X3 = SPAN_Y2 + 1;
var SPAN_Y3 = SPAN_X3 + 1;
-var SPAN_Q_T = SPAN_Y3 + 1;
-var SPAN_Q_TX = SPAN_Q_T + 1;
+var SPAN_Q_TX = SPAN_Y3 + 1;
var SPAN_Q_TY = SPAN_Q_TX + 1;
-var SPAN_Q_TEND = SPAN_Q_TY + 1;
-var SPAN_Q_OTHER = SPAN_Q_TEND + 1;
+var SPAN_Q_OTHER = SPAN_Q_TY + 1;
var SPAN_Q_OTHERT = SPAN_Q_OTHER + 1;
var SPAN_Q_OTHERI = SPAN_Q_OTHERT + 1;
var SPAN_Q_SUM = SPAN_Q_OTHERI + 1;
@@ -993,11 +809,9 @@ var SPAN_Q_VAL = SPAN_Q_SUM + 1;
var SPAN_Q_OPP = SPAN_Q_VAL + 1;
var SPAN_K_W = SPAN_Y3 + 1;
-var SPAN_K_T = SPAN_K_W + 1;
-var SPAN_K_TX = SPAN_K_T + 1;
+var SPAN_K_TX = SPAN_K_W + 1;
var SPAN_K_TY = SPAN_K_TX + 1;
-var SPAN_K_TEND = SPAN_K_TY + 1;
-var SPAN_K_OTHER = SPAN_K_TEND + 1;
+var SPAN_K_OTHER = SPAN_K_TY + 1;
var SPAN_K_OTHERT = SPAN_K_OTHER + 1;
var SPAN_K_OTHERI = SPAN_K_OTHERT + 1;
var SPAN_K_SUM = SPAN_K_OTHERI + 1;
@@ -1007,11 +821,9 @@ var SPAN_K_OPP = SPAN_K_VAL + 1;
var SPAN_X4 = SPAN_Y3 + 1;
var SPAN_Y4 = SPAN_X4 + 1;
-var SPAN_C_T = SPAN_Y4 + 1;
-var SPAN_C_TX = SPAN_C_T + 1;
+var SPAN_C_TX = SPAN_Y4 + 1;
var SPAN_C_TY = SPAN_C_TX + 1;
-var SPAN_C_TEND = SPAN_C_TY + 1;
-var SPAN_C_OTHER = SPAN_C_TEND + 1;
+var SPAN_C_OTHER = SPAN_C_TY + 1;
var SPAN_C_OTHERT = SPAN_C_OTHER + 1;
var SPAN_C_OTHERI = SPAN_C_OTHERT + 1;
var SPAN_C_SUM = SPAN_C_OTHERI + 1;
@@ -1048,7 +860,10 @@ var INTERSECT_QUAD_NO = INTERSECT_QUAD_2 + 1;
var INTERSECT_CONIC_LINE = INTERSECT_QUAD_NO + 1;
var INTERSECT_CONIC_LINE_2 = INTERSECT_CONIC_LINE + 1;
var INTERSECT_CONIC_LINE_NO = INTERSECT_CONIC_LINE_2 + 1;
-var INTERSECT_CONIC = INTERSECT_CONIC_LINE_NO + 1;
+var INTERSECT_CONIC_QUAD = INTERSECT_CONIC_LINE_NO + 1;
+var INTERSECT_CONIC_QUAD_2 = INTERSECT_CONIC_QUAD + 1;
+var INTERSECT_CONIC_QUAD_NO = INTERSECT_CONIC_QUAD_2 + 1;
+var INTERSECT_CONIC = INTERSECT_CONIC_QUAD_NO + 1;
var INTERSECT_CONIC_2 = INTERSECT_CONIC + 1;
var INTERSECT_CONIC_NO = INTERSECT_CONIC_2 + 1;
var INTERSECT_SELF_CUBIC = INTERSECT_CONIC_NO + 1;
@@ -1242,7 +1057,7 @@ function parse_all(test) {
var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE
: line.lastIndexOf("debugShowCoincidence", 0) === 0 ? REC_TYPE_COINCIDENCE
: line.lastIndexOf("((SkOpSegment*)", 0) === 0 ? REC_TYPE_PATH2
- : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN
+ : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN
: line.lastIndexOf("afterPart", 0) === 0 ? REC_TYPE_AFTERPART
: line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
: line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
@@ -1275,21 +1090,21 @@ function parse_all(test) {
switch (recType) {
case REC_TYPE_ACTIVE:
found = match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
-" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+" id=IDX LINE_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
-" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+" id=IDX QUAD_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CONIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+" id=IDX CONIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+" id=IDX CUBIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
) || match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
-" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX LINE_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
-" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX QUAD_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CONIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX CONIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX CUBIC_VAL t=T_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
);
break;
case REC_TYPE_ACTIVE_OP:
@@ -1401,12 +1216,21 @@ function parse_all(test) {
" wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
) || match_regexp(line, lineNo, record, INTERSECT_QUAD_NO, "debugShowQuadIntersection" +
" no intersect QUAD_VAL QUAD_VAL"
+
) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE, "debugShowConicLineIntersection" +
" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_2, "debugShowConicLineIntersection" +
" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_NO, "debugShowConicLineIntersection" +
" no intersect CONIC_VAL LINE_VAL"
+
+ ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD, "debugShowConicQuadIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL"
+ ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_2, "debugShowConicQuadIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
+ ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_QUAD_NO, "debugShowConicQuadIntersection" +
+" no intersect CONIC_VAL QUAD_VAL"
+
) || match_regexp(line, lineNo, record, INTERSECT_CONIC, "debugShowConicIntersection" +
" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL CONIC_VAL"
) || match_regexp(line, lineNo, record, INTERSECT_CONIC_2, "debugShowConicIntersection" +
@@ -1510,7 +1334,12 @@ function parse_all(test) {
) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
" last segment=IDX span=IDX"
) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
-" last segment=IDX span=IDX windSum=OPT");
+" last seg=IDX span=IDX"
+ ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
+" last segment=IDX span=IDX windSum=OPT"
+ ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
+" last seg=IDX span=IDX windSum=OPT"
+ );
break;
case REC_TYPE_OP:
if (line.lastIndexOf("oppSign oppSign=", 0) === 0
@@ -1608,7 +1437,7 @@ function init(test) {
last = 9;
break;
default:
- console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH"
+ console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH"
: "REC_TYPE_COMPUTED") + " frag type:" + fragType);
throw "stop execution";
}
@@ -1630,7 +1459,7 @@ function init(test) {
last = 9;
break;
default:
- console.log("unknown " + (recType == REC_TYPE_PATH2 ? "REC_TYPE_PATH2"
+ console.log("unknown " + (recType == REC_TYPE_PATH2 ? "REC_TYPE_PATH2"
: "REC_TYPE_COMPUTED") + " frag type:" + fragType);
throw "stop execution";
}
@@ -1763,6 +1592,15 @@ function init(test) {
case INTERSECT_CUBIC_LINE_NO:
first = 0; last = 8; first2 = 8; last2 = 12;
break;
+ case INTERSECT_CONIC_QUAD:
+ first = 1; last = 7; first2 = 11; last2 = 17;
+ break;
+ case INTERSECT_CONIC_QUAD_2:
+ first = 1; last = 7; first2 = 14; last2 = 20;
+ break;
+ case INTERSECT_CONIC_QUAD_NO:
+ first = 0; last = 6; first2 = 7; last2 = 13;
+ break;
case INTERSECT_CUBIC_QUAD:
first = 1; last = 9; first2 = 12; last2 = 18;
break;
@@ -2243,7 +2081,7 @@ function pointAtT(curve, curveType, t) {
}
return xy;
}
-
+
function drawPointAtT(curve, curveType) {
var x, y;
var xy = pointAtT(curve, curveType, curveT);
@@ -2433,8 +2271,8 @@ function alreadyDrawnConic(x1, y1, x2, y2, x3, y3, w) {
}
for (var pts = 0; pts < drawnConics.length; pts += 8) {
if (x1 == drawnConics[pts] && y1 == drawnCubics[pts + 1]
- && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
- && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
+ && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
+ && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
&& w == drawnCubics[pts + 6]) {
return true;
}
@@ -2572,7 +2410,7 @@ function conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
array[6] = partW;
return array;
}
-
+
function drawConicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
var a = conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2);
var ax = a[0];
@@ -2597,8 +2435,8 @@ function alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
}
for (var pts = 0; pts < drawnCubics.length; pts += 8) {
if (x1 == drawnCubics[pts] && y1 == drawnCubics[pts + 1]
- && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
- && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
+ && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3]
+ && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5]
&& x4 == drawnCubics[pts + 6] && y4 == drawnCubics[pts + 7]) {
return true;
}
@@ -2663,7 +2501,7 @@ function cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
];
return array;
}
-
+
function drawCubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
var a = cubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
var ax = a[0];
@@ -3023,21 +2861,37 @@ function drawLinePartialID(id, x1, y1, x2, y2, t1, t2) {
drawCurvePartialID(id, curve, t1, t2);
}
+function drawLineID(id, x1, y1, x2, y2) {
+ drawLinePartialID(id, x1, y1, x2, y2, 0, 1);
+}
+
function drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, t1, t2) {
var curve = [x1, y1, x2, y2, x3, y3];
drawCurvePartialID(id, curve, t1, t2);
}
+function drawQuadID(id, x1, y1, x2, y2, x3, y3) {
+ drawQuadPartialID(id, x1, y1, x2, y2, x3, y3, 0, 1);
+}
+
function drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, t1, t2) {
var curve = [x1, y1, x2, y2, x3, y3, w];
drawCurvePartialID(id, curve, t1, t2);
}
+function drawConicID(id, x1, y1, x2, y2, x3, y3, w) {
+ drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, 0, 1);
+}
+
function drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
drawCurvePartialID(id, curve, t1, t2);
}
+function drawCubicID(id, x1, y1, x2, y2, x3, y3, x4, y4) {
+ drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, 0, 1);
+}
+
function drawCurvePartialID(id, curve, t1, t2) {
var px = x_at_t(curve, (t1 + t2) / 2);
var py = y_at_t(curve, (t1 + t2) / 2);
@@ -3273,6 +3127,7 @@ function draw(test, lines, title) {
case INTERSECT_QUAD_LINE:
case INTERSECT_QUAD:
case INTERSECT_CONIC_LINE:
+ case INTERSECT_CONIC_QUAD:
case INTERSECT_CONIC:
case INTERSECT_SELF_CUBIC:
case INTERSECT_CUBIC_LINE:
@@ -3284,6 +3139,7 @@ function draw(test, lines, title) {
case INTERSECT_QUAD_LINE_2:
case INTERSECT_QUAD_2:
case INTERSECT_CONIC_LINE_2:
+ case INTERSECT_CONIC_QUAD_2:
case INTERSECT_CONIC_2:
case INTERSECT_CUBIC_LINE_2:
case INTERSECT_CUBIC_QUAD_2:
@@ -3294,6 +3150,7 @@ function draw(test, lines, title) {
case INTERSECT_QUAD_LINE_NO:
case INTERSECT_QUAD_NO:
case INTERSECT_CONIC_LINE_NO:
+ case INTERSECT_CONIC_QUAD_NO:
case INTERSECT_CONIC_NO:
case INTERSECT_SELF_CUBIC_NO:
case INTERSECT_CUBIC_LINE_NO:
@@ -3457,7 +3314,7 @@ function draw(test, lines, title) {
ctx.lineWidth = 1;
ctx.strokeStyle = firstPath ? "black" : "red";
ctx.fillStyle = "blue";
- var frags2 = [];
+ var frags2 = [];
switch (fragType) {
case PATH_LINE:
for (var i = 0; i < 4; ++ i) { frags2[i] = frags[i + 1]; }
@@ -3506,38 +3363,47 @@ function draw(test, lines, title) {
var y1 = frags[SPAN_Y1];
var x2 = frags[SPAN_X2];
var y2 = frags[SPAN_Y2];
- var x3, y3, x3, y4, t1, t2, w;
+ var x3, y3, x3, y4, w;
ctx.lineWidth = 3;
ctx.strokeStyle = "rgba(0,0,255, 0.3)";
focus_enabled = true;
switch (fragType) {
case ACTIVE_LINE_SPAN:
- t1 = frags[SPAN_L_T];
- t2 = frags[SPAN_L_TEND];
- drawLinePartial(x1, y1, x2, y2, t1, t2);
+ drawLine(x1, y1, x2, y2);
if (draw_id) {
- drawLinePartialID(frags[0], x1, y1, x2, y2, t1, t2);
+ drawLineID(frags[0], x1, y1, x2, y2);
+ }
+ if (pt_labels) {
+ var curve = [x1, y1, x2, y2];
+ ctx.fillStyle = "blue";
+ drawPoints(curve, PATH_LINE, pt_labels == 2);
}
break;
case ACTIVE_QUAD_SPAN:
x3 = frags[SPAN_X3];
y3 = frags[SPAN_Y3];
- t1 = frags[SPAN_Q_T];
- t2 = frags[SPAN_Q_TEND];
- drawQuadPartial(x1, y1, x2, y2, x3, y3, t1, t2);
+ drawQuad(x1, y1, x2, y2, x3, y3);
if (draw_id) {
- drawQuadPartialID(frags[0], x1, y1, x2, y2, x3, y3, t1, t2);
+ drawQuadID(frags[0], x1, y1, x2, y2, x3, y3);
+ }
+ if (pt_labels) {
+ var curve = [x1, y1, x2, y2, x3, y3];
+ ctx.fillStyle = "blue";
+ drawPoints(curve, PATH_QUAD, pt_labels == 2);
}
break;
case ACTIVE_CONIC_SPAN:
x3 = frags[SPAN_X3];
y3 = frags[SPAN_Y3];
- t1 = frags[SPAN_K_T];
- t2 = frags[SPAN_K_TEND];
w = frags[SPAN_K_W];
- drawConicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2);
+ drawConicWithQuads(x1, y1, x2, y2, x3, y3, w);
if (draw_id) {
- drawConicPartialID(frags[0], x1, y1, x2, y2, x3, y3, w, t1, t2);
+ drawConicID(frags[0], x1, y1, x2, y2, x3, y3, w);
+ }
+ if (pt_labels) {
+ var curve = [x1, y1, x2, y2, x3, y3, w];
+ ctx.fillStyle = "blue";
+ drawPoints(curve, PATH_CONIC, pt_labels == 2);
}
break;
case ACTIVE_CUBIC_SPAN:
@@ -3545,11 +3411,14 @@ function draw(test, lines, title) {
y3 = frags[SPAN_Y3];
x4 = frags[SPAN_X4];
y4 = frags[SPAN_Y4];
- t1 = frags[SPAN_C_T];
- t2 = frags[SPAN_C_TEND];
- drawCubicPartial(x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
+ drawCubic(x1, y1, x2, y2, x3, y3, x4, y4);
if (draw_id) {
- drawCubicPartialID(frags[0], x1, y1, x2, y2, x3, y3, x4, y4, t1, t2);
+ drawCubicID(frags[0], x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+ if (pt_labels) {
+ var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
+ ctx.fillStyle = "blue";
+ drawPoints(curve, PATH_CUBIC, pt_labels == 2);
}
break;
default:
@@ -3746,6 +3615,18 @@ function draw(test, lines, title) {
case INTERSECT_CONIC_LINE_NO:
c1s = 0; c1l = 7; c2s = 7; c2l = 4;
break;
+ case INTERSECT_CONIC_QUAD:
+ f.push(8, 9, 0, 10);
+ c1s = 1; c1l = 7; c2s = 11; c2l = 6;
+ break;
+ case INTERSECT_CONIC_QUAD_2:
+ f.push(8, 9, 0, 12);
+ f.push(11, 12, 10, 18);
+ c1s = 1; c1l = 7; c2s = 14; c2l = 6;
+ break;
+ case INTERSECT_CONIC_QUAD_NO:
+ c1s = 0; c1l = 7; c2s = 7; c2l = 6;
+ break;
case INTERSECT_CONIC:
f.push(8, 9, 0, 10);
c1s = 1; c1l = 7; c2s = 11; c2l = 7;
@@ -3841,7 +3722,7 @@ function draw(test, lines, title) {
var id = -1;
var curve;
switch (c1l) {
- case 4:
+ case 4:
drawLine(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]);
if (draw_id) {
curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3]];
@@ -3883,7 +3764,7 @@ function draw(test, lines, title) {
switch (c2l) {
case 0:
break;
- case 4:
+ case 4:
drawLine(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]);
if (draw_id) {
curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3]];
@@ -4143,7 +4024,7 @@ function draw(test, lines, title) {
drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_sort ? sortCount : sortMax, draw_sort, sortKey);
drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_top ? topCount : topMax, draw_top, topKey);
drawBox(pos++, "rgba(127,0,127, 0.3)", "black", draw_mark ? markCount : markMax, draw_mark, markKey);
- drawBox(pos++, "black", "white",
+ drawBox(pos++, "black", "white",
(new Array('P', 'P1', 'P2', 'P', 'p', 'p1', 'p2'))[draw_path], draw_path != 0, pathKey);
drawBox(pos++, "rgba(0,63,0, 0.7)", "white",
(new Array('Q', 'Q', 'C', 'QC', 'Qc', 'Cq'))[draw_computed],
@@ -4220,7 +4101,7 @@ function drawCurveTControl() {
function ptInTControl() {
var e = window.event;
- var tgt = e.target || e.srcElement;
+ var tgt = e.target || e.srcElement;
var left = tgt.offsetLeft;
var top = tgt.offsetTop;
var x = (e.clientX - left);
@@ -4275,7 +4156,7 @@ function dumpCurvePartial(test, id, t0, t1) {
function dumpAngleTest(test, id, t0, t1) {
var curve = curveByID(test, id);
- console.log(" { {" + curveToString(curve) + "}, "
+ console.log(" { {" + curveToString(curve) + "}, "
+ curve.length / 2 + ", " + t0 + ", " + t1 + ", {} }, //");
}
@@ -4372,11 +4253,11 @@ function doKeyPress(evt) {
break;
case activeKey:
draw_active ^= true;
- redraw();
+ redraw();
break;
case addKey:
draw_add ^= true;
- redraw();
+ redraw();
break;
case angleKey:
draw_angle = (draw_angle + 1) % 4;
@@ -4388,7 +4269,7 @@ function doKeyPress(evt) {
break;
case centerKey:
setScale(xmin, xmax, ymin, ymax);
- redraw();
+ redraw();
break;
case coincidenceKey:
draw_coincidence ^= true;
@@ -4396,19 +4277,19 @@ function doKeyPress(evt) {
break;
case controlLinesBackKey:
control_lines = (control_lines + 3) % 4;
- redraw();
+ redraw();
break;
case controlLinesKey:
control_lines = (control_lines + 1) % 4;
- redraw();
+ redraw();
break;
case computedBackKey:
draw_computed = (draw_computed + 5) % 6;
- redraw();
+ redraw();
break;
case computedKey:
draw_computed = (draw_computed + 1) % 6;
- redraw();
+ redraw();
break;
case curveTKey:
curve_t ^= true;
@@ -4432,11 +4313,11 @@ function doKeyPress(evt) {
break;
case intersectionBackKey:
draw_intersection = (draw_intersection + 3) % 4;
- redraw();
+ redraw();
break;
case intersectionKey:
draw_intersection = (draw_intersection + 1) % 4;
- redraw();
+ redraw();
break;
case intersectTKey:
draw_intersectT ^= true;
@@ -4472,11 +4353,11 @@ function doKeyPress(evt) {
break;
case pathKey:
draw_path = (draw_path + 1) % (4 + (hasAlignedPath ? 3 : 0));
- redraw();
+ redraw();
break;
case pathBackKey:
draw_path = (draw_path + 3 + (hasAlignedPath ? 3 : 0)) % (4 + (hasAlignedPath ? 3 : 0));
- redraw();
+ redraw();
break;
case ptsKey:
pt_labels = (pt_labels + 1) % 3;
@@ -4602,26 +4483,26 @@ function doKeyDown(evt) {
document.onfocusin = document.onfocusout = onchange;
// All others:
else
- window.onpageshow = window.onpagehide
+ window.onpageshow = window.onpagehide
= window.onfocus = window.onblur = onchange;
function onchange (evt) {
var v = 'visible', h = 'hidden',
- evtMap = {
- focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
+ evtMap = {
+ focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
};
evt = evt || window.event;
if (evt.type in evtMap)
document.body.className = evtMap[evt.type];
- else
+ else
document.body.className = this[hidden] ? "hidden" : "visible";
}
})();
function calcXY() {
var e = window.event;
- var tgt = e.target || e.srcElement;
+ var tgt = e.target || e.srcElement;
var left = tgt.offsetLeft;
var top = tgt.offsetTop;
mouseX = (e.clientX - left) / scale + srcLeft;