aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2015-07-06 11:38:33 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-07-06 11:38:33 -0700
commit27c8eb8ffd7e221693d840c2b9279d53fe6f03d4 (patch)
tree67fbbc378aced5c5de9ec9611021db6ef7659c3c
parent334e588d9ed5390efb82beb37329b56a380f1d0e (diff)
When three or more edges are coincident, the logic needs
to compute the overlapping ranges and combine the winding into a single destination. This computes coincidence more rigorously, fixing the edge cases exposed by this bug. Also, add the ability to debug and dump pathop structures from the coincident context. TBR=reed@google.com BUG=skia:3651 Review URL: https://codereview.chromium.org/1182493015
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp277
-rw-r--r--src/pathops/SkOpCoincidence.h54
-rw-r--r--src/pathops/SkOpContour.h23
-rw-r--r--src/pathops/SkOpSegment.cpp154
-rw-r--r--src/pathops/SkOpSegment.h13
-rwxr-xr-xsrc/pathops/SkOpSpan.cpp54
-rw-r--r--src/pathops/SkOpSpan.h29
-rw-r--r--src/pathops/SkPathOpsCommon.cpp42
-rw-r--r--src/pathops/SkPathOpsDebug.cpp12
-rw-r--r--src/pathops/SkPathOpsDebug.h12
-rw-r--r--src/pathops/SkPathOpsRect.h3
-rw-r--r--src/pathops/SkPathOpsTypes.cpp19
-rw-r--r--src/pathops/SkPathOpsTypes.h15
-rw-r--r--src/ports/SkDebug_win.cpp2
-rw-r--r--tests/PathOpsBattles.cpp2
-rwxr-xr-xtests/PathOpsDebug.cpp31
-rw-r--r--tests/PathOpsIssue3651.cpp117
-rw-r--r--tests/PathOpsSimplifyQuadThreadedTest.cpp2
-rw-r--r--tests/PathOpsSimplifyTest.cpp14
-rw-r--r--tools/pathops_sorter.htm43
-rw-r--r--tools/pathops_visualizer.htm272
21 files changed, 1008 insertions, 182 deletions
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index eb0ccc1737..58ff7f3ab9 100755
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -65,6 +65,90 @@ static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, d
*coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
}
+void 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
+ SkCoincidentSpans* coin = this->fHead;
+ SkASSERT(coin);
+ do {
+ SkOpPtT* startPtT = coin->fCoinPtTStart;
+ SkOpPtT* oStartPtT = coin->fOppPtTStart;
+ 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();
+ SkOpSpanBase* test = start->upCast()->next();
+ SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
+ while (test != end || oTest != oEnd) {
+ if (!test->ptT()->contains(oTest->ptT())) {
+ // use t ranges to guess which one is missing
+ double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
+ double startPart = (test->t() - startPtT->fT) / startRange;
+ double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
+ double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
+ SkASSERT(startPart != oStartPart);
+ SkOpPtT* newPt;
+ if (startPart < oStartPart) {
+ double newT = oStartPtT->fT + oStartRange * startPart;
+ newPt = oStart->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
+ newPt->fPt = test->pt();
+ test->ptT()->addOpp(newPt);
+ } else {
+ double newT = startPtT->fT + startRange * oStartPart;
+ newPt = start->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
+ newPt->fPt = oTest->pt();
+ oTest->ptT()->addOpp(newPt);
+ }
+ // start over
+ test = start;
+ oTest = oStart;
+ }
+ if (test != end) {
+ test = test->upCast()->next();
+ }
+ if (oStart != oEnd) {
+ oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
+ }
+ }
+ } while ((coin = coin->fNext));
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+}
+
+void SkOpCoincidence::addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s,
+ SkOpPtT* over1e, SkChunkAlloc* allocator) {
+ SkCoincidentSpans* check = this->fTop;
+ 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;
+ }
+ 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;
+ }
+ } 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
+}
+
bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
@@ -75,7 +159,7 @@ bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
SkOpSegment* coinSeg = coinPtTStart->segment();
SkOpSegment* oppSeg = oppPtTStart->segment();
SkASSERT(coinSeg != oppSeg);
- SkCoincidentSpans* check = this->fHead;
+ SkCoincidentSpans* check = this->fTop;
do {
const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
@@ -124,52 +208,136 @@ bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
return true;
}
+/* 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) {
- SkCoincidentSpans* outer = this->fHead;
+ SkCoincidentSpans* outer = fHead;
if (!outer) {
return true;
}
+ bool result;
+ fTop = outer;
+ fHead = NULL;
do {
+ // addifmissing can modify the list that this is walking
+ // maybe save head so that walker can iterate over old data unperturbed
+ // and addifmissing can add 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());
SkCoincidentSpans* inner = outer;
while ((inner = inner->fNext)) {
double overS, overE;
- if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+ 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->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
- return false;
+ result = false;
+ goto returnResult;
}
- } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+ } else if (outerCoin == innerOpp
+ && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
if (!this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
- return false;
+ result = false;
+ goto returnResult;
}
- } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
+ } else if (outerOpp == innerCoin
+ && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
- return false;
+ result = false;
+ goto returnResult;
}
- } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
+ } else if (outerOpp == innerOpp
+ && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
outer->fCoinPtTStart, outer->fCoinPtTEnd,
inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
- return false;
+ result = false;
+ goto returnResult;
+ }
+ } 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)) {
+ 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)) {
+ this->addIfMissing(inner, testS, testE, allocator);
+ }
}
}
}
-
} while ((outer = outer->fNext));
- return true;
+ result = true;
+returnResult:
+ SkCoincidentSpans** headPtr = &fHead;
+ while (*headPtr) {
+ SkCoincidentSpans** headNext = &(*headPtr)->fNext;
+ if (*headNext) {
+ break;
+ }
+ headPtr = headNext;
+ }
+ *headPtr = fTop;
+ return result;
+}
+
+void 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);
+ if (!s1->starter(e1)->span()->upCast()->windValue()) {
+ s1 = overS->find(seg1o);
+ e1 = overE->find(seg1o);
+ if (!s1->starter(e1)->span()->upCast()->windValue()) {
+ return;
+ }
+ }
+ SkOpPtT* s2 = overS->find(seg2);
+ SkOpPtT* e2 = overE->find(seg2);
+ if (!s2->starter(e2)->span()->upCast()->windValue()) {
+ s2 = overS->find(seg2o);
+ e2 = overE->find(seg2o);
+ if (!s2->starter(e2)->span()->upCast()->windValue()) {
+ return;
+ }
+ }
+ if (s1->segment() == s2->segment()) {
+ return;
+ }
+ if (s1->fT > e1->fT) {
+ SkTSwap(s1, e1);
+ SkTSwap(s2, e2);
+ }
+ this->add(s1, e1, s2, e2, allocator);
}
bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
@@ -316,11 +484,12 @@ void SkOpCoincidence::detach(SkCoincidentSpans* remove) {
SkASSERT(coin);
}
-void SkOpCoincidence::expand() {
+bool SkOpCoincidence::expand() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
- return;
+ return false;
}
+ bool expanded = false;
do {
SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
SkOpSpanBase* end = coin->fCoinPtTEnd->span();
@@ -333,6 +502,7 @@ void SkOpCoincidence::expand() {
if (segment->isClose(midT, oppSegment)) {
coin->fCoinPtTStart = prev->ptT();
coin->fOppPtTStart = oppPtT;
+ expanded = true;
}
}
SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next();
@@ -341,7 +511,61 @@ void SkOpCoincidence::expand() {
if (segment->isClose(midT, oppSegment)) {
coin->fCoinPtTEnd = next->ptT();
coin->fOppPtTEnd = oppPtT;
+ expanded = true;
+ }
+ }
+ } while ((coin = coin->fNext));
+ return expanded;
+}
+
+void SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const {
+ overlaps->fHead = overlaps->fTop = NULL;
+ SkDEBUGCODE_(overlaps->debugSetGlobalState(fDebugState));
+ SkCoincidentSpans* outer = fHead;
+ while (outer) {
+ SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
+ SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
+ SkCoincidentSpans* inner = outer;
+ while ((inner = inner->fNext)) {
+ SkOpSegment* innerCoin = inner->fCoinPtTStart->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,
+ &overlapS, &overlapE))
+ || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->fOppPtTStart,
+ outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
+ &overlapS, &overlapE))) {
+ overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
+ overlapS, overlapE, allocator);
+ }
+ }
+ outer = outer->fNext;
+ }
+}
+
+void SkOpCoincidence::fixAligned() {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ 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));
}
@@ -354,31 +578,36 @@ void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
do {
if (coin->fCoinPtTStart == deleted) {
if (coin->fCoinPtTEnd->span() == kept->span()) {
- return this->detach(coin);
+ this->detach(coin);
+ continue;
}
coin->fCoinPtTStart = kept;
}
if (coin->fCoinPtTEnd == deleted) {
if (coin->fCoinPtTStart->span() == kept->span()) {
- return this->detach(coin);
+ this->detach(coin);
+ continue;
}
coin->fCoinPtTEnd = kept;
}
if (coin->fOppPtTStart == deleted) {
if (coin->fOppPtTEnd->span() == kept->span()) {
- return this->detach(coin);
+ this->detach(coin);
+ continue;
}
coin->fOppPtTStart = kept;
}
if (coin->fOppPtTEnd == deleted) {
if (coin->fOppPtTStart->span() == kept->span()) {
- return this->detach(coin);
+ this->detach(coin);
+ continue;
}
coin->fOppPtTEnd = kept;
}
} while ((coin = coin->fNext));
}
+/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
void SkOpCoincidence::mark() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
@@ -397,8 +626,6 @@ void SkOpCoincidence::mark() {
}
SkOpSpanBase* next = start;
SkOpSpanBase* oNext = oStart;
- // check to see if coincident span could be bigger
-
do {
next = next->upCast()->next();
oNext = flipped ? oNext->prev() : oNext->upCast()->next();
@@ -419,10 +646,14 @@ void SkOpCoincidence::mark() {
bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
- if (coin1s->segment() != coin2s->segment()) {
- return false;
- }
+ SkASSERT(coin1s->segment() == coin2s->segment());
*overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
*overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
return *overS < *overE;
}
+
+bool SkOpCoincidence::testForCoincidence(const SkCoincidentSpans* outer, SkOpPtT* testS,
+ SkOpPtT* testE) const {
+ return testS->segment()->testForCoincidence(testS, testE, testS->span(),
+ testE->span(), outer->fCoinPtTStart->segment(), 120000); // FIXME: replace with tuned
+}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index ce57999b1d..83ba70a0f9 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -9,6 +9,7 @@
#include "SkOpTAllocator.h"
#include "SkOpSpan.h"
+#include "SkPathOpsTypes.h"
class SkOpPtT;
@@ -26,36 +27,81 @@ struct SkCoincidentSpans {
class SkOpCoincidence {
public:
SkOpCoincidence()
- : fHead(NULL) {
+ : fHead(NULL)
+ , fTop(NULL)
+ SkDEBUGPARAMS(fDebugState(NULL))
+ {
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
+ void addExpanded(SkChunkAlloc* allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* ));
bool addMissing(SkChunkAlloc* allocator);
void addMissing(SkCoincidentSpans* check, SkChunkAlloc* allocator);
bool apply();
bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, bool flipped);
+
+ const SkOpAngle* debugAngle(int id) const {
+ return SkDEBUGRELEASE(fDebugState->debugAngle(id), NULL);
+ }
+
+ SkOpContour* debugContour(int id) {
+ return SkDEBUGRELEASE(fDebugState->debugContour(id), NULL);
+ }
+
+ const SkOpPtT* debugPtT(int id) const {
+ return SkDEBUGRELEASE(fDebugState->debugPtT(id), NULL);
+ }
+
+ const SkOpSegment* debugSegment(int id) const {
+ return SkDEBUGRELEASE(fDebugState->debugSegment(id), NULL);
+ }
+
+ void debugSetGlobalState(SkOpGlobalState* debugState) {
+ SkDEBUGCODE(fDebugState = debugState);
+ }
+
void debugShowCoincidence() const;
+
+ const SkOpSpanBase* debugSpan(int id) const {
+ return SkDEBUGRELEASE(fDebugState->debugSpan(id), NULL);
+ }
+
void detach(SkCoincidentSpans* );
void dump() const;
- void expand();
+ bool expand();
bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd);
+ void findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
+ void fixAligned();
void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
+
+ bool isEmpty() const {
+ return !fHead;
+ }
+
void mark();
private:
+ void addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e,
+ SkChunkAlloc* );
bool 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);
+ SkChunkAlloc* );
+ void addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o,
+ SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* );
bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
double* overS, double* overE) const;
+ bool testForCoincidence(const SkCoincidentSpans* outer, SkOpPtT* testS, SkOpPtT* testE) const;
SkCoincidentSpans* fHead;
+ SkCoincidentSpans* fTop;
+ SkDEBUGCODE_(SkOpGlobalState* fDebugState);
};
#endif
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index f8143cf555..1225416eaa 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -34,6 +34,14 @@ public:
: 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);
}
@@ -222,16 +230,23 @@ public:
} while ((segment = segment->next()));
}
- void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
+ bool result = false;
do {
if (fState->angleCoincidence()) {
segment->checkAngleCoin(coincidences, allocator);
- } else {
- segment->missingCoincidence(coincidences, allocator);
+ } else if (segment->missingCoincidence(coincidences, allocator)) {
+ result = true;
+ // FIXME: trying again loops forever in issue3651_6
+ // The continue below is speculative -- once there's an actual case that requires it,
+ // add the plumbing necessary to look for another missing coincidence in the same segment
+ // continue; // try again in case another missing coincidence is further along
}
- } while ((segment = segment->next()));
+ segment = segment->next();
+ } while (segment);
+ return result;
}
bool moveMultiples() {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 462cff60cf..fd8ab65acd 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -159,6 +159,87 @@ 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()));
+}
+
void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathWriter* path, bool active) const {
SkOpCurve edge;
@@ -770,7 +851,7 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
SkASSERT(start != endNear);
SkASSERT((start->t() < endNear->t()) ^ (step < 0));
SkOpAngle* angle = this->spanToAngle(end, start);
- if (angle->unorderable()) {
+ if (!angle || angle->unorderable()) {
*unsortable = true;
markDone(start->starter(end));
return NULL;
@@ -817,6 +898,8 @@ SkOpGlobalState* SkOpSegment::globalState() const {
void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
fContour = contour;
fNext = NULL;
+ fOriginal[0] = pts[0];
+ fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)];
fPts = pts;
fWeight = weight;
fVerb = verb;
@@ -1113,12 +1196,12 @@ static void clear_visited(SkOpSpanBase* span) {
// 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
-void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
if (this->verb() != SkPath::kLine_Verb) {
- return;
+ return false;
}
if (this->done()) {
- return;
+ return false;
}
SkOpSpan* prior = NULL;
SkOpSpanBase* spanBase = &fHead;
@@ -1186,34 +1269,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
goto swapBack;
}
- {
- // average t, find mid pt
- double midT = (prior->t() + spanBase->t()) / 2;
- SkPoint midPt = this->ptAtT(midT);
- coincident = true;
- // 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)) {
- coincident = false;
- SkIntersections i;
- SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
- SkDLine ray = {{{midPt.fX, midPt.fY},
- {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
- (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
- // measure distance and see if it's small enough to denote coincidence
- for (int index = 0; index < i.used(); ++index) {
- 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 < 5000; // FIXME: replace with tuned value
- }
- }
- }
- }
+ coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
if (coincident) {
// mark coincidence
if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd)
@@ -1221,7 +1277,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
}
clear_visited(&fHead);
- return;
+ return true;
}
swapBack:
if (swapped) {
@@ -1230,6 +1286,7 @@ void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
}
} while ((spanBase = spanBase->final() ? NULL : spanBase->upCast()->next()));
clear_visited(&fHead);
+ return false;
}
// if a span has more than one intersection, merge the other segments' span as needed
@@ -1607,6 +1664,37 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
return true;
}
+bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT,
+ const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp,
+ SkScalar flatnessLimit) const {
+ // average t, find mid pt
+ double midT = (prior->t() + spanBase->t()) / 2;
+ SkPoint midPt = this->ptAtT(midT);
+ bool coincident = true;
+ // 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)) {
+ coincident = false;
+ SkIntersections i;
+ SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
+ SkDLine ray = {{{midPt.fX, midPt.fY}, {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
+ (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
+ // measure distance and see if it's small enough to denote coincidence
+ for (int index = 0; index < i.used(); ++index) {
+ 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;
+ }
+ }
+ }
+ return coincident;
+}
+
void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
SkOpSpan* span = this->head();
do {
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 38de40617a..6873203b25 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -45,6 +45,13 @@ 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);
@@ -244,7 +251,7 @@ public:
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;
- void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+ bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
void moveMultiples();
void moveNearby();
@@ -346,6 +353,9 @@ public:
return &fTail;
}
+ bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior,
+ const SkOpSpanBase* spanBase, const SkOpSegment* opp, SkScalar flatnessLimit) const;
+
void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateOppWinding(const SkOpAngle* angle) const;
@@ -386,6 +396,7 @@ private:
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;
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
index ae4771cffc..f5a52099da 100755
--- a/src/pathops/SkOpSpan.cpp
+++ b/src/pathops/SkOpSpan.cpp
@@ -13,10 +13,64 @@ bool SkOpPtT::alias() const {
return this->span()->ptT() != this;
}
+bool SkOpPtT::contains(const SkOpPtT* check) const {
+ SkASSERT(this != check);
+ const SkOpPtT* ptT = this;
+ const SkOpPtT* stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT == check) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) {
+ SkASSERT(this->segment() != check);
+ SkOpPtT* ptT = this;
+ const SkOpPtT* stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT->segment() == check) {
+ return ptT;
+ }
+ }
+ return NULL;
+}
+
SkOpContour* SkOpPtT::contour() const {
return segment()->contour();
}
+SkOpPtT* SkOpPtT::doppelganger() {
+ SkASSERT(fDeleted);
+ SkOpPtT* ptT = fNext;
+ while (ptT->fDeleted) {
+ ptT = ptT->fNext;
+ }
+ const SkOpPtT* stopPtT = ptT;
+ do {
+ if (ptT->fSpan == fSpan) {
+ return ptT;
+ }
+ ptT = ptT->fNext;
+ } while (stopPtT != ptT);
+ SkASSERT(0);
+ return NULL;
+}
+
+SkOpPtT* SkOpPtT::find(SkOpSegment* segment) {
+ SkOpPtT* ptT = this;
+ const SkOpPtT* stopPtT = ptT;
+ do {
+ if (ptT->segment() == segment) {
+ return ptT;
+ }
+ ptT = ptT->fNext;
+ } while (stopPtT != ptT);
+ SkASSERT(0);
+ return NULL;
+}
+
SkOpGlobalState* SkOpPtT::globalState() const {
return contour()->globalState();
}
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index 9b44247341..ec62246c31 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -8,6 +8,7 @@
#define SkOpSpan_DEFINED
#include "SkPathOpsDebug.h"
+#include "SkPathOpsTypes.h"
#include "SkPoint.h"
class SkChunkAlloc;
@@ -47,6 +48,8 @@ public:
}
bool alias() const;
+ bool contains(const SkOpPtT* ) const;
+ SkOpPtT* contains(const SkOpSegment* );
SkOpContour* contour() const;
int debugID() const {
@@ -67,6 +70,8 @@ public:
return fDeleted;
}
+ SkOpPtT* doppelganger();
+
bool duplicate() const {
return fDuplicatePt;
}
@@ -75,6 +80,7 @@ public:
void dumpAll() const;
void dumpBase() const;
+ SkOpPtT* find(SkOpSegment* );
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
void insert(SkOpPtT* span) {
@@ -92,6 +98,25 @@ 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;
+ *sOut = between(s1->fT, start2->fT, e1->fT) ? start2
+ : between(s2->fT, start1->fT, e2->fT) ? start1 : NULL;
+ SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
+ 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 : NULL;
+ if (*sOut == *eOut) {
+ SkASSERT(start1->fT >= end2->fT || start2->fT >= end1->fT);
+ return false;
+ }
+ SkASSERT(!*sOut || *sOut != *eOut);
+ return *sOut && *eOut;
+ }
+
SkOpPtT* prev();
SkOpPtT* remove();
void removeNext(SkOpPtT* kept);
@@ -112,6 +137,10 @@ public:
return fSpan;
}
+ const SkOpPtT* starter(const SkOpPtT* end) const {
+ return fT < end->fT ? this : end;
+ }
+
double fT;
SkPoint fPt; // cache of point value at this t
protected:
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 98cce15fb2..4ead297f7d 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -394,6 +394,13 @@ static void align(SkOpContourHead* contourList) {
} 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 {
@@ -401,12 +408,14 @@ static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
} while ((contour = contour->next()));
}
-static void missingCoincidence(SkOpContourHead* contourList,
+static bool missingCoincidence(SkOpContourHead* contourList,
SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
SkOpContour* contour = contourList;
+ bool result = false;
do {
- contour->missingCoincidence(coincidence, allocator);
+ result |= contour->missingCoincidence(coincidence, allocator);
} while ((contour = contour->next()));
+ return result;
}
static void moveMultiples(SkOpContourHead* contourList) {
@@ -438,23 +447,42 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
// move t values and points together to eliminate small/tiny gaps
moveNearby(contourList);
align(contourList); // give all span members common values
+ coincidence->fixAligned(); // aligning may have marked a coincidence pt-t deleted
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kIntersecting);
#endif
+ // look for intersections on line segments formed by moving end points
+ addAlignIntersections(contourList, allocator);
coincidence->addMissing(allocator);
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kWalking);
#endif
- coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
+ // check to see if, loosely, coincident ranges may be expanded
+ if (coincidence->expand()) {
+ coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState));
+ }
+ // the expanded ranges may not align -- add the missing spans
coincidence->mark(); // mark spans of coincident segments as coincident
- missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
- if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
- return false;
+ // look for coincidence missed earlier
+ if (missingCoincidence(contourList, coincidence, allocator)) {
+ (void) coincidence->expand();
+ coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState));
+ coincidence->mark();
}
+ SkOpCoincidence overlaps;
+ do {
+ SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
+ if (!pairs->apply()) { // adjust the winding value to account for coincident edges
+ return false;
+ }
+ // 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
+ pairs->findOverlaps(&overlaps, allocator);
+ } while (!overlaps.isEmpty());
calcAngles(contourList, allocator);
sortAngles(contourList);
if (globalState->angleCoincidence()) {
- missingCoincidence(contourList, coincidence, allocator);
+ (void) missingCoincidence(contourList, coincidence, allocator);
if (!coincidence->apply()) {
return false;
}
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 627a7c9f40..49422305a7 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -135,6 +135,18 @@ void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp
show_op(shapeOp, "path", "pathB");
}
+#include "SkPathOpsTypes.h"
+
+#ifdef SK_DEBUG
+bool SkOpGlobalState::debugRunFail() const {
+#if DEBUG_VALIDATE
+ return FLAGS_runFail;
+#else
+ return false;
+#endif
+}
+#endif
+
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 61c2ecab53..ee2c291869 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -95,6 +95,12 @@
#define SkDEBUGCODE_(...) __VA_ARGS__ // temporary until SkDEBUGCODE is fixed
#endif
+#if DEBUG_VALIDATE == 0
+ #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...)
+#else
+ #define PATH_OPS_DEBUG_VALIDATE_PARAMS(...) , __VA_ARGS__
+#endif
+
#if DEBUG_T_SECT == 0
#define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b
#define PATH_OPS_DEBUG_T_SECT_PARAMS(...)
@@ -179,6 +185,12 @@ public:
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 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 class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h
index 7fec70a32a..2b1e6393f4 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -25,9 +25,6 @@ struct SkDRect {
}
bool intersects(const SkDRect& r) const {
- if (fLeft > fRight) {
- SkDebugf("!");
- }
SkASSERT(fLeft <= fRight);
SkASSERT(fTop <= fBottom);
SkASSERT(r.fLeft <= r.fRight);
diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp
index 2c8d778c69..dcd33c9d73 100644
--- a/src/pathops/SkPathOpsTypes.cpp
+++ b/src/pathops/SkPathOpsTypes.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkFloatBits.h"
+#include "SkOpCoincidence.h"
#include "SkPathOpsTypes.h"
static bool arguments_denormalized(float a, float b, int epsilon) {
@@ -198,3 +199,21 @@ double SkDCubeRoot(double x) {
}
return result;
}
+
+SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head)
+ : fCoincidence(coincidence)
+ , fContourHead(head)
+ , fNested(0)
+ , fWindingFailed(false)
+ , fAngleCoincidence(false)
+ , fPhase(kIntersecting)
+ SkDEBUGPARAMS(fAngleID(0))
+ SkDEBUGPARAMS(fContourID(0))
+ SkDEBUGPARAMS(fPtTID(0))
+ SkDEBUGPARAMS(fSegmentID(0))
+ SkDEBUGPARAMS(fSpanID(0)) {
+ if (coincidence) {
+ coincidence->debugSetGlobalState(this);
+ }
+}
+
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 13ebfee84d..9550aa1ead 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -28,19 +28,7 @@ class SkOpContourHead;
class SkOpGlobalState {
public:
- SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head)
- : fCoincidence(coincidence)
- , fContourHead(head)
- , fNested(0)
- , fWindingFailed(false)
- , fAngleCoincidence(false)
- , fPhase(kIntersecting)
- SkDEBUGPARAMS(fAngleID(0))
- SkDEBUGPARAMS(fContourID(0))
- SkDEBUGPARAMS(fPtTID(0))
- SkDEBUGPARAMS(fSegmentID(0))
- SkDEBUGPARAMS(fSpanID(0)) {
- }
+ SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head);
enum Phase {
kIntersecting,
@@ -76,6 +64,7 @@ public:
const struct SkOpAngle* debugAngle(int id) const;
SkOpContour* debugContour(int id);
const class SkOpPtT* debugPtT(int id) const;
+ bool debugRunFail() const;
const class SkOpSegment* debugSegment(int id) const;
const class SkOpSpanBase* debugSpan(int id) const;
#endif
diff --git a/src/ports/SkDebug_win.cpp b/src/ports/SkDebug_win.cpp
index fe28ee27b1..8bbbef58e0 100644
--- a/src/ports/SkDebug_win.cpp
+++ b/src/ports/SkDebug_win.cpp
@@ -25,7 +25,7 @@ void SkDebugf(const char format[], ...) {
va_end(args);
// When we crash on Windows we often are missing a lot of prints. Since we don't really care
// about SkDebugf performance we flush after every print.
- fflush(stdout);
+// fflush(stdout);
va_start(args, format);
vsnprintf(buffer, kBufferSize, format, args);
diff --git a/tests/PathOpsBattles.cpp b/tests/PathOpsBattles.cpp
index 8f59f9f48e..3cf1353891 100644
--- a/tests/PathOpsBattles.cpp
+++ b/tests/PathOpsBattles.cpp
@@ -10681,7 +10681,7 @@ path.close();
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
}
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp121;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp183;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
diff --git a/tests/PathOpsDebug.cpp b/tests/PathOpsDebug.cpp
index 95f06e5b7d..507d865926 100755
--- a/tests/PathOpsDebug.cpp
+++ b/tests/PathOpsDebug.cpp
@@ -13,6 +13,8 @@
#include "SkOpSegment.h"
#include "SkString.h"
+extern bool FLAGS_runFail;
+
inline void DebugDumpDouble(double x) {
if (x == floor(x)) {
SkDebugf("%.0f", x);
@@ -238,6 +240,26 @@ const SkOpSpanBase* SkPathOpsDebug::DebugContourSpan(SkOpContour* contour, int i
return contour->debugSpan(id);
}
+const SkOpAngle* SkPathOpsDebug::DebugCoincidenceAngle(SkOpCoincidence* coin, int id) {
+ return coin->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugCoincidenceContour(SkOpCoincidence* coin, int id) {
+ return coin->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugCoincidencePtT(SkOpCoincidence* coin, int id) {
+ return coin->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugCoincidenceSegment(SkOpCoincidence* coin, int id) {
+ return coin->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugCoincidenceSpan(SkOpCoincidence* coin, int id) {
+ return coin->debugSpan(id);
+}
+
const SkOpAngle* SkPathOpsDebug::DebugPtTAngle(const SkOpPtT* ptT, int id) {
return ptT->debugAngle(id);
}
@@ -1119,6 +1141,15 @@ void SkOpCoincidence::dump() const {
span->dump();
span = span->fNext;
}
+ if (!fTop) {
+ return;
+ }
+ SkDebugf("top:\n");
+ span = fTop;
+ while (span) {
+ span->dump();
+ span = span->fNext;
+ }
}
void SkOpContour::dump() const {
diff --git a/tests/PathOpsIssue3651.cpp b/tests/PathOpsIssue3651.cpp
index 583c38f75a..ae4afc4688 100644
--- a/tests/PathOpsIssue3651.cpp
+++ b/tests/PathOpsIssue3651.cpp
@@ -367,6 +367,114 @@ path.close();
return path;
}
+static SkPath path1_a() {
+ SkPath path;
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a)); // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4)); // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789)); // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f
+#if 0
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc)); // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f
+#if 0
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b)); // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69)); // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26)); // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710)); // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909)); // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d)); // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048)); // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7)); // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104)); // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f)); // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7)); // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140)); // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b)); // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd)); // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37)); // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5)); // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff)); // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b)); // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947)); // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee)); // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3)); // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786)); // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d)); // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab)); // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c)); // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048)); // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6)); // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653)); // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6)); // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f
+#endif
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979)); // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e)); // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759)); // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8)); // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f
+#endif
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.close();
+ return path;
+}
+
+static SkPath path2_a() {
+ SkPath path;
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c)); // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2)); // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd)); // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f
+#if 0
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa)); // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f
+#if 0
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb)); // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd)); // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40)); // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56)); // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d)); // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9)); // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e)); // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf)); // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562)); // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847)); // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f)); // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526)); // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb)); // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99)); // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f)); // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1)); // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467)); // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b)); // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f)); // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78)); // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983)); // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0)); // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339)); // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb)); // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a)); // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e)); // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570)); // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013)); // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0)); // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f
+#endif
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced)); // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518)); // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d)); // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be)); // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f
+#endif
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f
+path.close();
+ return path;
+}
+
+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);
+}
+
static SkPath path3() {
SkPath path;
path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f
@@ -1094,7 +1202,7 @@ path.close();
static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
SkPath path = path1();
SkPath pathB = path2();
- testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, FLAGS_runFail);
+ testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
}
static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
@@ -1543,7 +1651,7 @@ path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959588), SkBits2Float(0x
path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a)); // 253, 74.8f
path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f
path.close();
- testPathOpCheck(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename, FLAGS_runFail);
+ testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
}
@@ -1552,11 +1660,12 @@ static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
- TEST(issue3651_7),
TEST(issue3651_6),
+ TEST(issue3651_1a),
+ TEST(issue3651_1),
+ TEST(issue3651_7),
TEST(issue3651_5),
TEST(issue3651_4),
- TEST(issue3651_1),
TEST(issue3651_2),
TEST(issue3651_3),
};
diff --git a/tests/PathOpsSimplifyQuadThreadedTest.cpp b/tests/PathOpsSimplifyQuadThreadedTest.cpp
index 047aa6b795..e3c0bc29d8 100644
--- a/tests/PathOpsSimplifyQuadThreadedTest.cpp
+++ b/tests/PathOpsSimplifyQuadThreadedTest.cpp
@@ -49,7 +49,7 @@ static void testSimplifyQuadsMain(PathOpsThreadState* data)
SkIntToScalar(hx), SkIntToScalar(hy));
path.close();
if (progress) {
- static int quadTest = 65;
+ static int quadTest = 66;
char* str = pathStr;
str += sprintf(str, "static void testQuads%d(skiatest::Reporter* reporter,"
"const char* filename) {\n", quadTest);
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 40bbeb6baa..5b763dee83 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -4797,11 +4797,25 @@ static void testIssue3838_3(skiatest::Reporter* reporter,const char* filename) {
testSimplify(reporter, path, filename);
}
+static void testQuads65(skiatest::Reporter* reporter,const char* filename) {
+ SkPath path;
+ path.moveTo(1, 2);
+ path.quadTo(3, 2, 0, 3);
+ path.lineTo(1, 3);
+ path.close();
+ path.moveTo(1, 0);
+ path.lineTo(1, 2);
+ path.quadTo(3, 2, 1, 3);
+ path.close();
+ testSimplify(reporter, path, filename);
+}
+
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static TestDesc tests[] = {
+ TEST(testQuads65),
TEST(testIssue3838_3),
TEST(testIssue3838),
TEST(testArc),
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 4fb18fad64..892dbea242 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -7,10 +7,47 @@
<div style="height:0">
<div id="sect1">
-{{{fX=1.97000003 fY=2.00000000 }, {fX=1.97000003 fY=3.97000003 }, {fX=0.000000000 fY=3.97000003 }} fW: 0.707106769
-{{{fX=1.99750006 fY=2.00000000 } {fX=1.99750006 fY=2.02749991 }
-{{{fX=1.96415257 fY=2.36347127 } {fX=1.95914865 fY=2.39051223 }
+{{{ (fX = 27.2011719, fY = 66.6567383) (fX = 24.5820313, fY = 72.1083984)
+{{{ (fX = 24.5820313, fY = 72.1083984) (fX = 17.2832031, fY = 72.1083984)
+{{{ (fX = 17.2832031, fY = 72.1083984) (fX = 18.40625, fY = 68.6191406)
+{{{ (fX = 18.40625, fY = 68.6191406) (fX = 16.7207031, fY = 66.2197265)
+{{{ (fX = 16.7207031, fY = 66.2197265) (fX = 10.3574219, fY = 65.7841796)
+{{{ (fX = 10.3574219, fY = 65.7841796) (fX = 9.984375, fY = 59.2387695)
+{{{ (fX = 9.984375, fY = 59.2387695) (fX = 9.421875, fY = 54.6552734)
+{{{ (fX = 9.421875, fY = 54.6552734) (fX = 14.1015625, fY = 53.1269531)
+{{{ (fX = 14.1015625, fY = 53.1269531) (fX = 15.5976563, fY = 48.5419922)
+{{{ (fX = 15.5976563, fY = 48.5419922) (fX = 21.0253906, fY = 49.4160156)
+{{{ (fX = 21.0253906, fY = 49.4160156) (fX = 24.015625, fY = 49.6342773)
+{{{ (fX = 24.015625, fY = 49.6342773) (fX = 26.0761719, fY = 48.3237305)
+{{{ (fX = 26.0761719, fY = 48.3237305) (fX = 27.2011719, fY = 52.0356445)
+{{{ (fX = 27.2011719, fY = 52.0356445) (fX = 29.2558594, fY = 52.0356445)
+{{{ (fX = 29.2558594, fY = 52.0356445) (fX = 30.1914063, fY = 49.4160156)
+{{{ (fX = 30.1914063, fY = 49.4160156) (fX = 31.875, fY = 52.0356445)
+{{{ (fX = 31.875, fY = 52.0356445) (fX = 32.4394531, fY = 52.9086914)
+{{{ (fX = 32.4394531, fY = 52.9086914) (fX = 33.375, fY = 54.8735352)
+{{{ (fX = 33.375, fY = 54.8735352) (fX = 29.8203125, fY = 57.7109375)
+{{{ (fX = 29.8203125, fY = 57.7109375) (fX = 33.375, fY = 61.8569336)
+{{{ (fX = 33.375, fY = 61.8569336) (fX = 33.9355469, fY = 61.8569336)
+{{{ (fX = 33.9355469, fY = 61.8569336) (fX = 37.6777344, fY = 67.3110352)
+{{{ (fX = 37.6777344, fY = 67.3110352) (fX = 37.1152344, fY = 72.1083984)
+{{{ (fX = 37.1152344, fY = 72.1083984) (fX = 31.3164063, fY = 72.1083984)
+{{{ (fX = 31.3164063, fY = 72.1083984) (fX = 30.9414063, fY = 72.1083984)
+{{{ (fX = 30.9414063, fY = 72.1083984) (fX = 28.3222656, fY = 65.5654296)
+{{{ (fX = 28.3222656, fY = 65.5654296) (fX = 27.2011719, fY = 66.6567383)
+{{{ (fX = 25.5175781, fY = 62.9482421) (fX = 19.9023438, fY = 60.7661133)
+{{{ (fX = 19.9023438, fY = 60.7661133) (fX = 19.3417969, fY = 59.456543)
+{{{ (fX = 19.3417969, fY = 59.456543) (fX = 18.21875, fY = 61.8569336)
+{{{ (fX = 18.21875, fY = 61.8569336) (fX = 19.1523438, fY = 63.1665039)
+{{{ (fX = 19.1523438, fY = 63.1665039) (fX = 21.9609375, fY = 67.0932617)
+{{{ (fX = 21.9609375, fY = 67.0932617) (fX = 25.1425781, fY = 65.5654296)
+{{{ (fX = 25.1425781, fY = 65.5654296) (fX = 25.5175781, fY = 62.9482421)
+
+{{{ (fX = 29.8203125, fY = 94.3359375) (fX = 29.2558594, fY = 88.890625)
+{{{ (fX = 29.2558594, fY = 88.890625) (fX = 31.3164063, fY = 87.1479492)
+{{{ (fX = 31.3164063, fY = 87.1479492) (fX = 36.3671875, fY = 90.633789)
+{{{ (fX = 36.3671875, fY = 90.633789) (fX = 33.9355469, fY = 95.4248046)
+{{{ (fX = 33.9355469, fY = 95.4248046) (fX = 29.8203125, fY = 94.3359375)
</div>
</div>
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index 4517ceb505..194c89980d 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -2,96 +2,200 @@
<head>
<div height="0" hidden="true">
-<div id="builder3838_3" >
-seg=1 {{{40, 10}, {60, 10}}}
-seg=2 {{{60, 10}, {60, 30}}}
-seg=3 {{{60, 30}, {40, 30}}}
-seg=4 {{{40, 30}, {40, 10}}}
-seg=5 {{{41, 11}, {41, 29}}}
-seg=6 {{{41, 29}, {59, 29}}}
-seg=7 {{{59, 29}, {59, 11}}}
-seg=8 {{{59, 11}, {41, 11}}}
-debugShowLineIntersection wtTs[0]=0 {{{60,10}, {60,30}}} {{60,10}} wnTs[0]=1 {{{40,10}, {60,10}}}
-debugShowLineIntersection wtTs[0]=1 {{{40,30}, {40,10}}} {{40,10}} wnTs[0]=0 {{{40,10}, {60,10}}}
-debugShowLineIntersection wtTs[0]=0 {{{60,30}, {40,30}}} {{60,30}} wnTs[0]=1 {{{60,10}, {60,30}}}
-debugShowLineIntersection wtTs[0]=0 {{{40,30}, {40,10}}} {{40,30}} wnTs[0]=1 {{{60,30}, {40,30}}}
-debugShowLineIntersection wtTs[0]=0 {{{41,29}, {59,29}}} {{41,29}} wnTs[0]=1 {{{41,11}, {41,29}}}
-debugShowLineIntersection wtTs[0]=1 {{{59,11}, {41,11}}} {{41,11}} wnTs[0]=0 {{{41,11}, {41,29}}}
-debugShowLineIntersection wtTs[0]=0 {{{59,29}, {59,11}}} {{59,29}} wnTs[0]=1 {{{41,29}, {59,29}}}
-debugShowLineIntersection wtTs[0]=0 {{{59,11}, {41,11}}} {{59,11}} wnTs[0]=1 {{{59,29}, {59,11}}}
-SkOpSegment::debugShowActiveSpans id=1 (40,10 60,10) t=0 (40,10) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=2 (60,10 60,30) t=0 (60,10) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=3 (60,30 40,30) t=0 (60,30) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=4 (40,30 40,10) t=0 (40,30) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=5 (41,11 41,29) t=0 (41,11) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=6 (41,29 59,29) t=0 (41,29) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=7 (59,29 59,11) t=0 (59,29) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=8 (59,11 41,11) t=0 (59,11) tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(50,10)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=1 seg=1 {{{40, 10}, {60, 10}}} t=0.5 pt=(50,10) slope=(20,0)
-SkOpSegment::markWinding id=1 (40,10 60,10) t=0 [1] (40,10) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=2 (60,10 60,30) t=0 [3] (60,10) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=3 (60,30 40,30) t=0 [5] (60,30) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=4 (40,30 40,10) t=0 [7] (40,30) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=1 (40,10 60,10) t=0 [1] (40,10) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=1 (40,10 60,10) t=0 [1] (40,10) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=1 from=(60,10) to=(40,10)
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=4 (40,30 40,10) t=0 [7] (40,30) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=4 from=(40,10) to=(40,30)
-path.moveTo(60,10);
-path.lineTo(40,10);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=3 (60,30 40,30) t=0 [5] (60,30) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=3 from=(40,30) to=(60,30)
-path.lineTo(40,30);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=2 (60,10 60,30) t=0 [3] (60,10) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=2 from=(60,30) to=(60,10)
-path.lineTo(60,30);
-path.lineTo(60,10);
-path.close();
-SkOpSegment::debugShowActiveSpans id=5 (41,11 41,29) t=0 (41,11) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=6 (41,29 59,29) t=0 (41,29) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=7 (59,29 59,11) t=0 (59,29) tEnd=1 windSum=? windValue=1
-SkOpSegment::debugShowActiveSpans id=8 (59,11 41,11) t=0 (59,11) tEnd=1 windSum=? windValue=1
-SkOpSpan::sortableTop dir=kLeft seg=5 t=0.5 pt=(41,20)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=7 ccw=1 seg=4 {{{40, 30}, {40, 10}}} t=0.5 pt=(40,20) slope=(0,-20)
-SkOpSpan::sortableTop [1] valid=1 operand=0 span=9 ccw=0 seg=5 {{{41, 11}, {41, 29}}} t=0.5 pt=(41,20) slope=(0,18)
-SkOpSegment::markWinding id=5 (41,11 41,29) t=0 [9] (41,11) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markWinding id=6 (41,29 59,29) t=0 [11] (41,29) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=7 (59,29 59,11) t=0 [13] (59,29) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=8 (59,11 41,11) t=0 [15] (59,11) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=5 (41,11 41,29) t=0 [9] (41,11) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=5 (41,11 41,29) t=0 [9] (41,11) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=5 from=(41,29) to=(41,11)
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=8 (59,11 41,11) t=0 [15] (59,11) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=8 from=(41,11) to=(59,11)
-path.moveTo(41,29);
-path.lineTo(41,11);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=7 (59,29 59,11) t=0 [13] (59,29) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=7 from=(59,11) to=(59,29)
-path.lineTo(59,11);
-SkOpSegment::findNextWinding simple
-SkOpSegment::markDone id=6 (41,29 59,29) t=0 [11] (41,29) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeWinding current id=6 from=(59,29) to=(41,29)
-path.lineTo(59,29);
-path.lineTo(41,29);
-path.close();
-SkOpSpan::sortableTop dir=kTop seg=1 t=0.5 pt=(50,10)
-SkOpSpan::sortableTop [0] valid=1 operand=0 span=1 ccw=0 seg=1 {{{60, 10}, {40, 10}}} t=0.5 pt=(50,10) slope=(-20,0)
+<div id="battleOp183">
+seg=1 {{{6.31801322e-006f, -60}, {0, -83}}}
+seg=2 {{{0, -83}, {32.0712242f, -83}, {61.2726326f, -64.5230865f}, {75.0056381f, -35.5408783f}}}
+seg=3 {{{75.0056381f, -35.5408783f}, {88.7386475f, -6.55867052f}, {84.545517f, 27.7420006f}, {64.2353287f, 52.562561f}}}
+seg=4 {{{64.2353287f, 52.562561f}, {60.2773972f, 57.3994484f}, {55.7858162f, 61.773819f}, {50.8459854f, 65.6024933f}}}
+seg=5 {{{50.8459854f, 65.6024933f}, {36.756134f, 47.423481f}}}
+seg=6 {{{36.756134f, 47.423481f}, {40.3270988f, 44.6557693f}, {43.5740242f, 41.493576f}, {46.4351768f, 37.9970322f}}}
+seg=7 {{{46.4351768f, 37.9970322f}, {61.1172447f, 20.0544662f}, {64.1484222f, -4.74120331f}, {54.2209473f, -25.6921959f}}}
+seg=8 {{{54.2209473f, -25.6921959f}, {44.2934723f, -46.6431847f}, {23.1840267f, -60}, {6.31801322e-006f, -60}}}
+op union
+seg=9 {{{50.8459854f, 65.6024857f}, {23.334074f, 86.9259186f}, {-14.5602179f, 88.8177719f}, {-44.0591507f, 70.3405457f}}}
+seg=10 {{{-44.0591507f, 70.3405457f}, {-73.5580902f, 51.8633156f}, {-88.3942261f, 16.9427452f}, {-81.2158127f, -17.116993f}}}
+seg=11 {{{-81.2158127f, -17.116993f}, {-74.0374069f, -51.1767159f}, {-46.3696136f, -77.1391754f}, {-11.9226456f, -82.1392059f}}}
+seg=12 {{{-11.9226456f, -82.1392059f}, {-8.61876869f, -59.3777466f}}}
+seg=13 {{{-8.61876869f, -59.3777466f}, {-33.5202026f, -55.7632599f}, {-53.5210152f, -36.9952087f}, {-58.7102203f, -12.3737135f}}}
+seg=14 {{{-58.7102203f, -12.3737135f}, {-63.8994179f, 12.2477798f}, {-53.1744957f, 37.4915581f}, {-31.849966f, 50.8485832f}}}
+seg=15 {{{-31.849966f, 50.8485832f}, {-10.5254354f, 64.2056046f}, {16.8680305f, 62.8380051f}, {36.7561607f, 47.4234695f}}}
+seg=16 {{{36.7561607f, 47.4234695f}, {50.8459854f, 65.6024857f}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{0,-83}, {32.0712242,-83}, {61.2726326,-64.5230865}, {75.0056381,-35.5408783}}} {{0,-83}} wnTs[0]=1 {{{6.31801322e-006,-60}, {0,-83}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{54.2209473,-25.6921959}, {44.2934723,-46.6431847}, {23.1840267,-60}, {6.31801322e-006,-60}}} {{6.31801322e-006,-60}} wnTs[0]=0 {{{6.31801322e-006,-60}, {0,-83}}}
+debugShowCubicIntersection wtTs[0]=1 {{{0,-83}, {32.0712242,-83}, {61.2726326,-64.5230865}, {75.0056381,-35.5408783}}} {{75.0056381,-35.5408783}} wnTs[0]=0 {{{75.0056381,-35.5408783}, {88.7386475,-6.55867052}, {84.545517,27.7420006}, {64.2353287,52.562561}}}
+debugShowCubicIntersection no intersect {{{0,-83}, {32.0712242,-83}, {61.2726326,-64.5230865}, {75.0056381,-35.5408783}}} {{{54.2209473,-25.6921959}, {44.2934723,-46.6431847}, {23.1840267,-60}, {6.31801322e-006,-60}}}
+debugShowCubicIntersection wtTs[0]=1 {{{75.0056381,-35.5408783}, {88.7386475,-6.55867052}, {84.545517,27.7420006}, {64.2353287,52.562561}}} {{64.2353287,52.562561}} wnTs[0]=0 {{{64.2353287,52.562561}, {60.2773972,57.3994484}, {55.7858162,61.773819}, {50.8459854,65.6024933}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{64.2353287,52.562561}, {60.2773972,57.3994484}, {55.7858162,61.773819}, {50.8459854,65.6024933}}} {{50.8459854,65.6024933}} wnTs[0]=0 {{{50.8459854,65.6024933}, {36.756134,47.423481}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{36.756134,47.423481}, {40.3270988,44.6557693}, {43.5740242,41.493576}, {46.4351768,37.9970322}}} {{36.756134,47.423481}} wnTs[0]=1 {{{50.8459854,65.6024933}, {36.756134,47.423481}}}
+debugShowCubicIntersection wtTs[0]=1 {{{36.756134,47.423481}, {40.3270988,44.6557693}, {43.5740242,41.493576}, {46.4351768,37.9970322}}} {{46.4351768,37.9970322}} wnTs[0]=0 {{{46.4351768,37.9970322}, {61.1172447,20.0544662}, {64.1484222,-4.74120331}, {54.2209473,-25.6921959}}}
+debugShowCubicIntersection wtTs[0]=1 {{{46.4351768,37.9970322}, {61.1172447,20.0544662}, {64.1484222,-4.74120331}, {54.2209473,-25.6921959}}} {{54.2209473,-25.6921959}} wnTs[0]=0 {{{54.2209473,-25.6921959}, {44.2934723,-46.6431847}, {23.1840267,-60}, {6.31801322e-006,-60}}}
+debugShowCubicIntersection no intersect {{{64.2353287,52.562561}, {60.2773972,57.3994484}, {55.7858162,61.773819}, {50.8459854,65.6024933}}} {{{50.8459854,65.6024857}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}}
+debugShowCubicLineIntersection no intersect {{{64.2353287,52.562561}, {60.2773972,57.3994484}, {55.7858162,61.773819}, {50.8459854,65.6024933}}} {{{36.7561607,47.4234695}, {50.8459854,65.6024857}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{50.8459854,65.6024857}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} {{50.8459854,65.6024857}} wnTs[0]=2.62183e-007 {{{50.8459854,65.6024933}, {36.756134,47.423481}}}
+SkOpSegment::addT alias t=2.62182896e-007 segID=5 spanID=9
+debugShowCubicLineIntersection wtTs[0]=0.999999628 {{{-31.849966,50.8485832}, {-10.5254354,64.2056046}, {16.8680305,62.8380051}, {36.7561607,47.4234695}}} {{36.7561378,47.4234886}} wnTs[0]=1 {{{50.8459854,65.6024933}, {36.756134,47.423481}}}
+SkOpSegment::addT alias t=0.999999682 segID=5 spanID=10
+SkOpSegment::addT insert t=0.999999628 segID=15 spanID=33
+debugShowLineIntersection wtTs[0]=2.62182896e-007 {{{50.8459854,65.6024933}, {36.756134,47.423481}}} {{50.8459854,65.6024857}} wnTs[0]=1 {{{36.7561607,47.4234695}, {50.8459854,65.6024857}}}
+debugShowCubicIntersection no intersect {{{36.756134,47.423481}, {40.3270988,44.6557693}, {43.5740242,41.493576}, {46.4351768,37.9970322}}} {{{-31.849966,50.8485832}, {-10.5254354,64.2056046}, {16.8680305,62.8380051}, {36.7561607,47.4234695}}}
+debugShowCubicLineIntersection no intersect {{{36.756134,47.423481}, {40.3270988,44.6557693}, {43.5740242,41.493576}, {46.4351768,37.9970322}}} {{{36.7561607,47.4234695}, {50.8459854,65.6024857}}}
+debugShowCubicIntersection wtTs[0]=1 {{{50.8459854,65.6024857}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} {{-44.0591507,70.3405457}} wnTs[0]=0 {{{-44.0591507,70.3405457}, {-73.5580902,51.8633156}, {-88.3942261,16.9427452}, {-81.2158127,-17.116993}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{50.8459854,65.6024857}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} {{50.8459854,65.6024857}} wnTs[0]=1 {{{36.7561607,47.4234695}, {50.8459854,65.6024857}}}
+debugShowCubicIntersection wtTs[0]=1 {{{-44.0591507,70.3405457}, {-73.5580902,51.8633156}, {-88.3942261,16.9427452}, {-81.2158127,-17.116993}}} {{-81.2158127,-17.116993}} wnTs[0]=0 {{{-81.2158127,-17.116993}, {-74.0374069,-51.1767159}, {-46.3696136,-77.1391754}, {-11.9226456,-82.1392059}}}
+debugShowCubicIntersection no intersect {{{-44.0591507,70.3405457}, {-73.5580902,51.8633156}, {-88.3942261,16.9427452}, {-81.2158127,-17.116993}}} {{{-8.61876869,-59.3777466}, {-33.5202026,-55.7632599}, {-53.5210152,-36.9952087}, {-58.7102203,-12.3737135}}}
+debugShowCubicIntersection no intersect {{{-44.0591507,70.3405457}, {-73.5580902,51.8633156}, {-88.3942261,16.9427452}, {-81.2158127,-17.116993}}} {{{-58.7102203,-12.3737135}, {-63.8994179,12.2477798}, {-53.1744957,37.4915581}, {-31.849966,50.8485832}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{-81.2158127,-17.116993}, {-74.0374069,-51.1767159}, {-46.3696136,-77.1391754}, {-11.9226456,-82.1392059}}} {{-11.9226456,-82.1392059}} wnTs[0]=0 {{{-11.9226456,-82.1392059}, {-8.61876869,-59.3777466}}}
+debugShowCubicIntersection no intersect {{{-81.2158127,-17.116993}, {-74.0374069,-51.1767159}, {-46.3696136,-77.1391754}, {-11.9226456,-82.1392059}}} {{{-8.61876869,-59.3777466}, {-33.5202026,-55.7632599}, {-53.5210152,-36.9952087}, {-58.7102203,-12.3737135}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{-8.61876869,-59.3777466}, {-33.5202026,-55.7632599}, {-53.5210152,-36.9952087}, {-58.7102203,-12.3737135}}} {{-8.61876869,-59.3777466}} wnTs[0]=1 {{{-11.9226456,-82.1392059}, {-8.61876869,-59.3777466}}}
+debugShowCubicIntersection wtTs[0]=1 {{{-8.61876869,-59.3777466}, {-33.5202026,-55.7632599}, {-53.5210152,-36.9952087}, {-58.7102203,-12.3737135}}} {{-58.7102203,-12.3737135}} wnTs[0]=0 {{{-58.7102203,-12.3737135}, {-63.8994179,12.2477798}, {-53.1744957,37.4915581}, {-31.849966,50.8485832}}}
+debugShowCubicIntersection wtTs[0]=1 {{{-58.7102203,-12.3737135}, {-63.8994179,12.2477798}, {-53.1744957,37.4915581}, {-31.849966,50.8485832}}} {{-31.849966,50.8485832}} wnTs[0]=0 {{{-31.849966,50.8485832}, {-10.5254354,64.2056046}, {16.8680305,62.8380051}, {36.7561607,47.4234695}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{-31.849966,50.8485832}, {-10.5254354,64.2056046}, {16.8680305,62.8380051}, {36.7561607,47.4234695}}} {{36.7561607,47.4234695}} wnTs[0]=0 {{{36.7561607,47.4234695}, {50.8459854,65.6024857}}}
+SkOpSegment::sortAngles [4] tStart=1 [8]
+SkOpAngle::after [4/1] 1/1 tStart=1 tEnd=0 < [16/8] 9/9 tStart=1 tEnd=0 < [5/2] 9/9 tStart=0 tEnd=1 T 7
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {55.7858162,61.773819}, {60.2773972,57.3994484}, {64.2353287,52.562561}}} id=4
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.7561607,47.4234695}}} id=16
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.756134,47.423481}}} id=5
+SkOpAngle::after [4/1] 1/1 tStart=1 tEnd=0 < [9/5] 17/17 tStart=0 tEnd=1 < [16/8] 9/9 tStart=1 tEnd=0 F 4
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {55.7858162,61.773819}, {60.2773972,57.3994484}, {64.2353287,52.562561}}} id=4
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} id=9
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.7561607,47.4234695}}} id=16
+SkOpAngle::after [16/8] 9/9 tStart=1 tEnd=0 < [9/5] 17/17 tStart=0 tEnd=1 < [5/2] 9/9 tStart=0 tEnd=1 F 5
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.7561607,47.4234695}}} id=16
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} id=9
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.756134,47.423481}}} id=5
+SkOpAngle::after [5/2] 9/9 tStart=0 tEnd=1 < [9/5] 17/17 tStart=0 tEnd=1 < [4/1] 1/1 tStart=1 tEnd=0 T 4
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {36.756134,47.423481}}} id=5
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {23.334074,86.9259186}, {-14.5602179,88.8177719}, {-44.0591507,70.3405457}}} id=9
+SkOpAngle::afterPart {{{50.8459854,65.6024933}, {55.7858162,61.773819}, {60.2773972,57.3994484}, {64.2353287,52.562561}}} id=4
+SkOpSegment::sortAngles [5] tStart=0 [9]
+SkOpSegment::sortAngles [5] tStart=1 [10]
+SkOpAngle::after [5/3] 25/25 tStart=1 tEnd=0 < [15/7] 1/1 tStart=0.999999628 tEnd=1 < [15/6] 17/17 tStart=0.999999628 tEnd=0 T 4
+SkOpAngle::afterPart {{{36.756134,47.423481}, {50.8459854,65.6024933}}} id=5
+SkOpAngle::afterPart {{{36.756134,47.423481}, {36.7561414,47.4234752}, {36.7561533,47.4234753}, {36.7561607,47.4234695}}} id=15
+SkOpAngle::afterPart {{{36.756134,47.423481}, {16.8680057,62.8380003}, {-10.5254434,64.2055996}, {-31.849966,50.8485832}}} id=15
+SkOpAngle::after [5/3] 25/25 tStart=1 tEnd=0 < [6/4] 1/1 tStart=0 tEnd=1 < [15/7] 1/1 tStart=0.999999628 tEnd=1 T 7
+SkOpAngle::afterPart {{{36.756134,47.423481}, {50.8459854,65.6024933}}} id=5
+SkOpAngle::afterPart {{{36.756134,47.423481}, {40.3270988,44.6557693}, {43.5740242,41.493576}, {46.4351768,37.9970322}}} id=6
+SkOpAngle::afterPart {{{36.756134,47.423481}, {36.7561414,47.4234752}, {36.7561533,47.4234753}, {36.7561607,47.4234695}}} id=15
+SkOpSegment::sortAngles [6] tStart=0 [11]
+SkOpSegment::sortAngles [9] tStart=0 [17]
+SkOpSegment::sortAngles [15] tStart=0.999999628 [33]
+SkOpSegment::sortAngles [16] tStart=1 [32]
+SkOpSegment::debugShowActiveSpans id=1 (6.31801322e-006,-60 0,-83) t=0 (6.31801322e-006,-60) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=2 (0,-83 32.0712242,-83 61.2726326,-64.5230865 75.0056381,-35.5408783) t=0 (0,-83) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=3 (75.0056381,-35.5408783 88.7386475,-6.55867052 84.545517,27.7420006 64.2353287,52.562561) t=0 (75.0056381,-35.5408783) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=4 (64.2353287,52.562561 60.2773972,57.3994484 55.7858162,61.773819 50.8459854,65.6024933) t=0 (64.2353287,52.562561) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=5 (50.8459854,65.6024933 36.756134,47.423481) t=0 (50.8459854,65.6024933) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=6 (36.756134,47.423481 40.3270988,44.6557693 43.5740242,41.493576 46.4351768,37.9970322) t=0 (36.756134,47.423481) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=7 (46.4351768,37.9970322 61.1172447,20.0544662 64.1484222,-4.74120331 54.2209473,-25.6921959) t=0 (46.4351768,37.9970322) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=8 (54.2209473,-25.6921959 44.2934723,-46.6431847 23.1840267,-60 6.31801322e-006,-60) t=0 (54.2209473,-25.6921959) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=9 (50.8459854,65.6024933 23.334074,86.9259186 -14.5602179,88.8177719 -44.0591507,70.3405457) t=0 (50.8459854,65.6024933) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=10 (-44.0591507,70.3405457 -73.5580902,51.8633156 -88.3942261,16.9427452 -81.2158127,-17.116993) t=0 (-44.0591507,70.3405457) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=11 (-81.2158127,-17.116993 -74.0374069,-51.1767159 -46.3696136,-77.1391754 -11.9226456,-82.1392059) t=0 (-81.2158127,-17.116993) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=12 (-11.9226456,-82.1392059 -8.61876869,-59.3777466) t=0 (-11.9226456,-82.1392059) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=13 (-8.61876869,-59.3777466 -33.5202026,-55.7632599 -53.5210152,-36.9952087 -58.7102203,-12.3737135) t=0 (-8.61876869,-59.3777466) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=14 (-58.7102203,-12.3737135 -63.8994179,12.2477798 -53.1744957,37.4915581 -31.849966,50.8485832) t=0 (-58.7102203,-12.3737135) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0 (-31.849966,50.8485832) tEnd=0.999999628 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0.999999628 (36.756134,47.423481) tEnd=1 windSum=? windValue=1
+SkOpSegment::debugShowActiveSpans id=16 (36.7561607,47.4234695 50.8459854,65.6024933) t=0 (36.7561607,47.4234695) tEnd=1 windSum=? windValue=1
+SkOpSpan::sortableTop dir=kLeft seg=1 t=0.5 pt=(3.15900661e-006,-71.5)
+SkOpSpan::sortableTop [0] valid=1 operand=1 span=21 ccw=1 seg=11 {{{-81.2158127f, -17.116993f}, {-74.0374069f, -51.1767159f}, {-46.3696136f, -77.1391754f}, {-11.9226456f, -82.1392059f}}} t=0.683500004 pt=(-42.1581116,-71.5) slope=(86.347103,-50.9415461)
+SkOpSpan::sortableTop [1] valid=1 operand=1 span=23 ccw=0 seg=12 {{{-11.9226456f, -82.1392059f}, {-8.61876869f, -59.3777466f}}} t=0.46742196 pt=(-10.3783407,-71.5) slope=(3.30387688,22.7614594)
+SkOpSpan::sortableTop [2] valid=1 operand=0 span=1 ccw=1 seg=1 {{{6.31801322e-006f, -60}, {0, -83}}} t=0.5 pt=(3.15900661e-006,-71.5) slope=(-6.31801322e-006,-23)
+SkOpSegment::markWinding id=11 (-81.2158127,-17.116993 -74.0374069,-51.1767159 -46.3696136,-77.1391754 -11.9226456,-82.1392059) t=0 [21] (-81.2158127,-17.116993) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=12 (-11.9226456,-82.1392059 -8.61876869,-59.3777466) t=0 [23] (-11.9226456,-82.1392059) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=13 (-8.61876869,-59.3777466 -33.5202026,-55.7632599 -53.5210152,-36.9952087 -58.7102203,-12.3737135) t=0 [25] (-8.61876869,-59.3777466) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=14 (-58.7102203,-12.3737135 -63.8994179,12.2477798 -53.1744957,37.4915581 -31.849966,50.8485832) t=0 [27] (-58.7102203,-12.3737135) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0 [29] (-31.849966,50.8485832) tEnd=0.999999628 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=11 (-81.2158127,-17.116993 -74.0374069,-51.1767159 -46.3696136,-77.1391754 -11.9226456,-82.1392059) t=0 [21] (-81.2158127,-17.116993) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=10 (-44.0591507,70.3405457 -73.5580902,51.8633156 -88.3942261,16.9427452 -81.2158127,-17.116993) t=0 [19] (-44.0591507,70.3405457) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=9 (50.8459854,65.6024933 23.334074,86.9259186 -14.5602179,88.8177719 -44.0591507,70.3405457) t=0 [17] (50.8459854,65.6024933) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=1 (6.31801322e-006,-60 0,-83) t=0 [1] (6.31801322e-006,-60) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=2 (0,-83 32.0712242,-83 61.2726326,-64.5230865 75.0056381,-35.5408783) t=0 [3] (0,-83) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=3 (75.0056381,-35.5408783 88.7386475,-6.55867052 84.545517,27.7420006 64.2353287,52.562561) t=0 [5] (75.0056381,-35.5408783) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (64.2353287,52.562561 60.2773972,57.3994484 55.7858162,61.773819 50.8459854,65.6024933) t=0 [7] (64.2353287,52.562561) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=1 (6.31801322e-006,-60 0,-83) t=0 [1] (6.31801322e-006,-60) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markWinding id=8 (54.2209473,-25.6921959 44.2934723,-46.6431847 23.1840267,-60 6.31801322e-006,-60) t=0 [15] (54.2209473,-25.6921959) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=7 (46.4351768,37.9970322 61.1172447,20.0544662 64.1484222,-4.74120331 54.2209473,-25.6921959) t=0 [13] (46.4351768,37.9970322) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=6 (36.756134,47.423481 40.3270988,44.6557693 43.5740242,41.493576 46.4351768,37.9970322) t=0 [11] (36.756134,47.423481) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? 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 (6.31801322e-006,-60 0,-83) t=0 [1] (6.31801322e-006,-60) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=1 from=(0,-83) to=(6.31801322e-006,-60)
+SkOpSegment::findNextOp simple
+SkOpSegment::markDone id=8 (54.2209473,-25.6921959 44.2934723,-46.6431847 23.1840267,-60 6.31801322e-006,-60) t=0 [15] (54.2209473,-25.6921959) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=8 from=(6.31801322e-006,-60) to=(54.2209473,-25.6921959)
+path.moveTo(0,-83);
+path.lineTo(6.31801322e-006,-60);
+path.cubicTo(23.1840267,-60, 44.2934723,-46.6431847, 54.2209473,-25.6921959);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDone id=7 (46.4351768,37.9970322 61.1172447,20.0544662 64.1484222,-4.74120331 54.2209473,-25.6921959) t=0 [13] (46.4351768,37.9970322) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=7 from=(54.2209473,-25.6921959) to=(46.4351768,37.9970322)
+path.cubicTo(64.1484222,-4.74120331, 61.1172447,20.0544662, 46.4351768,37.9970322);
+SkOpSegment::markWinding id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0.999999628 [33] (36.756134,47.423481) tEnd=1 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=16 (36.7561607,47.4234695 50.8459854,65.6024933) t=0 [31] (36.7561607,47.4234695) tEnd=1 newWindSum=1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=16 span=32
+SkOpSegment::markWinding id=5 (50.8459854,65.6024933 36.756134,47.423481) t=0 [9] (50.8459854,65.6024933) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last segment=5 span=9 windSum=-1
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [6/4] next=15/7 sect=1/1 s=0 [11] e=1 [12] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [15/7] next=15/6 sect=1/1 s=0.999999628 [33] e=1 [30] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [15/6] next=5/3 sect=17/17 s=0.999999628 [33] e=0 [29] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [5/3] next=6/4 sect=25/25 s=1 [10] e=0 [9] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpSegment::activeOp id=15 t=0.999999628 tEnd=1 op=union miFrom=0 miTo=0 suFrom=0 suTo=1 result=1
+SkOpSegment::findNextOp chase.append segment=16 span=32
+SkOpSegment::activeOp id=15 t=0.999999628 tEnd=0 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::activeOp id=5 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
+SkOpSegment::findNextOp chase.append segment=5 span=9 windSum=-1
+SkOpSegment::markDone id=6 (36.756134,47.423481 40.3270988,44.6557693 43.5740242,41.493576 46.4351768,37.9970322) t=0 [11] (36.756134,47.423481) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[6] to:[15] start=13585868 end=13585412
+bridgeOp current id=6 from=(46.4351768,37.9970322) to=(36.756134,47.423481)
+path.cubicTo(43.5740242,41.493576, 40.3270988,44.6557693, 36.756134,47.423481);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDone id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0.999999628 [33] (36.756134,47.423481) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+bridgeOp current id=15 from=(36.756134,47.423481) to=(36.7561607,47.4234695)
+path.cubicTo(36.7561417,47.4234734, 36.7561531,47.4234772, 36.7561607,47.4234695);
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [16/8] next=5/2 sect=9/9 s=1 [32] e=0 [31] sgn=1 windVal=1 windSum=1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [5/2] next=9/5 sect=9/9 s=0 [9] e=1 [10] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [9/5] next=4/1 sect=17/17 s=0 [17] e=1 [18] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [4/1] next=16/8 sect=1/1 s=1 [8] e=0 [7] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0
+SkOpSegment::activeOp id=5 t=0 tEnd=1 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
+SkOpSegment::activeOp id=9 t=0 tEnd=1 op=union miFrom=1 miTo=1 suFrom=0 suTo=1 result=0
+SkOpSegment::markDone id=9 (50.8459854,65.6024933 23.334074,86.9259186 -14.5602179,88.8177719 -44.0591507,70.3405457) t=0 [17] (50.8459854,65.6024933) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=10 (-44.0591507,70.3405457 -73.5580902,51.8633156 -88.3942261,16.9427452 -81.2158127,-17.116993) t=0 [19] (-44.0591507,70.3405457) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=11 (-81.2158127,-17.116993 -74.0374069,-51.1767159 -46.3696136,-77.1391754 -11.9226456,-82.1392059) t=0 [21] (-81.2158127,-17.116993) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=12 (-11.9226456,-82.1392059 -8.61876869,-59.3777466) t=0 [23] (-11.9226456,-82.1392059) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=13 (-8.61876869,-59.3777466 -33.5202026,-55.7632599 -53.5210152,-36.9952087 -58.7102203,-12.3737135) t=0 [25] (-8.61876869,-59.3777466) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=14 (-58.7102203,-12.3737135 -63.8994179,12.2477798 -53.1744957,37.4915581 -31.849966,50.8485832) t=0 [27] (-58.7102203,-12.3737135) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=15 (-31.849966,50.8485832 -10.5254354,64.2056046 16.8680305,62.8380051 36.7561607,47.4234695) t=0 [29] (-31.849966,50.8485832) tEnd=0.999999628 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=4 t=1 tEnd=0 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::markDone id=4 (64.2353287,52.562561 60.2773972,57.3994484 55.7858162,61.773819 50.8459854,65.6024933) t=0 [7] (64.2353287,52.562561) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=3 (75.0056381,-35.5408783 88.7386475,-6.55867052 84.545517,27.7420006 64.2353287,52.562561) t=0 [5] (75.0056381,-35.5408783) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=2 (0,-83 32.0712242,-83 61.2726326,-64.5230865 75.0056381,-35.5408783) t=0 [3] (0,-83) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDone id=16 (36.7561607,47.4234695 50.8459854,65.6024933) t=0 [31] (36.7561607,47.4234695) tEnd=1 newWindSum=1 newOppSum=0 oppSum=0 windSum=1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[16] to:[5] start=13582764 end=13582868
+bridgeOp current id=16 from=(36.7561607,47.4234695) to=(50.8459854,65.6024933)
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [5/3] next=6/4 sect=25/25 s=1 [10] e=0 [9] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [6/4] next=15/7 sect=1/1 s=0 [11] e=1 [12] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done
+SkOpAngle::dumpOne [15/7] next=15/6 sect=1/1 s=0.999999628 [33] e=1 [30] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=0 done operand
+SkOpAngle::dumpOne [15/6] next=5/3 sect=17/17 s=0.999999628 [33] e=0 [29] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 done operand
+SkOpSegment::activeOp id=6 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::activeOp id=15 t=0.999999628 tEnd=1 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::activeOp id=15 t=0.999999628 tEnd=0 op=union miFrom=0 miTo=0 suFrom=0 suTo=1 result=1
+SkOpSegment::markDone id=5 (50.8459854,65.6024933 36.756134,47.423481) t=0 [9] (50.8459854,65.6024933) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[5] to:[15] start=13585868 end=13585412
+bridgeOp current id=5 from=(50.8459854,65.6024933) to=(36.756134,47.423481)
+path.lineTo(50.8459854,65.6024933);
+path.lineTo(36.756134,47.423481);
</div>
-
</div>
<script type="text/javascript">
var testDivs = [
- builder3838_3,
+ battleOp183,
];
var decimal_places = 3; // make this 3 to show more precision