Index: src/pathops/SkOpContour.cpp |
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp |
index e3137b756c899cb1663801c59d00259504fc8eb4..5ef702d4c10cb462d66130918090e53ace7467e5 100644 |
--- a/src/pathops/SkOpContour.cpp |
+++ b/src/pathops/SkOpContour.cpp |
@@ -28,8 +28,14 @@ bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, |
coincidence.fTs[swap][1] = ts[0][1]; |
coincidence.fTs[!swap][0] = ts[1][0]; |
coincidence.fTs[!swap][1] = ts[1][1]; |
- coincidence.fPts[0] = pt0; |
- coincidence.fPts[1] = pt1; |
+ coincidence.fPts[swap][0] = pt0; |
+ coincidence.fPts[swap][1] = pt1; |
+ bool nearStart = ts.nearlySame(0); |
+ bool nearEnd = ts.nearlySame(1); |
+ coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0; |
+ coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1; |
+ coincidence.fNearly[0] = nearStart; |
+ coincidence.fNearly[1] = nearEnd; |
return true; |
} |
@@ -93,28 +99,31 @@ void SkOpContour::addCoincidentPoints() { |
cancelers ^= true; |
} |
SkASSERT(!approximately_negative(oEndT - oStartT)); |
+ const SkPoint& startPt = coincidence.fPts[0][startSwapped]; |
if (cancelers) { |
// make sure startT and endT have t entries |
- const SkPoint& startPt = coincidence.fPts[startSwapped]; |
if (startT > 0 || oEndT < 1 |
|| thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) { |
- thisOne.addTPair(startT, &other, oEndT, true, startPt); |
+ thisOne.addTPair(startT, &other, oEndT, true, startPt, |
+ coincidence.fPts[1][startSwapped]); |
} |
- const SkPoint& oStartPt = coincidence.fPts[oStartSwapped]; |
+ const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped]; |
if (oStartT > 0 || endT < 1 |
|| thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) { |
- other.addTPair(oStartT, &thisOne, endT, true, oStartPt); |
+ other.addTPair(oStartT, &thisOne, endT, true, oStartPt, |
+ coincidence.fPts[0][oStartSwapped]); |
} |
} else { |
- const SkPoint& startPt = coincidence.fPts[startSwapped]; |
if (startT > 0 || oStartT > 0 |
|| thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) { |
- thisOne.addTPair(startT, &other, oStartT, true, startPt); |
+ thisOne.addTPair(startT, &other, oStartT, true, startPt, |
+ coincidence.fPts[1][startSwapped]); |
} |
- const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped]; |
+ const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped]; |
if (endT < 1 || oEndT < 1 |
|| thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) { |
- other.addTPair(oEndT, &thisOne, endT, true, oEndPt); |
+ other.addTPair(oEndT, &thisOne, endT, true, oEndPt, |
+ coincidence.fPts[0][!oStartSwapped]); |
} |
} |
#if DEBUG_CONCIDENT |
@@ -122,6 +131,32 @@ void SkOpContour::addCoincidentPoints() { |
other.debugShowTs("o"); |
#endif |
} |
+ // if there are multiple pairs of coincidence that share an edge, see if the opposite |
+ // are also coincident |
+ for (int index = 0; index < count - 1; ++index) { |
+ const SkCoincidence& coincidence = fCoincidences[index]; |
+ int thisIndex = coincidence.fSegments[0]; |
+ SkOpContour* otherContour = coincidence.fOther; |
+ int otherIndex = coincidence.fSegments[1]; |
+ for (int idx2 = 1; idx2 < count; ++idx2) { |
+ const SkCoincidence& innerCoin = fCoincidences[idx2]; |
+ int innerThisIndex = innerCoin.fSegments[0]; |
+ if (thisIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, false); |
+ } |
+ if (this == otherContour && otherIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, false); |
+ } |
+ SkOpContour* innerOtherContour = innerCoin.fOther; |
+ innerThisIndex = innerCoin.fSegments[1]; |
+ if (this == innerOtherContour && thisIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, false); |
+ } |
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, false); |
+ } |
+ } |
+ } |
} |
bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex, |
@@ -143,11 +178,77 @@ bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherI |
coincidence.fTs[swap][1] = ts[0][ptIndex + 1]; |
coincidence.fTs[!swap][0] = ts[1][ptIndex]; |
coincidence.fTs[!swap][1] = ts[1][ptIndex + 1]; |
- coincidence.fPts[0] = pt0; |
- coincidence.fPts[1] = pt1; |
+ coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0; |
+ coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1; |
+ coincidence.fNearly[0] = 0; |
+ coincidence.fNearly[1] = 0; |
return true; |
} |
+void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap, |
+ SkCoincidence* coincidence) { |
+ for (int idx2 = 0; idx2 < 2; ++idx2) { |
+ if (coincidence->fPts[0][idx2] == aligned.fOldPt |
+ && coincidence->fTs[swap][idx2] == aligned.fOldT) { |
+ SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt)); |
+ coincidence->fPts[0][idx2] = aligned.fPt; |
+ SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT)); |
+ coincidence->fTs[swap][idx2] = aligned.fT; |
+ } |
+ } |
+} |
+ |
+void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned, |
+ SkTArray<SkCoincidence, true>* coincidences) { |
+ int count = coincidences->count(); |
+ for (int index = 0; index < count; ++index) { |
+ SkCoincidence& coincidence = (*coincidences)[index]; |
+ int thisIndex = coincidence.fSegments[0]; |
+ const SkOpSegment* thisOne = &fSegments[thisIndex]; |
+ const SkOpContour* otherContour = coincidence.fOther; |
+ int otherIndex = coincidence.fSegments[1]; |
+ const SkOpSegment* other = &otherContour->fSegments[otherIndex]; |
+ if (thisOne == aligned.fOther1 && other == aligned.fOther2) { |
+ align(aligned, false, &coincidence); |
+ } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) { |
+ align(aligned, true, &coincidence); |
+ } |
+ } |
+} |
+ |
+void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex, |
+ bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const { |
+ int zeroPt; |
+ if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) { |
+ alignPt(segmentIndex, point, zeroPt); |
+ } |
+ if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) { |
+ other->alignPt(otherIndex, point, zeroPt); |
+ } |
+} |
+ |
+void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const { |
+ const SkOpSegment& segment = fSegments[index]; |
+ if (0 == zeroPt) { |
+ *point = segment.pts()[0]; |
+ } else { |
+ *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())]; |
+ } |
+} |
+ |
+int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const { |
+ double tVal = (*ts)[swap][tIndex]; |
+ if (tVal != 0 && precisely_zero(tVal)) { |
+ ts->set(swap, tIndex, 0); |
+ return 0; |
+ } |
+ if (tVal != 1 && precisely_equal(tVal, 1)) { |
+ ts->set(swap, tIndex, 1); |
+ return 1; |
+ } |
+ return -1; |
+} |
+ |
bool SkOpContour::calcAngles() { |
int segmentCount = fSegments.count(); |
for (int test = 0; test < segmentCount; ++test) { |
@@ -180,7 +281,187 @@ void SkOpContour::calcPartialCoincidentWinding() { |
#endif |
for (int index = 0; index < count; ++index) { |
SkCoincidence& coincidence = fPartialCoincidences[index]; |
- calcCommonCoincidentWinding(coincidence); |
+ calcCommonCoincidentWinding(coincidence); |
+ } |
+ // if there are multiple pairs of partial coincidence that share an edge, see if the opposite |
+ // are also coincident |
+ for (int index = 0; index < count - 1; ++index) { |
+ const SkCoincidence& coincidence = fPartialCoincidences[index]; |
+ int thisIndex = coincidence.fSegments[0]; |
+ SkOpContour* otherContour = coincidence.fOther; |
+ int otherIndex = coincidence.fSegments[1]; |
+ for (int idx2 = 1; idx2 < count; ++idx2) { |
+ const SkCoincidence& innerCoin = fPartialCoincidences[idx2]; |
+ int innerThisIndex = innerCoin.fSegments[0]; |
+ if (thisIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, true); |
+ } |
+ if (this == otherContour && otherIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, true); |
+ } |
+ SkOpContour* innerOtherContour = innerCoin.fOther; |
+ innerThisIndex = innerCoin.fSegments[1]; |
+ if (this == innerOtherContour && thisIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, true); |
+ } |
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) { |
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, true); |
+ } |
+ } |
+ } |
+} |
+ |
+void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx, |
+ const SkCoincidence& twoCoin, int twoIdx, bool partial) { |
+ SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther)); |
+ SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]); |
+ // look for common overlap |
+ double min = SK_ScalarMax; |
+ double max = SK_ScalarMin; |
+ double min1 = oneCoin.fTs[!oneIdx][0]; |
+ double max1 = oneCoin.fTs[!oneIdx][1]; |
+ double min2 = twoCoin.fTs[!twoIdx][0]; |
+ double max2 = twoCoin.fTs[!twoIdx][1]; |
+ bool cancelers = (min1 < max1) != (min2 < max2); |
+ if (min1 > max1) { |
+ SkTSwap(min1, max1); |
+ } |
+ if (min2 > max2) { |
+ SkTSwap(min2, max2); |
+ } |
+ if (between(min1, min2, max1)) { |
+ min = min2; |
+ } |
+ if (between(min1, max2, max1)) { |
+ max = max2; |
+ } |
+ if (between(min2, min1, max2)) { |
+ min = SkTMin(min, min1); |
+ } |
+ if (between(min2, max1, max2)) { |
+ max = SkTMax(max, max1); |
+ } |
+ if (min >= max) { |
+ return; // no overlap |
+ } |
+ // look to see if opposite are different segments |
+ int seg1Index = oneCoin.fSegments[oneIdx]; |
+ int seg2Index = twoCoin.fSegments[twoIdx]; |
+ if (seg1Index == seg2Index) { |
+ return; |
+ } |
+ SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this; |
+ SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this; |
+ SkOpSegment* segment1 = &contour1->fSegments[seg1Index]; |
+ SkOpSegment* segment2 = &contour2->fSegments[seg2Index]; |
+ // find opposite t value ranges corresponding to reference min/max range |
+ const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther; |
+ const int refSegIndex = oneCoin.fSegments[!oneIdx]; |
+ const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex]; |
+ int seg1Start = segment1->findOtherT(min, refSegment); |
+ int seg1End = segment1->findOtherT(max, refSegment); |
+ int seg2Start = segment2->findOtherT(min, refSegment); |
+ int seg2End = segment2->findOtherT(max, refSegment); |
+ // if the opposite pairs already contain min/max, we're done |
+ if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) { |
+ return; |
+ } |
+ double loEnd = SkTMin(min1, min2); |
+ double hiEnd = SkTMax(max1, max2); |
+ // insert the missing coincident point(s) |
+ double missingT1 = -1; |
+ double otherT1 = -1; |
+ if (seg1Start < 0) { |
+ if (seg2Start < 0) { |
+ return; |
+ } |
+ missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd, |
+ segment2, seg1End); |
+ if (missingT1 < 0) { |
+ return; |
+ } |
+ const SkOpSpan* missingSpan = &segment2->span(seg2Start); |
+ otherT1 = missingSpan->fT; |
+ } else if (seg2Start < 0) { |
+ SkASSERT(seg1Start >= 0); |
+ missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd, |
+ segment1, seg2End); |
+ if (missingT1 < 0) { |
+ return; |
+ } |
+ const SkOpSpan* missingSpan = &segment1->span(seg1Start); |
+ otherT1 = missingSpan->fT; |
+ } |
+ SkPoint missingPt1; |
+ SkOpSegment* addTo1 = NULL; |
+ SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1; |
+ int minTIndex = refSegment->findExactT(min, addOther1); |
+ SkASSERT(minTIndex >= 0); |
+ if (missingT1 >= 0) { |
+ missingPt1 = refSegment->span(minTIndex).fPt; |
+ addTo1 = seg1Start < 0 ? segment1 : segment2; |
+ } |
+ double missingT2 = -1; |
+ double otherT2 = -1; |
+ if (seg1End < 0) { |
+ if (seg2End < 0) { |
+ return; |
+ } |
+ missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd, |
+ segment2, seg1Start); |
+ if (missingT2 < 0) { |
+ return; |
+ } |
+ const SkOpSpan* missingSpan = &segment2->span(seg2End); |
+ otherT2 = missingSpan->fT; |
+ } else if (seg2End < 0) { |
+ SkASSERT(seg1End >= 0); |
+ missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd, |
+ segment1, seg2Start); |
+ if (missingT2 < 0) { |
+ return; |
+ } |
+ const SkOpSpan* missingSpan = &segment1->span(seg1End); |
+ otherT2 = missingSpan->fT; |
+ } |
+ SkPoint missingPt2; |
+ SkOpSegment* addTo2 = NULL; |
+ SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1; |
+ int maxTIndex = refSegment->findExactT(max, addOther2); |
+ SkASSERT(maxTIndex >= 0); |
+ if (missingT2 >= 0) { |
+ missingPt2 = refSegment->span(maxTIndex).fPt; |
+ addTo2 = seg1End < 0 ? segment1 : segment2; |
+ } |
+ if (missingT1 >= 0) { |
+ addTo1->pinT(missingPt1, &missingT1); |
+ addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1); |
+ } else { |
+ SkASSERT(minTIndex >= 0); |
+ missingPt1 = refSegment->span(minTIndex).fPt; |
+ } |
+ if (missingT2 >= 0) { |
+ addTo2->pinT(missingPt2, &missingT2); |
+ addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2); |
+ } else { |
+ SkASSERT(minTIndex >= 0); |
+ missingPt2 = refSegment->span(maxTIndex).fPt; |
+ } |
+ if (!partial) { |
+ return; |
+ } |
+ if (cancelers) { |
+ if (missingT1 >= 0) { |
+ addTo1->addTCancel(missingPt1, missingPt2, addOther1); |
+ } else { |
+ addTo2->addTCancel(missingPt1, missingPt2, addOther2); |
+ } |
+ } else if (missingT1 >= 0) { |
+ addTo1->addTCoincident(missingPt1, missingPt2, addTo1 == addTo2 ? missingT2 : otherT2, |
+ addOther1); |
+ } else { |
+ addTo2->addTCoincident(missingPt2, missingPt1, addTo2 == addTo1 ? missingT1 : otherT1, |
+ addOther2); |
} |
} |
@@ -196,9 +477,15 @@ void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden |
const SkCoincidence& coincidence = coincidences[index]; |
int thisIndex = coincidence.fSegments[0]; |
SkOpSegment& thisOne = fSegments[thisIndex]; |
+ if (thisOne.done()) { |
+ continue; |
+ } |
SkOpContour* otherContour = coincidence.fOther; |
int otherIndex = coincidence.fSegments[1]; |
SkOpSegment& other = otherContour->fSegments[otherIndex]; |
+ if (other.done()) { |
+ continue; |
+ } |
double startT = coincidence.fTs[0][0]; |
double endT = coincidence.fTs[0][1]; |
if (startT == endT) { // this can happen in very large compares |
@@ -211,8 +498,8 @@ void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden |
} |
bool swapStart = startT > endT; |
bool swapOther = oStartT > oEndT; |
- const SkPoint* startPt = &coincidence.fPts[0]; |
- const SkPoint* endPt = &coincidence.fPts[1]; |
+ const SkPoint* startPt = &coincidence.fPts[0][0]; |
+ const SkPoint* endPt = &coincidence.fPts[0][1]; |
if (swapStart) { |
SkTSwap(startT, endT); |
SkTSwap(oStartT, oEndT); |
@@ -243,6 +530,9 @@ void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden |
} |
void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) { |
+ if (coincidence.fNearly[0] && coincidence.fNearly[1]) { |
+ return; |
+ } |
int thisIndex = coincidence.fSegments[0]; |
SkOpSegment& thisOne = fSegments[thisIndex]; |
if (thisOne.done()) { |
@@ -256,8 +546,8 @@ void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) |
} |
double startT = coincidence.fTs[0][0]; |
double endT = coincidence.fTs[0][1]; |
- const SkPoint* startPt = &coincidence.fPts[0]; |
- const SkPoint* endPt = &coincidence.fPts[1]; |
+ const SkPoint* startPt = &coincidence.fPts[0][0]; |
+ const SkPoint* endPt = &coincidence.fPts[0][1]; |
bool cancelers; |
if ((cancelers = startT > endT)) { |
SkTSwap<double>(startT, endT); |
@@ -291,6 +581,57 @@ void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) |
#endif |
} |
+void SkOpContour::resolveNearCoincidence() { |
+ int count = fCoincidences.count(); |
+ for (int index = 0; index < count; ++index) { |
+ SkCoincidence& coincidence = fCoincidences[index]; |
+ if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) { |
+ continue; |
+ } |
+ int thisIndex = coincidence.fSegments[0]; |
+ SkOpSegment& thisOne = fSegments[thisIndex]; |
+ SkOpContour* otherContour = coincidence.fOther; |
+ int otherIndex = coincidence.fSegments[1]; |
+ SkOpSegment& other = otherContour->fSegments[otherIndex]; |
+ if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) { |
+ // OPTIMIZATION: remove from coincidence array |
+ continue; |
+ } |
+ #if DEBUG_CONCIDENT |
+ thisOne.debugShowTs("-"); |
+ other.debugShowTs("o"); |
+ #endif |
+ double startT = coincidence.fTs[0][0]; |
+ double endT = coincidence.fTs[0][1]; |
+ bool cancelers; |
+ if ((cancelers = startT > endT)) { |
+ SkTSwap<double>(startT, endT); |
+ } |
+ if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing |
+ if (endT <= 1 - FLT_EPSILON) { |
+ endT += FLT_EPSILON; |
+ SkASSERT(endT <= 1); |
+ } else { |
+ startT -= FLT_EPSILON; |
+ SkASSERT(startT >= 0); |
+ } |
+ } |
+ SkASSERT(!approximately_negative(endT - startT)); |
+ double oStartT = coincidence.fTs[1][0]; |
+ double oEndT = coincidence.fTs[1][1]; |
+ if (oStartT > oEndT) { |
+ SkTSwap<double>(oStartT, oEndT); |
+ cancelers ^= true; |
+ } |
+ SkASSERT(!approximately_negative(oEndT - oStartT)); |
+ if (cancelers) { |
+ thisOne.blindCancel(coincidence, &other); |
+ } else { |
+ thisOne.blindCoincident(coincidence, &other); |
+ } |
+ } |
+} |
+ |
void SkOpContour::sortAngles() { |
int segmentCount = fSegments.count(); |
for (int test = 0; test < segmentCount; ++test) { |