blob: 0e9e1bee8e3065ba3c0c3440f121996e245523f8 [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"
caryclark@google.com07393ca2013-04-08 11:47:37 +00008#include "SkOpEdgeBuilder.h"
9#include "SkPathOpsCommon.h"
10#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000011#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
caryclark@google.comd892bd82013-06-17 14:10:36 +000013static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
caryclark@google.com07393ca2013-04-08 11:47:37 +000014 int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
15 bool* tryAgain, double* midPtr, bool opp) {
16 const int index = *indexPtr;
17 const int endIndex = *endIndexPtr;
18 const double mid = *midPtr;
19 const SkOpSegment* current = *currentPtr;
20 double tAtMid = current->tAtMid(index, endIndex, mid);
caryclark@google.com4fdbb222013-07-23 15:27:41 +000021 SkPoint basePt = current->ptAtT(tAtMid);
caryclark@google.com07393ca2013-04-08 11:47:37 +000022 int contourCount = contourList.count();
23 SkScalar bestY = SK_ScalarMin;
24 SkOpSegment* bestSeg = NULL;
caryclark@google.com3e475dc2013-04-11 14:09:50 +000025 int bestTIndex = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 bool bestOpp;
27 bool hitSomething = false;
28 for (int cTest = 0; cTest < contourCount; ++cTest) {
29 SkOpContour* contour = contourList[cTest];
30 bool testOpp = contour->operand() ^ current->operand() ^ opp;
31 if (basePt.fY < contour->bounds().fTop) {
32 continue;
33 }
34 if (bestY > contour->bounds().fBottom) {
35 continue;
36 }
37 int segmentCount = contour->segments().count();
38 for (int test = 0; test < segmentCount; ++test) {
39 SkOpSegment* testSeg = &contour->segments()[test];
40 SkScalar testY = bestY;
41 double testHit;
42 int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
43 testOpp, testSeg == current);
44 if (testTIndex < 0) {
45 if (testTIndex == SK_MinS32) {
46 hitSomething = true;
47 bestSeg = NULL;
48 goto abortContours; // vertical encountered, return and try different point
49 }
50 continue;
51 }
52 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
53 double baseT = current->t(index);
54 double endT = current->t(endIndex);
55 double newMid = (testHit - baseT) / (endT - baseT);
56#if DEBUG_WINDING
57 double midT = current->tAtMid(index, endIndex, mid);
58 SkPoint midXY = current->xyAtT(midT);
59 double newMidT = current->tAtMid(index, endIndex, newMid);
60 SkPoint newXY = current->xyAtT(newMidT);
61 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
62 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
63 current->debugID(), mid, newMid,
64 baseT, current->xAtT(index), current->yAtT(index),
65 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
66 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
67 endT, current->xAtT(endIndex), current->yAtT(endIndex));
68#endif
69 *midPtr = newMid * 2; // calling loop with divide by 2 before continuing
70 return SK_MinS32;
71 }
72 bestSeg = testSeg;
73 *bestHit = testHit;
74 bestOpp = testOpp;
75 bestTIndex = testTIndex;
76 bestY = testY;
77 }
78 }
79abortContours:
80 int result;
81 if (!bestSeg) {
82 result = hitSomething ? SK_MinS32 : 0;
83 } else {
84 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
85 *currentPtr = bestSeg;
86 *indexPtr = bestTIndex;
87 *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
88 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
89 *tryAgain = true;
90 return 0;
91 }
92 result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
caryclark@google.comf11a5af2013-04-10 18:55:11 +000093 SkASSERT(result == SK_MinS32 || *bestDx);
caryclark@google.com07393ca2013-04-08 11:47:37 +000094 }
95 double baseT = current->t(index);
96 double endT = current->t(endIndex);
97 *bestHit = baseT + mid * (endT - baseT);
98 return result;
99}
100
caryclark@google.comd892bd82013-06-17 14:10:36 +0000101SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102 int contourCount = contourList.count();
103 SkOpSegment* result;
104 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
105 SkOpContour* contour = contourList[cIndex];
106 result = contour->undoneSegment(start, end);
107 if (result) {
108 return result;
109 }
110 }
111 return NULL;
112}
113
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000114SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
115 while (chase->count()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000116 SkOpSpan* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000117 chase->pop(&span);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
119 SkOpSegment* segment = backPtr.fOther;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000120 *tIndex = backPtr.fOtherIndex;
121 bool sortable = true;
122 bool done = true;
123 *endIndex = -1;
124 if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
125 &sortable)) {
126 *tIndex = last->start();
127 *endIndex = last->end();
128 #if TRY_ROTATE
129 *chase->insert(0) = span;
130 #else
131 *chase->append() = span;
132 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 return last->segment();
134 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000135 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000136 continue;
137 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000138 if (!sortable) {
139 continue;
140 }
141 // find first angle, initialize winding to computed fWindSum
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000142 const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
143 const SkOpAngle* firstAngle;
144 SkDEBUGCODE(firstAngle = angle);
145 SkDEBUGCODE(bool loop = false);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000146 int winding;
147 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000148 angle = angle->next();
149 SkASSERT(angle != firstAngle || !loop);
150 SkDEBUGCODE(loop |= angle == firstAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000151 segment = angle->segment();
152 winding = segment->windSum(angle);
153 } while (winding == SK_MinS32);
154 int spanWinding = segment->spanSign(angle->start(), angle->end());
155 #if DEBUG_WINDING
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000156 SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 #endif
158 // turn span winding into contour winding
159 if (spanWinding * winding < 0) {
160 winding += spanWinding;
161 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000162 // we care about first sign and whether wind sum indicates this
163 // edge is inside or outside. Maybe need to pass span winding
164 // or first winding or something into this function?
165 // advance to first undone angle, then return it and winding
166 // (to set whether edges are active or not)
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000167 firstAngle = angle;
168 winding -= firstAngle->segment()->spanSign(firstAngle);
169 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000170 segment = angle->segment();
171 int maxWinding = winding;
172 winding -= segment->spanSign(angle);
173 #if DEBUG_SORT
174 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
175 segment->debugID(), maxWinding, winding, angle->sign());
176 #endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000177 *tIndex = angle->start();
178 *endIndex = angle->end();
179 int lesser = SkMin32(*tIndex, *endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000180 const SkOpSpan& nextSpan = segment->span(lesser);
181 if (!nextSpan.fDone) {
182 // FIXME: this be wrong? assign startWinding if edge is in
183 // same direction. If the direction is opposite, winding to
184 // assign is flipped sign or +/- 1?
185 if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
186 maxWinding = winding;
187 }
188 segment->markAndChaseWinding(angle, maxWinding, 0);
189 break;
190 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000191 }
192 *chase->insert(0) = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000193 return segment;
194 }
195 return NULL;
196}
197
caryclark@google.coma5e55922013-05-07 18:51:31 +0000198#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.comd892bd82013-06-17 14:10:36 +0000199void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& 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
caryclark@google.comd892bd82013-06-17 14:10:36 +0000207static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 int* index, int* endIndex, SkPoint* topLeft, bool* unsortable,
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000209 bool* done, bool firstPass) {
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;
212 int lastIndex = -1, lastEndIndex = -1;
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;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000241 result = topStart->findTop(index, endIndex, unsortable, firstPass);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000242 if (!result) {
243 if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
244 *done = true;
245 return NULL;
246 }
247 lastTopStart = topStart;
248 lastIndex = *index;
249 lastEndIndex = *endIndex;
250 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000251 } while (!result);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000252#if 0
caryclark@google.com570863f2013-09-16 15:55:01 +0000253 if (result) {
254 *unsortable = false;
255 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000256#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000257 return result;
258}
259
caryclark@google.comd892bd82013-06-17 14:10:36 +0000260static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000261 SkOpSegment** current, int* index, int* endIndex, double* tHit,
262 SkScalar* hitDx, bool* tryAgain, bool opp) {
263 double test = 0.9;
264 int contourWinding;
265 do {
266 contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
267 tryAgain, &test, opp);
268 if (contourWinding != SK_MinS32 || *tryAgain) {
269 return contourWinding;
270 }
271 test /= 2;
272 } while (!approximately_negative(test));
273 SkASSERT(0); // should be OK to comment out, but interested when this hits
274 return contourWinding;
275}
276
caryclark@google.comd892bd82013-06-17 14:10:36 +0000277static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278 SkOpSegment** current, int* index, int* endIndex) {
279 if (!(*current)->isVertical(*index, *endIndex)) {
280 return;
281 }
282 int contourCount = contourList.count();
283 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
284 SkOpContour* contour = contourList[cIndex];
285 if (contour->done()) {
286 continue;
287 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000288 SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
289 if (nonVertical) {
290 *current = nonVertical;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000291 return;
292 }
293 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000294 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000295}
296
caryclark@google.com570863f2013-09-16 15:55:01 +0000297SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
298 SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000299 int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000300 SkOpSegment* current = findSortableTop(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000301 done, firstPass);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000302 if (!current) {
303 return NULL;
304 }
305 const int index = *indexPtr;
306 const int endIndex = *endIndexPtr;
307 if (*firstContour) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000308 current->initWinding(index, endIndex, angleIncludeType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000309 *firstContour = false;
310 return current;
311 }
312 int minIndex = SkMin32(index, endIndex);
313 int sumWinding = current->windSum(minIndex);
314 if (sumWinding != SK_MinS32) {
315 return current;
316 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000317 SkASSERT(current->windSum(SkMin32(index, endIndex)) == SK_MinS32);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000318 const SkOpSpan& span = current->span(endIndex);
319 if ((index < endIndex ? span.fFromAngleIndex : span.fToAngleIndex) < 0) {
320 current->addSimpleAngle(endIndex);
321 }
322 sumWinding = current->computeSum(index, endIndex, angleIncludeType);
caryclark@google.com570863f2013-09-16 15:55:01 +0000323 if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000324 return current;
325 }
326 int contourWinding;
327 int oppContourWinding = 0;
328 // the simple upward projection of the unresolved points hit unsortable angles
329 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
330 bool tryAgain;
331 double tHit;
332 SkScalar hitDx = 0;
333 SkScalar hitOppDx = 0;
334 do {
335 // if current is vertical, find another candidate which is not
336 // if only remaining candidates are vertical, then they can be marked done
337 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
338 skipVertical(contourList, &current, indexPtr, endIndexPtr);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000339 SkASSERT(current); // FIXME: if null, all remaining are vertical
caryclark@google.com07393ca2013-04-08 11:47:37 +0000340 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
341 tryAgain = false;
342 contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
343 &hitDx, &tryAgain, false);
344 if (tryAgain) {
345 continue;
346 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000347 if (angleIncludeType < SkOpAngle::kBinarySingle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000348 break;
349 }
350 oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
351 &hitOppDx, &tryAgain, true);
352 } while (tryAgain);
353 current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
354 hitOppDx);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000355 if (current->done()) {
356 return NULL;
357 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000358 return current;
359}
360
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000361static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) {
362 int contourCount = (*contourList).count();
363 for (int cTest = 0; cTest < contourCount; ++cTest) {
364 SkOpContour* contour = (*contourList)[cTest];
365 if (!contour->calcAngles()) {
366 return false;
367 }
368 }
369 return true;
370}
371
372static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
373 int contourCount = (*contourList).count();
374 for (int cTest = 0; cTest < contourCount; ++cTest) {
375 SkOpContour* contour = (*contourList)[cTest];
376 contour->checkDuplicates();
377 }
378}
379
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000380static void checkEnds(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000381 // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
382 // instead, look to see if the connecting curve intersected at that same end.
383 int contourCount = (*contourList).count();
384 for (int cTest = 0; cTest < contourCount; ++cTest) {
385 SkOpContour* contour = (*contourList)[cTest];
386 contour->checkEnds();
387 }
388}
389
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000390static void checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
391 // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
392 // instead, look to see if the connecting curve intersected at that same end.
393 int contourCount = (*contourList).count();
394 for (int cTest = 0; cTest < contourCount; ++cTest) {
395 SkOpContour* contour = (*contourList)[cTest];
396 contour->checkMultiples();
397 }
398}
399
400// A small interval of a pair of curves may collapse to lines for each, triggering coincidence
401static void checkSmall(SkTArray<SkOpContour*, true>* contourList) {
402 int contourCount = (*contourList).count();
403 for (int cTest = 0; cTest < contourCount; ++cTest) {
404 SkOpContour* contour = (*contourList)[cTest];
405 contour->checkSmall();
406 }
407}
408
caryclark@google.com570863f2013-09-16 15:55:01 +0000409// A tiny interval may indicate an undiscovered coincidence. Find and fix.
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000410static void checkTiny(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000411 int contourCount = (*contourList).count();
412 for (int cTest = 0; cTest < contourCount; ++cTest) {
413 SkOpContour* contour = (*contourList)[cTest];
414 contour->checkTiny();
415 }
416}
417
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000418static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000419 int contourCount = (*contourList).count();
420 for (int cTest = 0; cTest < contourCount; ++cTest) {
421 SkOpContour* contour = (*contourList)[cTest];
422 contour->fixOtherTIndex();
423 }
424}
425
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000426static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) {
427 int contourCount = (*contourList).count();
428 for (int cTest = 0; cTest < contourCount; ++cTest) {
429 SkOpContour* contour = (*contourList)[cTest];
430 contour->joinCoincidence();
431 }
432}
433
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000434static void sortAngles(SkTArray<SkOpContour*, true>* contourList) {
435 int contourCount = (*contourList).count();
436 for (int cTest = 0; cTest < contourCount; ++cTest) {
437 SkOpContour* contour = (*contourList)[cTest];
438 contour->sortAngles();
439 }
440}
441
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000442static void sortSegments(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000443 int contourCount = (*contourList).count();
444 for (int cTest = 0; cTest < contourCount; ++cTest) {
445 SkOpContour* contour = (*contourList)[cTest];
446 contour->sortSegments();
447 }
448}
449
caryclark@google.comd892bd82013-06-17 14:10:36 +0000450void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000451 bool evenOdd, bool oppEvenOdd) {
452 int count = contours.count();
453 if (count == 0) {
454 return;
455 }
456 for (int index = 0; index < count; ++index) {
457 SkOpContour& contour = contours[index];
458 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
caryclark@google.comd892bd82013-06-17 14:10:36 +0000459 list.push_back(&contour);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000460 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000461 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000462}
463
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000464class DistanceLessThan {
465public:
466 DistanceLessThan(double* distances) : fDistances(distances) { }
467 double* fDistances;
468 bool operator()(const int one, const int two) {
469 return fDistances[one] < fDistances[two];
470 }
471};
472
caryclark@google.com07393ca2013-04-08 11:47:37 +0000473 /*
474 check start and end of each contour
475 if not the same, record them
476 match them up
477 connect closest
478 reassemble contour pieces into new path
479 */
480void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
481#if DEBUG_PATH_CONSTRUCTION
482 SkDebugf("%s\n", __FUNCTION__);
483#endif
484 SkTArray<SkOpContour> contours;
485 SkOpEdgeBuilder builder(path, contours);
486 builder.finish();
487 int count = contours.count();
488 int outer;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000489 SkTArray<int, true> runs(count); // indices of partial contours
caryclark@google.com07393ca2013-04-08 11:47:37 +0000490 for (outer = 0; outer < count; ++outer) {
491 const SkOpContour& eContour = contours[outer];
492 const SkPoint& eStart = eContour.start();
493 const SkPoint& eEnd = eContour.end();
494#if DEBUG_ASSEMBLE
495 SkDebugf("%s contour", __FUNCTION__);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000496 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000497 SkDebugf("[%d]", runs.count());
498 } else {
499 SkDebugf(" ");
500 }
501 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
502 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
503#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000504 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000505 eContour.toPath(simple);
506 continue;
507 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000508 runs.push_back(outer);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000509 }
510 count = runs.count();
511 if (count == 0) {
512 return;
513 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000514 SkTArray<int, true> sLink, eLink;
515 sLink.push_back_n(count);
516 eLink.push_back_n(count);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000517 int rIndex, iIndex;
518 for (rIndex = 0; rIndex < count; ++rIndex) {
519 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
520 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000521 const int ends = count * 2; // all starts and ends
522 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
caryclark@google.comd892bd82013-06-17 14:10:36 +0000523 SkTArray<double, true> distances;
524 distances.push_back_n(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000525 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
526 outer = runs[rIndex >> 1];
527 const SkOpContour& oContour = contours[outer];
528 const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
529 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
530 * ends - rIndex - 1;
531 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
532 int inner = runs[iIndex >> 1];
533 const SkOpContour& iContour = contours[inner];
534 const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
535 double dx = iPt.fX - oPt.fX;
536 double dy = iPt.fY - oPt.fY;
537 double dist = dx * dx + dy * dy;
538 distances[row + iIndex] = dist; // oStart distance from iStart
539 }
540 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000541 SkTArray<int, true> sortedDist;
542 sortedDist.push_back_n(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000543 for (rIndex = 0; rIndex < entries; ++rIndex) {
544 sortedDist[rIndex] = rIndex;
545 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000546 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000547 int remaining = count; // number of start/end pairs
548 for (rIndex = 0; rIndex < entries; ++rIndex) {
549 int pair = sortedDist[rIndex];
550 int row = pair / ends;
551 int col = pair - row * ends;
552 int thingOne = row < col ? row : ends - row - 2;
553 int ndxOne = thingOne >> 1;
554 bool endOne = thingOne & 1;
555 int* linkOne = endOne ? eLink.begin() : sLink.begin();
556 if (linkOne[ndxOne] != SK_MaxS32) {
557 continue;
558 }
559 int thingTwo = row < col ? col : ends - row + col - 1;
560 int ndxTwo = thingTwo >> 1;
561 bool endTwo = thingTwo & 1;
562 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
563 if (linkTwo[ndxTwo] != SK_MaxS32) {
564 continue;
565 }
566 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
567 bool flip = endOne == endTwo;
568 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
569 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
570 if (!--remaining) {
571 break;
572 }
573 }
574 SkASSERT(!remaining);
575#if DEBUG_ASSEMBLE
576 for (rIndex = 0; rIndex < count; ++rIndex) {
577 int s = sLink[rIndex];
578 int e = eLink[rIndex];
579 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
580 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
581 }
582#endif
583 rIndex = 0;
584 do {
585 bool forward = true;
586 bool first = true;
587 int sIndex = sLink[rIndex];
588 SkASSERT(sIndex != SK_MaxS32);
589 sLink[rIndex] = SK_MaxS32;
590 int eIndex;
591 if (sIndex < 0) {
592 eIndex = sLink[~sIndex];
593 sLink[~sIndex] = SK_MaxS32;
594 } else {
595 eIndex = eLink[sIndex];
596 eLink[sIndex] = SK_MaxS32;
597 }
598 SkASSERT(eIndex != SK_MaxS32);
599#if DEBUG_ASSEMBLE
600 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
601 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
602 eIndex < 0 ? ~eIndex : eIndex);
603#endif
604 do {
605 outer = runs[rIndex];
606 const SkOpContour& contour = contours[outer];
607 if (first) {
608 first = false;
609 const SkPoint* startPtr = &contour.start();
610 simple->deferredMove(startPtr[0]);
611 }
612 if (forward) {
613 contour.toPartialForward(simple);
614 } else {
615 contour.toPartialBackward(simple);
616 }
617#if DEBUG_ASSEMBLE
618 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
619 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
620 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
621#endif
622 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
623 simple->close();
624 break;
625 }
626 if (forward) {
627 eIndex = eLink[rIndex];
628 SkASSERT(eIndex != SK_MaxS32);
629 eLink[rIndex] = SK_MaxS32;
630 if (eIndex >= 0) {
631 SkASSERT(sLink[eIndex] == rIndex);
632 sLink[eIndex] = SK_MaxS32;
633 } else {
634 SkASSERT(eLink[~eIndex] == ~rIndex);
635 eLink[~eIndex] = SK_MaxS32;
636 }
637 } else {
638 eIndex = sLink[rIndex];
639 SkASSERT(eIndex != SK_MaxS32);
640 sLink[rIndex] = SK_MaxS32;
641 if (eIndex >= 0) {
642 SkASSERT(eLink[eIndex] == rIndex);
643 eLink[eIndex] = SK_MaxS32;
644 } else {
645 SkASSERT(sLink[~eIndex] == ~rIndex);
646 sLink[~eIndex] = SK_MaxS32;
647 }
648 }
649 rIndex = eIndex;
650 if (rIndex < 0) {
651 forward ^= 1;
652 rIndex = ~rIndex;
653 }
654 } while (true);
655 for (rIndex = 0; rIndex < count; ++rIndex) {
656 if (sLink[rIndex] != SK_MaxS32) {
657 break;
658 }
659 }
660 } while (rIndex < count);
661#if DEBUG_ASSEMBLE
662 for (rIndex = 0; rIndex < count; ++rIndex) {
663 SkASSERT(sLink[rIndex] == SK_MaxS32);
664 SkASSERT(eLink[rIndex] == SK_MaxS32);
665 }
666#endif
667}
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000668
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000669bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000670#if DEBUG_SHOW_WINDING
671 SkOpContour::debugShowWindingValues(contourList);
672#endif
673 CoincidenceCheck(contourList, total);
674#if DEBUG_SHOW_WINDING
675 SkOpContour::debugShowWindingValues(contourList);
676#endif
677 fixOtherTIndex(contourList);
678 checkEnds(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000679 checkMultiples(contourList);
680 checkDuplicates(contourList);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000681 checkTiny(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000682 checkSmall(contourList);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000683 joinCoincidence(contourList);
684 sortSegments(contourList);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000685 if (!calcAngles(contourList)) {
686 return false;
687 }
688 sortAngles(contourList);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000689#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
690 DebugShowActiveSpans(*contourList);
691#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000692 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000693}