blob: 1dc171caf35864de04b712f85a96e467776a41b5 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00007#include "SkAddIntersections.h"
caryclark54359292015-03-26 07:52:43 -07008#include "SkOpCoincidence.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpEdgeBuilder.h"
10#include "SkPathOpsCommon.h"
11#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000012#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000013
caryclark54359292015-03-26 07:52:43 -070014static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
15 SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
16 double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) {
17 SkOpSpanBase* start = *startPtr;
18 SkOpSpanBase* end = *endPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 const double mid = *midPtr;
20 const SkOpSegment* current = *currentPtr;
caryclark54359292015-03-26 07:52:43 -070021 double tAtMid = SkOpSegment::TAtMid(start, end, mid);
caryclark@google.com4fdbb222013-07-23 15:27:41 +000022 SkPoint basePt = current->ptAtT(tAtMid);
caryclark@google.com07393ca2013-04-08 11:47:37 +000023 int contourCount = contourList.count();
24 SkScalar bestY = SK_ScalarMin;
25 SkOpSegment* bestSeg = NULL;
caryclark54359292015-03-26 07:52:43 -070026 SkOpSpan* bestTSpan = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 bool bestOpp;
28 bool hitSomething = false;
29 for (int cTest = 0; cTest < contourCount; ++cTest) {
30 SkOpContour* contour = contourList[cTest];
31 bool testOpp = contour->operand() ^ current->operand() ^ opp;
32 if (basePt.fY < contour->bounds().fTop) {
33 continue;
34 }
35 if (bestY > contour->bounds().fBottom) {
36 continue;
37 }
caryclark54359292015-03-26 07:52:43 -070038 SkOpSegment* testSeg = contour->first();
39 SkASSERT(testSeg);
40 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000041 SkScalar testY = bestY;
42 double testHit;
caryclark54359292015-03-26 07:52:43 -070043 bool vertical;
44 SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
45 testSeg == current, &testY, &testHit, &hitSomething, &vertical);
46 if (!testTSpan) {
47 if (vertical) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000048 hitSomething = true;
49 bestSeg = NULL;
50 goto abortContours; // vertical encountered, return and try different point
51 }
52 continue;
53 }
caryclark54359292015-03-26 07:52:43 -070054 if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
55 double baseT = start->t();
56 double endT = end->t();
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 double newMid = (testHit - baseT) / (endT - baseT);
58#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070059 double midT = SkOpSegment::TAtMid(start, end, mid);
60 SkPoint midXY = current->ptAtT(midT);
61 double newMidT = SkOpSegment::TAtMid(start, end, newMid);
62 SkPoint newXY = current->ptAtT(newMidT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000063 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
64 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
65 current->debugID(), mid, newMid,
caryclark54359292015-03-26 07:52:43 -070066 baseT, start->pt().fX, start->pt().fY,
caryclark@google.com07393ca2013-04-08 11:47:37 +000067 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
68 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
caryclark54359292015-03-26 07:52:43 -070069 endT, end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000070#endif
71 *midPtr = newMid * 2; // calling loop with divide by 2 before continuing
72 return SK_MinS32;
73 }
74 bestSeg = testSeg;
75 *bestHit = testHit;
76 bestOpp = testOpp;
caryclark54359292015-03-26 07:52:43 -070077 bestTSpan = testTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 bestY = testY;
caryclark54359292015-03-26 07:52:43 -070079 } while ((testSeg = testSeg->next()));
caryclark@google.com07393ca2013-04-08 11:47:37 +000080 }
81abortContours:
82 int result;
83 if (!bestSeg) {
84 result = hitSomething ? SK_MinS32 : 0;
85 } else {
caryclark54359292015-03-26 07:52:43 -070086 if (bestTSpan->windSum() == SK_MinS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 *currentPtr = bestSeg;
caryclark54359292015-03-26 07:52:43 -070088 *startPtr = bestTSpan;
89 *endPtr = bestTSpan->next();
90 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +000091 *tryAgain = true;
92 return 0;
93 }
caryclark54359292015-03-26 07:52:43 -070094 result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
caryclark@google.comf11a5af2013-04-10 18:55:11 +000095 SkASSERT(result == SK_MinS32 || *bestDx);
caryclark@google.com07393ca2013-04-08 11:47:37 +000096 }
caryclark54359292015-03-26 07:52:43 -070097 double baseT = (*startPtr)->t();
98 double endT = (*endPtr)->t();
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 *bestHit = baseT + mid * (endT - baseT);
100 return result;
101}
102
caryclark54359292015-03-26 07:52:43 -0700103SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
104 SkOpSpanBase** endPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 int contourCount = contourList.count();
106 SkOpSegment* result;
107 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
108 SkOpContour* contour = contourList[cIndex];
caryclark54359292015-03-26 07:52:43 -0700109 result = contour->undoneSegment(startPtr, endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 if (result) {
111 return result;
112 }
113 }
114 return NULL;
115}
116
caryclark54359292015-03-26 07:52:43 -0700117SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
118 SkOpSpanBase** endPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000119 while (chase->count()) {
caryclark54359292015-03-26 07:52:43 -0700120 SkOpSpanBase* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000121 chase->pop(&span);
caryclark54359292015-03-26 07:52:43 -0700122 SkOpSegment* segment = span->segment();
123 *startPtr = span->ptT()->next()->span();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000124 bool sortable = true;
125 bool done = true;
caryclark54359292015-03-26 07:52:43 -0700126 *endPtr = NULL;
127 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000128 &sortable)) {
caryclark54359292015-03-26 07:52:43 -0700129 if (last->unorderable()) {
130 continue;
131 }
132 *startPtr = last->start();
133 *endPtr = last->end();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000134 #if TRY_ROTATE
135 *chase->insert(0) = span;
136 #else
137 *chase->append() = span;
138 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 return last->segment();
140 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000141 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 continue;
143 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 if (!sortable) {
145 continue;
146 }
caryclark65f55312014-11-13 06:58:52 -0800147 // find first angle, initialize winding to computed wind sum
caryclark54359292015-03-26 07:52:43 -0700148 const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
149 if (!angle) {
150 continue;
151 }
152 const SkOpAngle* firstAngle = angle;
153 bool loop = false;
154 int winding = SK_MinS32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000156 angle = angle->next();
caryclark54359292015-03-26 07:52:43 -0700157 if (angle == firstAngle && loop) {
158 break; // if we get here, there's no winding, loop is unorderable
159 }
160 loop |= angle == firstAngle;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 segment = angle->segment();
162 winding = segment->windSum(angle);
163 } while (winding == SK_MinS32);
caryclark54359292015-03-26 07:52:43 -0700164 if (winding == SK_MinS32) {
165 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000166 }
caryclark54359292015-03-26 07:52:43 -0700167 int sumWinding = segment->updateWindingReverse(angle);
168 SkOpSegment* first = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000169 firstAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -0700172 SkOpSpanBase* start = angle->start();
173 SkOpSpanBase* end = angle->end();
174 int maxWinding;
175 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
176 if (!segment->done(angle)) {
177 if (!first) {
178 first = segment;
179 *startPtr = start;
180 *endPtr = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181 }
caryclark54359292015-03-26 07:52:43 -0700182 // OPTIMIZATION: should this also add to the chase?
183 (void) segment->markAngle(maxWinding, sumWinding, angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000185 }
caryclark54359292015-03-26 07:52:43 -0700186 if (first) {
187 #if TRY_ROTATE
188 *chase->insert(0) = span;
189 #else
190 *chase->append() = span;
191 #endif
192 return first;
193 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194 }
195 return NULL;
196}
197
caryclark54359292015-03-26 07:52:43 -0700198#if DEBUG_ACTIVE_SPANS
199void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 int index;
201 for (index = 0; index < contourList.count(); ++ index) {
202 contourList[index]->debugShowActiveSpans();
203 }
204}
205#endif
206
caryclark54359292015-03-26 07:52:43 -0700207static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
208 bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
209 bool* unsortable, bool* done, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 SkOpSegment* result;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000211 const SkOpSegment* lastTopStart = NULL;
caryclark54359292015-03-26 07:52:43 -0700212 SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000213 do {
214 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
215 int contourCount = contourList.count();
216 SkOpSegment* topStart = NULL;
217 *done = true;
218 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
219 SkOpContour* contour = contourList[cIndex];
220 if (contour->done()) {
221 continue;
222 }
223 const SkPathOpsBounds& bounds = contour->bounds();
224 if (bounds.fBottom < topLeft->fY) {
225 *done = false;
226 continue;
227 }
228 if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) {
229 *done = false;
230 continue;
231 }
232 contour->topSortableSegment(*topLeft, &bestXY, &topStart);
233 if (!contour->done()) {
234 *done = false;
235 }
236 }
237 if (!topStart) {
238 return NULL;
239 }
240 *topLeft = bestXY;
caryclark54359292015-03-26 07:52:43 -0700241 result = topStart->findTop(firstPass, start, end, unsortable, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000242 if (!result) {
caryclark54359292015-03-26 07:52:43 -0700243 if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000244 *done = true;
245 return NULL;
246 }
247 lastTopStart = topStart;
caryclark54359292015-03-26 07:52:43 -0700248 lastStart = *start;
249 lastEnd = *end;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000250 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000251 } while (!result);
252 return result;
253}
254
caryclark54359292015-03-26 07:52:43 -0700255static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
256 SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700257 SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000258 double test = 0.9;
259 int contourWinding;
260 do {
caryclark54359292015-03-26 07:52:43 -0700261 contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
caryclarkdac1d172014-06-17 05:15:38 -0700262 tHit, hitDx, tryAgain, &test, opp);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000263 if (contourWinding != SK_MinS32 || *tryAgain) {
264 return contourWinding;
265 }
caryclarkdac1d172014-06-17 05:15:38 -0700266 if (*currentPtr && (*currentPtr)->isVertical()) {
267 *onlyVertical = true;
268 return contourWinding;
269 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 test /= 2;
271 } while (!approximately_negative(test));
caryclarkdac1d172014-06-17 05:15:38 -0700272 SkASSERT(0); // FIXME: incomplete functionality
caryclark@google.com07393ca2013-04-08 11:47:37 +0000273 return contourWinding;
274}
275
caryclark54359292015-03-26 07:52:43 -0700276static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
277 SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
278 if (!(*current)->isVertical(*start, *end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000279 return;
280 }
281 int contourCount = contourList.count();
282 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
283 SkOpContour* contour = contourList[cIndex];
284 if (contour->done()) {
285 continue;
286 }
caryclark54359292015-03-26 07:52:43 -0700287 SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000288 if (nonVertical) {
289 *current = nonVertical;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000290 return;
291 }
292 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000293 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000294}
295
caryclark54359292015-03-26 07:52:43 -0700296struct SortableTop2 { // error if local in pre-C++11
297 SkOpSpanBase* fStart;
298 SkOpSpanBase* fEnd;
caryclark65f55312014-11-13 06:58:52 -0800299};
300
caryclark54359292015-03-26 07:52:43 -0700301SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass,
302 SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr,
303 SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
304 SkChunkAlloc* allocator) {
305 SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft,
306 unsortable, done, allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000307 if (!current) {
308 return NULL;
309 }
caryclark54359292015-03-26 07:52:43 -0700310 SkOpSpanBase* start = *startPtr;
311 SkOpSpanBase* end = *endPtr;
312 SkASSERT(current == start->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000313 if (*firstContour) {
caryclark54359292015-03-26 07:52:43 -0700314 current->initWinding(start, end, angleIncludeType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000315 *firstContour = false;
316 return current;
317 }
caryclark54359292015-03-26 07:52:43 -0700318 SkOpSpan* minSpan = start->starter(end);
319 int sumWinding = minSpan->windSum();
caryclarkdac1d172014-06-17 05:15:38 -0700320 if (sumWinding == SK_MinS32) {
caryclark54359292015-03-26 07:52:43 -0700321 SkOpSpanBase* iSpan = end;
322 SkOpSpanBase* oSpan = start;
323 do {
324 bool checkFrom = oSpan->t() < iSpan->t();
325 if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) {
326 iSpan->addSimpleAngle(checkFrom, allocator);
caryclarkdac1d172014-06-17 05:15:38 -0700327 }
caryclark54359292015-03-26 07:52:43 -0700328 sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
329 SkTSwap(iSpan, oSpan);
330 } while (sumWinding == SK_MinS32 && iSpan == start);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000331 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000332 if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000333 return current;
334 }
335 int contourWinding;
336 int oppContourWinding = 0;
337 // the simple upward projection of the unresolved points hit unsortable angles
338 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
339 bool tryAgain;
340 double tHit;
341 SkScalar hitDx = 0;
342 SkScalar hitOppDx = 0;
caryclark65f55312014-11-13 06:58:52 -0800343 // keep track of subsequent returns to detect infinite loops
caryclark54359292015-03-26 07:52:43 -0700344 SkTDArray<SortableTop2> sortableTops;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000345 do {
346 // if current is vertical, find another candidate which is not
347 // if only remaining candidates are vertical, then they can be marked done
caryclark54359292015-03-26 07:52:43 -0700348 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
349 SkASSERT(current == (*startPtr)->segment());
350 skipVertical(contourList, &current, startPtr, endPtr);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000351 SkASSERT(current); // FIXME: if null, all remaining are vertical
caryclark54359292015-03-26 07:52:43 -0700352 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
353 SkASSERT(current == (*startPtr)->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000354 tryAgain = false;
caryclark54359292015-03-26 07:52:43 -0700355 contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700356 &hitDx, &tryAgain, onlyVertical, false);
caryclark54359292015-03-26 07:52:43 -0700357 SkASSERT(current == (*startPtr)->segment());
caryclark65f55312014-11-13 06:58:52 -0800358 if (tryAgain) {
359 bool giveUp = false;
360 int count = sortableTops.count();
361 for (int index = 0; index < count; ++index) {
caryclark54359292015-03-26 07:52:43 -0700362 const SortableTop2& prev = sortableTops[index];
caryclark65f55312014-11-13 06:58:52 -0800363 if (giveUp) {
caryclark54359292015-03-26 07:52:43 -0700364 prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
365 } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
caryclark65f55312014-11-13 06:58:52 -0800366 // remaining edges are non-vertical and cannot have their winding computed
367 // mark them as done and return, and hope that assembly can fill the holes
368 giveUp = true;
369 index = -1;
370 }
371 }
372 if (giveUp) {
373 *done = true;
374 return NULL;
375 }
376 }
caryclark54359292015-03-26 07:52:43 -0700377 SortableTop2* sortableTop = sortableTops.append();
378 sortableTop->fStart = *startPtr;
379 sortableTop->fEnd = *endPtr;
caryclark65f55312014-11-13 06:58:52 -0800380#if DEBUG_SORT
381 SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
caryclark54359292015-03-26 07:52:43 -0700382 __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
383 tHit, hitDx, tryAgain, *onlyVertical);
caryclark65f55312014-11-13 06:58:52 -0800384#endif
caryclarkdac1d172014-06-17 05:15:38 -0700385 if (*onlyVertical) {
386 return current;
387 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000388 if (tryAgain) {
389 continue;
390 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000391 if (angleIncludeType < SkOpAngle::kBinarySingle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000392 break;
393 }
caryclark54359292015-03-26 07:52:43 -0700394 oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700395 &hitOppDx, &tryAgain, NULL, true);
caryclark54359292015-03-26 07:52:43 -0700396 SkASSERT(current == (*startPtr)->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000397 } while (tryAgain);
caryclark54359292015-03-26 07:52:43 -0700398 bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
caryclark65f55312014-11-13 06:58:52 -0800399 oppContourWinding, hitOppDx);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000400 if (current->done()) {
401 return NULL;
caryclark65f55312014-11-13 06:58:52 -0800402 } else if (!success) { // check if the span has a valid winding
caryclark54359292015-03-26 07:52:43 -0700403 SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
404 : (*endPtr)->upCast();
405 if (minSpan->windSum() == SK_MinS32) {
caryclark65f55312014-11-13 06:58:52 -0800406 return NULL;
407 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000408 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000409 return current;
410}
411
caryclark54359292015-03-26 07:52:43 -0700412void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
reed0dc4dd62015-03-24 13:55:33 -0700413 bool evenOdd, bool oppEvenOdd) {
caryclark54359292015-03-26 07:52:43 -0700414 do {
415 if (contour->count()) {
416 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
417 *list.append() = contour;
418 }
419 } while ((contour = contour->next()));
420 if (list.count() < 2) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000421 return;
422 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000423 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000424}
425
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000426class DistanceLessThan {
427public:
428 DistanceLessThan(double* distances) : fDistances(distances) { }
429 double* fDistances;
430 bool operator()(const int one, const int two) {
431 return fDistances[one] < fDistances[two];
432 }
433};
434
caryclark@google.com07393ca2013-04-08 11:47:37 +0000435 /*
436 check start and end of each contour
437 if not the same, record them
438 match them up
439 connect closest
440 reassemble contour pieces into new path
441 */
442void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
caryclark54359292015-03-26 07:52:43 -0700443 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
444 SkOpContour contour;
445 SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000446#if DEBUG_PATH_CONSTRUCTION
447 SkDebugf("%s\n", __FUNCTION__);
448#endif
caryclark54359292015-03-26 07:52:43 -0700449 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
450 builder.finish(&allocator);
451 SkTDArray<const SkOpContour* > runs; // indices of partial contours
452 const SkOpContour* eContour = builder.head();
453 do {
454 if (!eContour->count()) {
455 continue;
456 }
457 const SkPoint& eStart = eContour->start();
458 const SkPoint& eEnd = eContour->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000459#if DEBUG_ASSEMBLE
460 SkDebugf("%s contour", __FUNCTION__);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000461 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000462 SkDebugf("[%d]", runs.count());
463 } else {
464 SkDebugf(" ");
465 }
466 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
467 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
468#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000469 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark54359292015-03-26 07:52:43 -0700470 eContour->toPath(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000471 continue;
472 }
caryclark54359292015-03-26 07:52:43 -0700473 *runs.append() = eContour;
474 } while ((eContour = eContour->next()));
475 int count = runs.count();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000476 if (count == 0) {
477 return;
478 }
caryclark54359292015-03-26 07:52:43 -0700479 SkTDArray<int> sLink, eLink;
480 sLink.append(count);
481 eLink.append(count);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000482 int rIndex, iIndex;
483 for (rIndex = 0; rIndex < count; ++rIndex) {
484 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
485 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000486 const int ends = count * 2; // all starts and ends
487 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
caryclark54359292015-03-26 07:52:43 -0700488 SkTDArray<double> distances;
489 distances.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000490 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
caryclark54359292015-03-26 07:52:43 -0700491 const SkOpContour* oContour = runs[rIndex >> 1];
492 const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000493 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
494 * ends - rIndex - 1;
495 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
caryclark54359292015-03-26 07:52:43 -0700496 const SkOpContour* iContour = runs[iIndex >> 1];
497 const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000498 double dx = iPt.fX - oPt.fX;
499 double dy = iPt.fY - oPt.fY;
500 double dist = dx * dx + dy * dy;
501 distances[row + iIndex] = dist; // oStart distance from iStart
502 }
503 }
caryclark54359292015-03-26 07:52:43 -0700504 SkTDArray<int> sortedDist;
505 sortedDist.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000506 for (rIndex = 0; rIndex < entries; ++rIndex) {
507 sortedDist[rIndex] = rIndex;
508 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000509 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000510 int remaining = count; // number of start/end pairs
511 for (rIndex = 0; rIndex < entries; ++rIndex) {
512 int pair = sortedDist[rIndex];
513 int row = pair / ends;
514 int col = pair - row * ends;
515 int thingOne = row < col ? row : ends - row - 2;
516 int ndxOne = thingOne >> 1;
517 bool endOne = thingOne & 1;
518 int* linkOne = endOne ? eLink.begin() : sLink.begin();
519 if (linkOne[ndxOne] != SK_MaxS32) {
520 continue;
521 }
522 int thingTwo = row < col ? col : ends - row + col - 1;
523 int ndxTwo = thingTwo >> 1;
524 bool endTwo = thingTwo & 1;
525 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
526 if (linkTwo[ndxTwo] != SK_MaxS32) {
527 continue;
528 }
529 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
530 bool flip = endOne == endTwo;
531 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
532 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
533 if (!--remaining) {
534 break;
535 }
536 }
537 SkASSERT(!remaining);
538#if DEBUG_ASSEMBLE
539 for (rIndex = 0; rIndex < count; ++rIndex) {
540 int s = sLink[rIndex];
541 int e = eLink[rIndex];
542 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
543 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
544 }
545#endif
546 rIndex = 0;
547 do {
548 bool forward = true;
549 bool first = true;
550 int sIndex = sLink[rIndex];
551 SkASSERT(sIndex != SK_MaxS32);
552 sLink[rIndex] = SK_MaxS32;
553 int eIndex;
554 if (sIndex < 0) {
555 eIndex = sLink[~sIndex];
556 sLink[~sIndex] = SK_MaxS32;
557 } else {
558 eIndex = eLink[sIndex];
559 eLink[sIndex] = SK_MaxS32;
560 }
561 SkASSERT(eIndex != SK_MaxS32);
562#if DEBUG_ASSEMBLE
563 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
564 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
565 eIndex < 0 ? ~eIndex : eIndex);
566#endif
567 do {
caryclark54359292015-03-26 07:52:43 -0700568 const SkOpContour* contour = runs[rIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000569 if (first) {
570 first = false;
caryclark54359292015-03-26 07:52:43 -0700571 const SkPoint* startPtr = &contour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000572 simple->deferredMove(startPtr[0]);
573 }
574 if (forward) {
caryclark54359292015-03-26 07:52:43 -0700575 contour->toPartialForward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000576 } else {
caryclark54359292015-03-26 07:52:43 -0700577 contour->toPartialBackward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000578 }
579#if DEBUG_ASSEMBLE
580 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
581 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
582 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
583#endif
584 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
585 simple->close();
586 break;
587 }
588 if (forward) {
589 eIndex = eLink[rIndex];
590 SkASSERT(eIndex != SK_MaxS32);
591 eLink[rIndex] = SK_MaxS32;
592 if (eIndex >= 0) {
593 SkASSERT(sLink[eIndex] == rIndex);
594 sLink[eIndex] = SK_MaxS32;
595 } else {
596 SkASSERT(eLink[~eIndex] == ~rIndex);
597 eLink[~eIndex] = SK_MaxS32;
598 }
599 } else {
600 eIndex = sLink[rIndex];
601 SkASSERT(eIndex != SK_MaxS32);
602 sLink[rIndex] = SK_MaxS32;
603 if (eIndex >= 0) {
604 SkASSERT(eLink[eIndex] == rIndex);
605 eLink[eIndex] = SK_MaxS32;
606 } else {
607 SkASSERT(sLink[~eIndex] == ~rIndex);
608 sLink[~eIndex] = SK_MaxS32;
609 }
610 }
611 rIndex = eIndex;
612 if (rIndex < 0) {
613 forward ^= 1;
614 rIndex = ~rIndex;
615 }
616 } while (true);
617 for (rIndex = 0; rIndex < count; ++rIndex) {
618 if (sLink[rIndex] != SK_MaxS32) {
619 break;
620 }
621 }
622 } while (rIndex < count);
623#if DEBUG_ASSEMBLE
624 for (rIndex = 0; rIndex < count; ++rIndex) {
625 SkASSERT(sLink[rIndex] == SK_MaxS32);
626 SkASSERT(eLink[rIndex] == SK_MaxS32);
627 }
628#endif
629}
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000630
caryclark54359292015-03-26 07:52:43 -0700631static void align(SkTDArray<SkOpContour* >* contourList) {
632 int contourCount = (*contourList).count();
633 for (int cTest = 0; cTest < contourCount; ++cTest) {
634 SkOpContour* contour = (*contourList)[cTest];
635 contour->align();
636 }
637}
638
639static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
640 int contourCount = (*contourList).count();
641 for (int cTest = 0; cTest < contourCount; ++cTest) {
642 SkOpContour* contour = (*contourList)[cTest];
643 contour->calcAngles(allocator);
644 }
645}
646
647static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
648 SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
649 int contourCount = (*contourList).count();
650 for (int cTest = 0; cTest < contourCount; ++cTest) {
651 SkOpContour* contour = (*contourList)[cTest];
652 contour->missingCoincidence(coincidence, allocator);
653 }
654}
655
656static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
657 int contourCount = (*contourList).count();
658 for (int cTest = 0; cTest < contourCount; ++cTest) {
659 SkOpContour* contour = (*contourList)[cTest];
660 if (!contour->moveNearby()) {
661 return false;
662 }
663 }
664 return true;
665}
666
667static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
668 int contourCount = (*contourList).count();
669 for (int cTest = 0; cTest < contourCount; ++cTest) {
670 SkOpContour* contour = (*contourList)[cTest];
671 contour->sortAngles();
672 }
673}
674
675static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
676 int contourCount = (*contourList).count();
677 for (int cTest = 0; cTest < contourCount; ++cTest) {
678 SkOpContour* contour = (*contourList)[cTest];
679 contour->sortSegments();
680 }
681}
682
683bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
684 SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
685 // move t values and points together to eliminate small/tiny gaps
686 if (!moveNearby(contourList)) {
caryclark630240d2014-09-19 06:33:31 -0700687 return false;
688 }
caryclark54359292015-03-26 07:52:43 -0700689 align(contourList); // give all span members common values
690#if DEBUG_VALIDATE
691 globalState->setPhase(SkOpGlobalState::kIntersecting);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000692#endif
caryclark54359292015-03-26 07:52:43 -0700693 coincidence->addMissing(allocator);
694#if DEBUG_VALIDATE
695 globalState->setPhase(SkOpGlobalState::kWalking);
696#endif
697 coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
698 coincidence->mark(); // mark spans of coincident segments as coincident
699 missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
700 if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
caryclark65f55312014-11-13 06:58:52 -0800701 return false;
702 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000703 sortSegments(contourList);
caryclark54359292015-03-26 07:52:43 -0700704 calcAngles(contourList, allocator);
reed0dc4dd62015-03-24 13:55:33 -0700705 sortAngles(contourList);
caryclark54359292015-03-26 07:52:43 -0700706 if (globalState->angleCoincidence()) {
707 missingCoincidence(contourList, coincidence, allocator);
708 if (!coincidence->apply()) {
709 return false;
710 }
711 }
712#if DEBUG_ACTIVE_SPANS
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000713 DebugShowActiveSpans(*contourList);
714#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000715 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000716}