| Index: src/pathops/SkOpCoincidence.cpp
|
| diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
|
| index 831ce71190054113c66e0b5abe9aa647e95b9236..61ea3ef4447f4ab2a84890ac3044a15008e7b506 100755
|
| --- a/src/pathops/SkOpCoincidence.cpp
|
| +++ b/src/pathops/SkOpCoincidence.cpp
|
| @@ -308,7 +308,7 @@ bool SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase*
|
| SkOpSegment* coinSeg = base->segment();
|
| SkOpSegment* oppSeg = oppStart->segment();
|
| double coinTs, coinTe, oppTs, oppTe;
|
| - if (coinSeg < oppSeg) {
|
| + if (Ordered(coinSeg, oppSeg)) {
|
| coinTs = base->t();
|
| coinTe = testSpan->t();
|
| oppTs = oppStart->fT;
|
| @@ -416,6 +416,8 @@ bool SkOpCoincidence::addExpanded() {
|
| do {
|
| const SkOpPtT* startPtT = coin->coinPtTStart();
|
| const SkOpPtT* oStartPtT = coin->oppPtTStart();
|
| + double priorT = startPtT->fT;
|
| + double oPriorT = oStartPtT->fT;
|
| SkASSERT(startPtT->contains(oStartPtT));
|
| SkOPASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
|
| const SkOpSpanBase* start = startPtT->span();
|
| @@ -427,23 +429,47 @@ bool SkOpCoincidence::addExpanded() {
|
| const SkOpSpanBase* test = start->upCast()->next();
|
| const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
|
| FAIL_IF(!oTest);
|
| + SkOpSegment* seg = start->segment();
|
| + SkOpSegment* oSeg = oStart->segment();
|
| while (test != end || oTest != oEnd) {
|
| - if (!test->ptT()->contains(oStart->segment())
|
| - || !oTest->ptT()->contains(start->segment())) {
|
| + const SkOpPtT* containedOpp = test->ptT()->contains(oSeg);
|
| + const SkOpPtT* containedThis = oTest->ptT()->contains(seg);
|
| + if (!containedOpp || !containedThis) {
|
| + // choose the ends, or the first common pt-t list shared by both
|
| + double nextT, oNextT;
|
| + if (containedOpp) {
|
| + nextT = test->t();
|
| + oNextT = containedOpp->fT;
|
| + } else if (containedThis) {
|
| + nextT = containedThis->fT;
|
| + oNextT = oTest->t();
|
| + } else {
|
| + // iterate through until a pt-t list found that contains the other
|
| + const SkOpSpanBase* walk = test;
|
| + const SkOpPtT* walkOpp;
|
| + do {
|
| + FAIL_IF(!walk->upCastable());
|
| + walk = walk->upCast()->next();
|
| + } while (!(walkOpp = walk->ptT()->contains(oSeg))
|
| + && walk != coin->coinPtTEnd()->span());
|
| + nextT = walk->t();
|
| + oNextT = walkOpp->fT;
|
| + }
|
| // use t ranges to guess which one is missing
|
| - double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
|
| + double startRange = nextT - priorT;
|
| FAIL_IF(!startRange);
|
| - double startPart = (test->t() - startPtT->fT) / startRange;
|
| - double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
|
| + double startPart = (test->t() - priorT) / startRange;
|
| + double oStartRange = oNextT - oPriorT;
|
| FAIL_IF(!oStartRange);
|
| - double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
|
| + double oStartPart = (oTest->t() - oPriorT) / oStartRange;
|
| FAIL_IF(startPart == oStartPart);
|
| + bool addToOpp = !containedOpp && !containedThis ? startPart < oStartPart
|
| + : !!containedThis;
|
| bool startOver = false;
|
| - bool success = startPart < oStartPart
|
| - ? oStart->segment()->addExpanded(
|
| - oStartPtT->fT + oStartRange * startPart, test, &startOver)
|
| - : start->segment()->addExpanded(
|
| - startPtT->fT + startRange * oStartPart, oTest, &startOver);
|
| + bool success = addToOpp ? oSeg->addExpanded(
|
| + oPriorT + oStartRange * startPart, test, &startOver)
|
| + : seg->addExpanded(
|
| + priorT + startRange * oStartPart, oTest, &startOver);
|
| FAIL_IF(!success);
|
| if (startOver) {
|
| test = start;
|
| @@ -454,12 +480,15 @@ bool SkOpCoincidence::addExpanded() {
|
| }
|
| if (test != end) {
|
| FAIL_IF(!test->upCastable());
|
| + priorT = test->t();
|
| test = test->upCast()->next();
|
| }
|
| if (oTest != oEnd) {
|
| + oPriorT = oTest->t();
|
| oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
|
| FAIL_IF(!oTest);
|
| }
|
| +
|
| }
|
| } while ((coin = coin->next()));
|
| return true;
|
| @@ -507,16 +536,40 @@ bool SkOpCoincidence::addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over
|
| }
|
|
|
| // given a t span, map the same range on the coincident span
|
| -void SkOpCoincidence::TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
|
| - double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs,
|
| - double* coinTe) {
|
| - double denom = overE->fT - overS->fT;
|
| - double start = 0 < denom ? tStart : tEnd;
|
| - double end = 0 < denom ? tEnd : tStart;
|
| - double sRatio = (start - overS->fT) / denom;
|
| - double eRatio = (end - overS->fT) / denom;
|
| - *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
|
| - *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
|
| +/*
|
| +the curves may not scale linearly, so interpolation may only happen within known points
|
| +remap over1s, over1e, cointPtTStart, coinPtTEnd to smallest range that captures over1s
|
| +then repeat to capture over1e
|
| +*/
|
| +double SkOpCoincidence::TRange(const SkOpPtT* overS, double t,
|
| + const SkOpSegment* coinSeg SkDEBUGPARAMS(const SkOpPtT* overE)) {
|
| + const SkOpSpanBase* work = overS->span();
|
| + const SkOpPtT* foundStart = nullptr;
|
| + const SkOpPtT* foundEnd = nullptr;
|
| + const SkOpPtT* coinStart = nullptr;
|
| + const SkOpPtT* coinEnd = nullptr;
|
| + do {
|
| + const SkOpPtT* contained = work->contains(coinSeg);
|
| + if (!contained) {
|
| + continue;
|
| + }
|
| + if (work->t() <= t) {
|
| + coinStart = contained;
|
| + foundStart = work->ptT();
|
| + }
|
| + if (work->t() >= t) {
|
| + coinEnd = contained;
|
| + foundEnd = work->ptT();
|
| + break;
|
| + }
|
| + SkASSERT(work->ptT() != overE);
|
| + } while ((work = work->upCast()->next()));
|
| + SkASSERT(coinStart);
|
| + SkASSERT(coinEnd);
|
| + // while overS->fT <=t and overS contains coinSeg
|
| + double denom = foundEnd->fT - foundStart->fT;
|
| + double sRatio = denom ? (t - foundStart->fT) / denom : 1;
|
| + return coinStart->fT + (coinEnd->fT - coinStart->fT) * sRatio;
|
| }
|
|
|
| // return true if span overlaps existing and needs to adjust the coincident list
|
| @@ -568,36 +621,40 @@ bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check,
|
| }
|
|
|
| /* Please keep this in sync with debugAddIfMissing() */
|
| -bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
|
| - const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
|
| - SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
| - SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
|
| - SkOpSegment* coinSeg = coinPtTStart->segment();
|
| - SkOpSegment* oppSeg = oppPtTStart->segment();
|
| - if (coinSeg == oppSeg) {
|
| - return false;
|
| - }
|
| +// note that over1s, over1e, over2s, over2e are ordered
|
| +bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over2s,
|
| + double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg
|
| + SkDEBUGPARAMS(const SkOpPtT* over1e) SkDEBUGPARAMS(const SkOpPtT* over2e)) {
|
| + SkASSERT(tStart < tEnd);
|
| + SkASSERT(over1s->fT < over1e->fT);
|
| + SkASSERT(between(over1s->fT, tStart, over1e->fT));
|
| + SkASSERT(between(over1s->fT, tEnd, over1e->fT));
|
| + SkASSERT(over2s->fT < over2e->fT);
|
| + SkASSERT(between(over2s->fT, tStart, over2e->fT));
|
| + SkASSERT(between(over2s->fT, tEnd, over2e->fT));
|
| + SkASSERT(over1s->segment() == over1e->segment());
|
| + SkASSERT(over2s->segment() == over2e->segment());
|
| + SkASSERT(over1s->segment() == over2s->segment());
|
| + SkASSERT(over1s->segment() != coinSeg);
|
| + SkASSERT(over1s->segment() != oppSeg);
|
| + SkASSERT(coinSeg != oppSeg);
|
| double coinTs, coinTe, oppTs, oppTe;
|
| - TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
|
| + coinTs = TRange(over1s, tStart, coinSeg SkDEBUGPARAMS(over1e));
|
| + coinTe = TRange(over1s, tEnd, coinSeg SkDEBUGPARAMS(over1e));
|
| if (coinSeg->collapsed(coinTs, coinTe)) {
|
| return false;
|
| }
|
| - TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
|
| + oppTs = TRange(over2s, tStart, oppSeg SkDEBUGPARAMS(over2e));
|
| + oppTe = TRange(over2s, tEnd, oppSeg SkDEBUGPARAMS(over2e));
|
| if (oppSeg->collapsed(oppTs, oppTe)) {
|
| return false;
|
| }
|
| - bool swap = coinTs > coinTe;
|
| - if (swap) {
|
| + if (coinTs > coinTe) {
|
| SkTSwap(coinTs, coinTe);
|
| - }
|
| - if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
|
| - SkTSwap(oppTs, oppTe);
|
| - }
|
| - if (swap) {
|
| SkTSwap(oppTs, oppTe);
|
| }
|
| return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe
|
| - SkDEBUGPARAMS(false) /* don't assert if addOrOverlap fails */ );
|
| + SkDEBUGPARAMS(false) /* don't assert if addOrOverlap fails */ );
|
| }
|
|
|
| /* Please keep this in sync with debugAddOrOverlap() */
|
| @@ -733,55 +790,75 @@ bool SkOpCoincidence::addMissing() {
|
| // addifmissing can modify the list that this is walking
|
| // save head so that walker can iterate over old data unperturbed
|
| // addifmissing adds to head freely then add saved head in the end
|
| - const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
|
| - const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
|
| - if (outerCoin->done() || outerOpp->done()) {
|
| - continue;
|
| - }
|
| + const SkOpPtT* ocs = outer->coinPtTStart();
|
| + SkASSERT(!ocs->deleted());
|
| + const SkOpSegment* outerCoin = ocs->segment();
|
| + SkASSERT(!outerCoin->done()); // if it's done, should have already been removed from list
|
| + const SkOpPtT* oos = outer->oppPtTStart();
|
| + SkASSERT(!oos->deleted());
|
| + const SkOpSegment* outerOpp = oos->segment();
|
| + SkASSERT(!outerOpp->done());
|
| + SkOpSegment* outerCoinWritable = const_cast<SkOpSegment*>(outerCoin);
|
| + SkOpSegment* outerOppWritable = const_cast<SkOpSegment*>(outerOpp);
|
| SkCoincidentSpans* inner = outer;
|
| while ((inner = inner->next())) {
|
| this->debugValidate();
|
| double overS, overE;
|
| - const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
|
| - const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
|
| - if (innerCoin->done() || innerOpp->done()) {
|
| - continue;
|
| - }
|
| + const SkOpPtT* ics = inner->coinPtTStart();
|
| + SkASSERT(!ics->deleted());
|
| + const SkOpSegment* innerCoin = ics->segment();
|
| + SkASSERT(!innerCoin->done());
|
| + const SkOpPtT* ios = inner->oppPtTStart();
|
| + SkASSERT(!ios->deleted());
|
| + const SkOpSegment* innerOpp = ios->segment();
|
| + SkASSERT(!innerOpp->done());
|
| + SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
|
| + SkOpSegment* innerOppWritable = const_cast<SkOpSegment*>(innerOpp);
|
| if (outerCoin == innerCoin) {
|
| - if (outerOpp != innerOpp
|
| - && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
|
| - added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
|
| - outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd());
|
| + const SkOpPtT* oce = outer->coinPtTEnd();
|
| + SkASSERT(!oce->deleted());
|
| + const SkOpPtT* ice = inner->coinPtTEnd();
|
| + SkASSERT(!ice->deleted());
|
| + if (outerOpp != innerOpp && this->overlap(ocs, oce, ics, ice, &overS, &overE)) {
|
| + added |= this->addIfMissing(ocs->starter(oce), ics->starter(ice),
|
| + overS, overE, outerOppWritable, innerOppWritable
|
| + SkDEBUGPARAMS(ocs->debugEnder(oce))
|
| + SkDEBUGPARAMS(ics->debugEnder(ice)));
|
| }
|
| } else if (outerCoin == innerOpp) {
|
| - if (outerOpp != innerCoin
|
| - && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
|
| - added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
|
| - outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd());
|
| + const SkOpPtT* oce = outer->coinPtTEnd();
|
| + SkASSERT(!oce->deleted());
|
| + const SkOpPtT* ioe = inner->oppPtTEnd();
|
| + SkASSERT(!ioe->deleted());
|
| + if (outerOpp != innerCoin && this->overlap(ocs, oce, ios, ioe, &overS, &overE)) {
|
| + added |= this->addIfMissing(ocs->starter(oce), ios->starter(ioe),
|
| + overS, overE, outerOppWritable, innerCoinWritable
|
| + SkDEBUGPARAMS(ocs->debugEnder(oce))
|
| + SkDEBUGPARAMS(ios->debugEnder(ioe)));
|
| }
|
| } else if (outerOpp == innerCoin) {
|
| + const SkOpPtT* ooe = outer->oppPtTEnd();
|
| + SkASSERT(!ooe->deleted());
|
| + const SkOpPtT* ice = inner->coinPtTEnd();
|
| + SkASSERT(!ice->deleted());
|
| SkASSERT(outerCoin != innerOpp);
|
| - if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
|
| - added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
|
| - outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd());
|
| + if (this->overlap(oos, ooe, ics, ice, &overS, &overE)) {
|
| + added |= this->addIfMissing(oos->starter(ooe), ics->starter(ice),
|
| + overS, overE, outerCoinWritable, innerOppWritable
|
| + SkDEBUGPARAMS(oos->debugEnder(ooe))
|
| + SkDEBUGPARAMS(ics->debugEnder(ice)));
|
| }
|
| } else if (outerOpp == innerOpp) {
|
| + const SkOpPtT* ooe = outer->oppPtTEnd();
|
| + SkASSERT(!ooe->deleted());
|
| + const SkOpPtT* ioe = inner->oppPtTEnd();
|
| + SkASSERT(!ioe->deleted());
|
| SkASSERT(outerCoin != innerCoin);
|
| - if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
|
| - added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
|
| - inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
|
| - outer->coinPtTStart(), outer->coinPtTEnd(),
|
| - inner->coinPtTStart(), inner->coinPtTEnd());
|
| + if (this->overlap(oos, ooe, ios, ioe, &overS, &overE)) {
|
| + added |= this->addIfMissing(oos->starter(ooe), ios->starter(ioe),
|
| + overS, overE, outerCoinWritable, innerCoinWritable
|
| + SkDEBUGPARAMS(oos->debugEnder(ooe))
|
| + SkDEBUGPARAMS(ios->debugEnder(ioe)));
|
| }
|
| }
|
| this->debugValidate();
|
|
|