2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
7 #include "SkIntersections.h"
8 #include "SkOpContour.h"
9 #include "SkPathWriter.h"
12 bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
13 const SkIntersections& ts, bool swap) {
14 SkPoint pt0 = ts.pt(0).asSkPoint();
15 SkPoint pt1 = ts.pt(1).asSkPoint();
17 // FIXME: one could imagine a case where it would be incorrect to ignore this
18 // suppose two self-intersecting cubics overlap to be coincident --
19 // this needs to check that by some measure the t values are far enough apart
20 // or needs to check to see if the self-intersection bit was set on the cubic segment
23 SkCoincidence& coincidence = fCoincidences.push_back();
24 coincidence.fOther = other;
25 coincidence.fSegments[0] = index;
26 coincidence.fSegments[1] = otherIndex;
27 coincidence.fTs[swap][0] = ts[0][0];
28 coincidence.fTs[swap][1] = ts[0][1];
29 coincidence.fTs[!swap][0] = ts[1][0];
30 coincidence.fTs[!swap][1] = ts[1][1];
31 coincidence.fPts[0] = pt0;
32 coincidence.fPts[1] = pt1;
36 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
37 int segmentCount = fSortedSegments.count();
38 SkASSERT(segmentCount > 0);
39 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
40 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
41 if (testSegment->done()) {
45 while (testSegment->nextCandidate(start, end)) {
46 if (!testSegment->isVertical(*start, *end)) {
54 // first pass, add missing T values
55 // second pass, determine winding values of overlaps
56 void SkOpContour::addCoincidentPoints() {
57 int count = fCoincidences.count();
58 for (int index = 0; index < count; ++index) {
59 SkCoincidence& coincidence = fCoincidences[index];
60 int thisIndex = coincidence.fSegments[0];
61 SkOpSegment& thisOne = fSegments[thisIndex];
62 SkOpContour* otherContour = coincidence.fOther;
63 int otherIndex = coincidence.fSegments[1];
64 SkOpSegment& other = otherContour->fSegments[otherIndex];
65 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
66 // OPTIMIZATION: remove from array
70 thisOne.debugShowTs("-");
71 other.debugShowTs("o");
73 double startT = coincidence.fTs[0][0];
74 double endT = coincidence.fTs[0][1];
75 bool startSwapped, oStartSwapped, cancelers;
76 if ((cancelers = startSwapped = startT > endT)) {
77 SkTSwap(startT, endT);
79 if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
80 if (endT <= 1 - FLT_EPSILON) {
84 startT -= FLT_EPSILON;
85 SkASSERT(startT >= 0);
88 SkASSERT(!approximately_negative(endT - startT));
89 double oStartT = coincidence.fTs[1][0];
90 double oEndT = coincidence.fTs[1][1];
91 if ((oStartSwapped = oStartT > oEndT)) {
92 SkTSwap(oStartT, oEndT);
95 SkASSERT(!approximately_negative(oEndT - oStartT));
97 // make sure startT and endT have t entries
98 const SkPoint& startPt = coincidence.fPts[startSwapped];
99 if (startT > 0 || oEndT < 1
100 || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
101 thisOne.addTPair(startT, &other, oEndT, true, startPt);
103 const SkPoint& oStartPt = coincidence.fPts[oStartSwapped];
104 if (oStartT > 0 || endT < 1
105 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
106 other.addTPair(oStartT, &thisOne, endT, true, oStartPt);
109 const SkPoint& startPt = coincidence.fPts[startSwapped];
110 if (startT > 0 || oStartT > 0
111 || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
112 thisOne.addTPair(startT, &other, oStartT, true, startPt);
114 const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped];
115 if (endT < 1 || oEndT < 1
116 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
117 other.addTPair(oEndT, &thisOne, endT, true, oEndPt);
121 thisOne.debugShowTs("+");
122 other.debugShowTs("o");
127 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
128 const SkIntersections& ts, int ptIndex, bool swap) {
129 SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
130 SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
131 if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
132 // FIXME: one could imagine a case where it would be incorrect to ignore this
133 // suppose two self-intersecting cubics overlap to form a partial coincidence --
134 // although it isn't clear why the regular coincidence could wouldn't pick this up
135 // this is exceptional enough to ignore for now
138 SkCoincidence& coincidence = fPartialCoincidences.push_back();
139 coincidence.fOther = other;
140 coincidence.fSegments[0] = index;
141 coincidence.fSegments[1] = otherIndex;
142 coincidence.fTs[swap][0] = ts[0][ptIndex];
143 coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
144 coincidence.fTs[!swap][0] = ts[1][ptIndex];
145 coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
146 coincidence.fPts[0] = pt0;
147 coincidence.fPts[1] = pt1;
151 void SkOpContour::calcCoincidentWinding() {
152 int count = fCoincidences.count();
155 SkDebugf("%s count=%d\n", __FUNCTION__, count);
158 for (int index = 0; index < count; ++index) {
159 SkCoincidence& coincidence = fCoincidences[index];
160 calcCommonCoincidentWinding(coincidence);
164 void SkOpContour::calcPartialCoincidentWinding() {
165 int count = fPartialCoincidences.count();
168 SkDebugf("%s count=%d\n", __FUNCTION__, count);
171 for (int index = 0; index < count; ++index) {
172 SkCoincidence& coincidence = fPartialCoincidences[index];
173 calcCommonCoincidentWinding(coincidence);
177 void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
178 int count = coincidences.count();
181 SkDebugf("%s count=%d\n", __FUNCTION__, count);
184 // look for a lineup where the partial implies another adjoining coincidence
185 for (int index = 0; index < count; ++index) {
186 const SkCoincidence& coincidence = coincidences[index];
187 int thisIndex = coincidence.fSegments[0];
188 SkOpSegment& thisOne = fSegments[thisIndex];
189 SkOpContour* otherContour = coincidence.fOther;
190 int otherIndex = coincidence.fSegments[1];
191 SkOpSegment& other = otherContour->fSegments[otherIndex];
192 double startT = coincidence.fTs[0][0];
193 double endT = coincidence.fTs[0][1];
194 if (startT == endT) { // this can happen in very large compares
197 double oStartT = coincidence.fTs[1][0];
198 double oEndT = coincidence.fTs[1][1];
199 if (oStartT == oEndT) {
202 bool swapStart = startT > endT;
203 bool swapOther = oStartT > oEndT;
205 SkTSwap<double>(startT, endT);
206 SkTSwap<double>(oStartT, oEndT);
208 bool cancel = swapOther != swapStart;
209 int step = swapStart ? -1 : 1;
210 int oStep = swapOther ? -1 : 1;
211 double oMatchStart = cancel ? oEndT : oStartT;
212 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
214 if (oMatchStart != 0) {
215 added = thisOne.joinCoincidence(&other, oMatchStart, oStep, cancel);
217 if (!cancel && startT != 0 && !added) {
218 (void) other.joinCoincidence(&thisOne, startT, step, cancel);
221 double oMatchEnd = cancel ? oStartT : oEndT;
222 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
224 if (cancel && endT != 1 && !added) {
225 (void) other.joinCoincidence(&thisOne, endT, -step, cancel);
231 void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
232 int thisIndex = coincidence.fSegments[0];
233 SkOpSegment& thisOne = fSegments[thisIndex];
234 if (thisOne.done()) {
237 SkOpContour* otherContour = coincidence.fOther;
238 int otherIndex = coincidence.fSegments[1];
239 SkOpSegment& other = otherContour->fSegments[otherIndex];
243 double startT = coincidence.fTs[0][0];
244 double endT = coincidence.fTs[0][1];
245 const SkPoint* startPt = &coincidence.fPts[0];
246 const SkPoint* endPt = &coincidence.fPts[1];
248 if ((cancelers = startT > endT)) {
249 SkTSwap<double>(startT, endT);
250 SkTSwap<const SkPoint*>(startPt, endPt);
252 if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
253 if (endT <= 1 - FLT_EPSILON) {
257 startT -= FLT_EPSILON;
258 SkASSERT(startT >= 0);
261 SkASSERT(!approximately_negative(endT - startT));
262 double oStartT = coincidence.fTs[1][0];
263 double oEndT = coincidence.fTs[1][1];
264 if (oStartT > oEndT) {
265 SkTSwap<double>(oStartT, oEndT);
268 SkASSERT(!approximately_negative(oEndT - oStartT));
270 thisOne.addTCancel(*startPt, *endPt, &other);
272 thisOne.addTCoincident(*startPt, *endPt, endT, &other);
275 thisOne.debugShowTs("p");
276 other.debugShowTs("o");
280 void SkOpContour::sortSegments() {
281 int segmentCount = fSegments.count();
282 fSortedSegments.push_back_n(segmentCount);
283 for (int test = 0; test < segmentCount; ++test) {
284 fSortedSegments[test] = &fSegments[test];
286 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
290 void SkOpContour::toPath(SkPathWriter* path) const {
291 int segmentCount = fSegments.count();
292 const SkPoint& pt = fSegments.front().pts()[0];
293 path->deferredMove(pt);
294 for (int test = 0; test < segmentCount; ++test) {
295 fSegments[test].addCurveTo(0, 1, path, true);
300 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
301 SkOpSegment** topStart) {
302 int segmentCount = fSortedSegments.count();
303 SkASSERT(segmentCount > 0);
304 int sortedIndex = fFirstSorted;
305 fDone = true; // may be cleared below
306 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
307 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
308 if (testSegment->done()) {
309 if (sortedIndex == fFirstSorted) {
315 SkPoint testXY = testSegment->activeLeftTop(true, NULL);
317 if (testXY.fY < topLeft.fY) {
320 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
323 if (bestXY->fY < testXY.fY) {
326 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
330 *topStart = testSegment;
335 SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
336 int segmentCount = fSegments.count();
337 for (int test = 0; test < segmentCount; ++test) {
338 SkOpSegment* testSegment = &fSegments[test];
339 if (testSegment->done()) {
342 testSegment->undoneSpan(start, end);
348 #if DEBUG_SHOW_WINDING
349 int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
350 int count = fSegments.count();
352 for (int index = 0; index < count; ++index) {
353 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
355 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
359 void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
360 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
361 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
362 int ofInterest = 1 << 5 | 1 << 8;
365 for (index = 0; index < contourList.count(); ++index) {
366 total += contourList[index]->segments().count();
369 for (index = 0; index < contourList.count(); ++index) {
370 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
372 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
376 void SkOpContour::setBounds() {
377 int count = fSegments.count();
379 SkDebugf("%s empty contour\n", __FUNCTION__);
381 // FIXME: delete empty contour?
384 fBounds = fSegments.front().bounds();
385 for (int index = 1; index < count; ++index) {
386 fBounds.add(fSegments[index].bounds());