Index: src/pathops/SkOpSegment.cpp |
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp |
index 4d11eb39e8259b73c79e313c109e1aa314f94402..6fe1fbb49d46d893c08da9fa9cfac2cf35c30425 100644 |
--- a/src/pathops/SkOpSegment.cpp |
+++ b/src/pathops/SkOpSegment.cpp |
@@ -1298,6 +1298,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi |
SkIntersections intersections; |
// OPTIMIZE: use specialty function that intersects ray with curve, |
// returning t values only for curve (we don't care about t on ray) |
+ intersections.allowNear(false); |
int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)]) |
(fPts, top, bottom, basePt.fX, false); |
if (pts == 0 || (current && pts == 1)) { |
@@ -1420,15 +1421,29 @@ void SkOpSegment::checkEnds() { |
} |
// t start/last describe the range of spans that match the t of this span |
double t = span.fT; |
- int tStart = index; |
- while (--tStart >= 0 && (t == fTs[tStart].fT || fTs[tStart].fTiny)) |
- ; |
- int tLast = index; |
- while (fTs[tLast].fTiny) { |
- ++tLast; |
+ double tBottom = -1; |
+ int tStart = -1; |
+ int tLast = count; |
+ bool lastSmall = false; |
+ double afterT = t; |
+ for (int inner = 0; inner < count; ++inner) { |
+ double innerT = fTs[inner].fT; |
+ if (innerT <= t && innerT > tBottom) { |
+ if (innerT < t || !lastSmall) { |
+ tStart = inner - 1; |
+ } |
+ tBottom = innerT; |
+ } |
+ if (innerT > afterT) { |
+ if (t == afterT && lastSmall) { |
+ afterT = innerT; |
+ } else { |
+ tLast = inner; |
+ break; |
+ } |
+ } |
+ lastSmall = innerT <= t ? fTs[inner].fSmall : false; |
} |
- while (++tLast < count && t == fTs[tLast].fT) |
- ; |
for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) { |
if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span |
continue; |
@@ -1696,6 +1711,70 @@ void SkOpSegment::checkTiny() { |
} |
} |
+bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, |
+ int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const { |
+ SkASSERT(span->fT == 0 || span->fT == 1); |
+ SkASSERT(span->fOtherT == 0 || span->fOtherT == 1); |
+ const SkOpSpan* otherSpan = &other->span(oEnd); |
+ double refT = otherSpan->fT; |
+ const SkPoint& refPt = otherSpan->fPt; |
+ const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0); |
+ do { |
+ const SkOpSegment* match = span->fOther; |
+ if (match == otherSpan->fOther) { |
+ // find start of respective spans and see if both have winding |
+ int startIndex, endIndex; |
+ if (span->fOtherT == 1) { |
+ endIndex = span->fOtherIndex; |
+ startIndex = match->nextExactSpan(endIndex, -1); |
+ } else { |
+ startIndex = span->fOtherIndex; |
+ endIndex = match->nextExactSpan(startIndex, 1); |
+ } |
+ const SkOpSpan& startSpan = match->span(startIndex); |
+ if (startSpan.fWindValue != 0) { |
+ // draw ray from endSpan.fPt perpendicular to end tangent and measure distance |
+ // to other segment. |
+ const SkOpSpan& endSpan = match->span(endIndex); |
+ SkDLine ray; |
+ SkVector dxdy; |
+ if (span->fOtherT == 1) { |
+ ray.fPts[0].set(startSpan.fPt); |
+ dxdy = match->dxdy(startIndex); |
+ } else { |
+ ray.fPts[0].set(endSpan.fPt); |
+ dxdy = match->dxdy(endIndex); |
+ } |
+ ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY; |
+ ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX; |
+ SkIntersections i; |
+ int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray); |
+ for (int index = 0; index < roots; ++index) { |
+ if (ray.fPts[0].approximatelyEqual(i.pt(index))) { |
+ double matchMidT = (match->span(startIndex).fT |
+ + match->span(endIndex).fT) / 2; |
+ SkPoint matchMidPt = match->ptAtT(matchMidT); |
+ double otherMidT = (i[0][index] + other->span(oStart).fT) / 2; |
+ SkPoint otherMidPt = other->ptAtT(otherMidT); |
+ if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) { |
+ *startPt = startSpan.fPt; |
+ *endPt = endSpan.fPt; |
+ *endT = endSpan.fT; |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ if (otherSpan == lastSpan) { |
+ break; |
+ } |
+ otherSpan += step; |
+ } while (otherSpan->fT == refT || otherSpan->fPt == refPt); |
+ return false; |
+} |
+ |
/* |
The M and S variable name parts stand for the operators. |
Mi stands for Minuend (see wiki subtraction, analogous to difference) |
@@ -2076,6 +2155,18 @@ int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int |
return firstIndex; |
} |
+int SkOpSegment::findT(double t, const SkOpSegment* match) const { |
+ int count = this->count(); |
+ for (int index = 0; index < count; ++index) { |
+ const SkOpSpan& span = fTs[index]; |
+ if (span.fT == t && span.fOther == match) { |
+ return index; |
+ } |
+ } |
+ SkASSERT(0); |
+ return -1; |
+} |
+ |
// FIXME: either: |
// a) mark spans with either end unsortable as done, or |
// b) rewrite findTop / findTopSegment / findTopContour to iterate further |
@@ -2299,6 +2390,76 @@ bool SkOpSegment::isSimple(int end) const { |
return false; |
} |
+bool SkOpSegment::isTiny(const SkOpAngle* angle) const { |
+ int start = angle->start(); |
+ int end = angle->end(); |
+ const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; |
+ return mSpan.fTiny; |
+} |
+ |
+bool SkOpSegment::isTiny(int index) const { |
+ return fTs[index].fTiny; |
+} |
+ |
+// look pair of active edges going away from coincident edge |
+// one of them should be the continuation of other |
+// if both are active, look to see if they both the connect to another coincident pair |
+// if one at least one is a line, then make the pair coincident |
+// if neither is a line, test for coincidence |
+bool SkOpSegment::joinCoincidence(bool end, SkOpSegment* other, double otherT, int step, |
+ bool cancel) { |
+ int otherTIndex = other->findT(otherT, this); |
+ int next = other->nextExactSpan(otherTIndex, step); |
+ int otherMin = SkTMin(otherTIndex, next); |
+ int otherWind = other->span(otherMin).fWindValue; |
+ if (otherWind == 0) { |
+ return false; |
+ } |
+ SkASSERT(next >= 0); |
+ if (end) { |
+ int tIndex = count() - 1; |
+ do { |
+ SkOpSpan* test = &fTs[tIndex]; |
+ SkASSERT(test->fT == 1); |
+ if (test->fOther == other || test->fOtherT != 0) { |
+ continue; |
+ } |
+ SkPoint startPt, endPt; |
+ double endT; |
+ if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { |
+ SkOpSegment* match = test->fOther; |
+ if (cancel) { |
+ match->addTCancel(startPt, endPt, other); |
+ } else { |
+ match->addTCoincident(startPt, endPt, endT, other); |
+ } |
+ return true; |
+ } |
+ } while (fTs[--tIndex].fT == 1); |
+ } else { |
+ int tIndex = 0; |
+ do { |
+ SkOpSpan* test = &fTs[tIndex]; |
+ SkASSERT(test->fT == 0); |
+ if (test->fOther == other || test->fOtherT != 1) { |
+ continue; |
+ } |
+ SkPoint startPt, endPt; |
+ double endT; |
+ if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { |
+ SkOpSegment* match = test->fOther; |
+ if (cancel) { |
+ match->addTCancel(startPt, endPt, other); |
+ } else { |
+ match->addTCoincident(startPt, endPt, endT, other); |
+ } |
+ return true; |
+ } |
+ } while (fTs[++tIndex].fT == 0); |
+ } |
+ return false; |
+} |
+ |
// this span is excluded by the winding rule -- chase the ends |
// as long as they are unambiguous to mark connections as done |
// and give them the same winding value |
@@ -3018,17 +3179,6 @@ void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) c |
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge); |
} |
-bool SkOpSegment::isTiny(const SkOpAngle* angle) const { |
- int start = angle->start(); |
- int end = angle->end(); |
- const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; |
- return mSpan.fTiny; |
-} |
- |
-bool SkOpSegment::isTiny(int index) const { |
- return fTs[index].fTiny; |
-} |
- |
void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt, |
const SkPoint& startPt) { |
int outCount = outsidePts->count(); |
@@ -3558,10 +3708,10 @@ void SkOpSegment::dumpPts() const { |
SkDebugf("{{"); |
int index = 0; |
do { |
- SkDPoint::DumpSkPoint(fPts[index]); |
+ SkDPoint::dump(fPts[index]); |
SkDebugf(", "); |
} while (++index < last); |
- SkDPoint::DumpSkPoint(fPts[index]); |
+ SkDPoint::dump(fPts[index]); |
SkDebugf("}}\n"); |
} |