blob: 28c072a3c1340200c2cd7f1b2514bd20b19e2c05 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2* Copyright 2013 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*/
7#include "SkIntersections.h"
8#include "SkOpContour.h"
9#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000010#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000012bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com07393ca2013-04-08 11:47:37 +000013 const SkIntersections& ts, bool swap) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000014 SkPoint pt0 = ts.pt(0).asSkPoint();
15 SkPoint pt1 = ts.pt(1).asSkPoint();
caryclark65f55312014-11-13 06:58:52 -080016 if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000017 // 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
21 return false;
22 }
caryclark@google.comd892bd82013-06-17 14:10:36 +000023 SkCoincidence& coincidence = fCoincidences.push_back();
caryclark@google.com570863f2013-09-16 15:55:01 +000024 coincidence.fOther = other;
caryclark@google.com07393ca2013-04-08 11:47:37 +000025 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];
caryclarkdac1d172014-06-17 05:15:38 -070031 coincidence.fPts[swap][0] = pt0;
32 coincidence.fPts[swap][1] = pt1;
33 bool nearStart = ts.nearlySame(0);
34 bool nearEnd = ts.nearlySame(1);
35 coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
36 coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
37 coincidence.fNearly[0] = nearStart;
38 coincidence.fNearly[1] = nearEnd;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000039 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000040}
41
42SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
43 int segmentCount = fSortedSegments.count();
44 SkASSERT(segmentCount > 0);
45 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
46 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
47 if (testSegment->done()) {
48 continue;
49 }
50 *start = *end = 0;
51 while (testSegment->nextCandidate(start, end)) {
52 if (!testSegment->isVertical(*start, *end)) {
53 return testSegment;
54 }
55 }
56 }
57 return NULL;
58}
59
caryclark361b8b02014-09-08 10:25:38 -070060// if one is very large the smaller may have collapsed to nothing
61static void bump_out_close_span(double* startTPtr, double* endTPtr) {
62 double startT = *startTPtr;
63 double endT = *endTPtr;
64 if (approximately_negative(endT - startT)) {
65 if (endT <= 1 - FLT_EPSILON) {
66 *endTPtr += FLT_EPSILON;
67 SkASSERT(*endTPtr <= 1);
68 } else {
69 *startTPtr -= FLT_EPSILON;
70 SkASSERT(*startTPtr >= 0);
71 }
72 }
73}
74
caryclark@google.com07393ca2013-04-08 11:47:37 +000075// first pass, add missing T values
76// second pass, determine winding values of overlaps
77void SkOpContour::addCoincidentPoints() {
78 int count = fCoincidences.count();
79 for (int index = 0; index < count; ++index) {
80 SkCoincidence& coincidence = fCoincidences[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 int thisIndex = coincidence.fSegments[0];
82 SkOpSegment& thisOne = fSegments[thisIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +000083 SkOpContour* otherContour = coincidence.fOther;
caryclark@google.com07393ca2013-04-08 11:47:37 +000084 int otherIndex = coincidence.fSegments[1];
85 SkOpSegment& other = otherContour->fSegments[otherIndex];
86 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
87 // OPTIMIZATION: remove from array
88 continue;
89 }
90 #if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000091 thisOne.debugShowTs("-");
92 other.debugShowTs("o");
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 #endif
94 double startT = coincidence.fTs[0][0];
95 double endT = coincidence.fTs[0][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +000096 bool startSwapped, oStartSwapped, cancelers;
97 if ((cancelers = startSwapped = startT > endT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000098 SkTSwap(startT, endT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 }
caryclark361b8b02014-09-08 10:25:38 -0700100 bump_out_close_span(&startT, &endT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 SkASSERT(!approximately_negative(endT - startT));
102 double oStartT = coincidence.fTs[1][0];
103 double oEndT = coincidence.fTs[1][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +0000104 if ((oStartSwapped = oStartT > oEndT)) {
105 SkTSwap(oStartT, oEndT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000106 cancelers ^= true;
107 }
caryclark361b8b02014-09-08 10:25:38 -0700108 bump_out_close_span(&oStartT, &oEndT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000109 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclarkdac1d172014-06-17 05:15:38 -0700110 const SkPoint& startPt = coincidence.fPts[0][startSwapped];
caryclark@google.coma5e55922013-05-07 18:51:31 +0000111 if (cancelers) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 // make sure startT and endT have t entries
113 if (startT > 0 || oEndT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000114 || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
caryclarkdac1d172014-06-17 05:15:38 -0700115 thisOne.addTPair(startT, &other, oEndT, true, startPt,
116 coincidence.fPts[1][startSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000117 }
caryclarkdac1d172014-06-17 05:15:38 -0700118 const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 if (oStartT > 0 || endT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000120 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
caryclarkdac1d172014-06-17 05:15:38 -0700121 other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
122 coincidence.fPts[0][oStartSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 }
124 } else {
125 if (startT > 0 || oStartT > 0
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000126 || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
caryclarkdac1d172014-06-17 05:15:38 -0700127 thisOne.addTPair(startT, &other, oStartT, true, startPt,
128 coincidence.fPts[1][startSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000129 }
caryclarkdac1d172014-06-17 05:15:38 -0700130 const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000131 if (endT < 1 || oEndT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000132 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
caryclarkdac1d172014-06-17 05:15:38 -0700133 other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
134 coincidence.fPts[0][!oStartSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000135 }
136 }
137 #if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000138 thisOne.debugShowTs("+");
139 other.debugShowTs("o");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000140 #endif
141 }
caryclarkdac1d172014-06-17 05:15:38 -0700142 // if there are multiple pairs of coincidence that share an edge, see if the opposite
143 // are also coincident
144 for (int index = 0; index < count - 1; ++index) {
145 const SkCoincidence& coincidence = fCoincidences[index];
146 int thisIndex = coincidence.fSegments[0];
147 SkOpContour* otherContour = coincidence.fOther;
148 int otherIndex = coincidence.fSegments[1];
149 for (int idx2 = 1; idx2 < count; ++idx2) {
150 const SkCoincidence& innerCoin = fCoincidences[idx2];
151 int innerThisIndex = innerCoin.fSegments[0];
152 if (thisIndex == innerThisIndex) {
153 checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
154 }
155 if (this == otherContour && otherIndex == innerThisIndex) {
156 checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
157 }
158 SkOpContour* innerOtherContour = innerCoin.fOther;
159 innerThisIndex = innerCoin.fSegments[1];
160 if (this == innerOtherContour && thisIndex == innerThisIndex) {
161 checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
162 }
163 if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
164 checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
165 }
166 }
167 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000168}
169
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000170bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com570863f2013-09-16 15:55:01 +0000171 const SkIntersections& ts, int ptIndex, bool swap) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000172 SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
173 SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
174 if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
175 // FIXME: one could imagine a case where it would be incorrect to ignore this
176 // suppose two self-intersecting cubics overlap to form a partial coincidence --
177 // although it isn't clear why the regular coincidence could wouldn't pick this up
178 // this is exceptional enough to ignore for now
179 return false;
180 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000181 SkCoincidence& coincidence = fPartialCoincidences.push_back();
182 coincidence.fOther = other;
183 coincidence.fSegments[0] = index;
184 coincidence.fSegments[1] = otherIndex;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000185 coincidence.fTs[swap][0] = ts[0][ptIndex];
186 coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
187 coincidence.fTs[!swap][0] = ts[1][ptIndex];
188 coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
caryclarkdac1d172014-06-17 05:15:38 -0700189 coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
190 coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
191 coincidence.fNearly[0] = 0;
192 coincidence.fNearly[1] = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000193 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000194}
195
caryclarkdac1d172014-06-17 05:15:38 -0700196void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
197 SkCoincidence* coincidence) {
198 for (int idx2 = 0; idx2 < 2; ++idx2) {
199 if (coincidence->fPts[0][idx2] == aligned.fOldPt
200 && coincidence->fTs[swap][idx2] == aligned.fOldT) {
201 SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
202 coincidence->fPts[0][idx2] = aligned.fPt;
203 SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
204 coincidence->fTs[swap][idx2] = aligned.fT;
205 }
206 }
207}
208
209void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
210 SkTArray<SkCoincidence, true>* coincidences) {
211 int count = coincidences->count();
212 for (int index = 0; index < count; ++index) {
213 SkCoincidence& coincidence = (*coincidences)[index];
214 int thisIndex = coincidence.fSegments[0];
215 const SkOpSegment* thisOne = &fSegments[thisIndex];
216 const SkOpContour* otherContour = coincidence.fOther;
217 int otherIndex = coincidence.fSegments[1];
218 const SkOpSegment* other = &otherContour->fSegments[otherIndex];
219 if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
220 align(aligned, false, &coincidence);
221 } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
222 align(aligned, true, &coincidence);
223 }
224 }
225}
226
227void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
228 bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
229 int zeroPt;
230 if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
231 alignPt(segmentIndex, point, zeroPt);
232 }
233 if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
234 other->alignPt(otherIndex, point, zeroPt);
235 }
236}
237
238void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
239 const SkOpSegment& segment = fSegments[index];
240 if (0 == zeroPt) {
241 *point = segment.pts()[0];
242 } else {
243 *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
244 }
245}
246
247int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
248 double tVal = (*ts)[swap][tIndex];
249 if (tVal != 0 && precisely_zero(tVal)) {
250 ts->set(swap, tIndex, 0);
251 return 0;
252 }
253 if (tVal != 1 && precisely_equal(tVal, 1)) {
254 ts->set(swap, tIndex, 1);
255 return 1;
256 }
257 return -1;
258}
259
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000260bool SkOpContour::calcAngles() {
261 int segmentCount = fSegments.count();
262 for (int test = 0; test < segmentCount; ++test) {
263 if (!fSegments[test].calcAngles()) {
264 return false;
265 }
266 }
267 return true;
268}
269
caryclark630240d2014-09-19 06:33:31 -0700270bool SkOpContour::calcCoincidentWinding() {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000271 int count = fCoincidences.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000272#if DEBUG_CONCIDENT
273 if (count > 0) {
274 SkDebugf("%s count=%d\n", __FUNCTION__, count);
275 }
276#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277 for (int index = 0; index < count; ++index) {
278 SkCoincidence& coincidence = fCoincidences[index];
caryclark630240d2014-09-19 06:33:31 -0700279 if (!calcCommonCoincidentWinding(coincidence)) {
280 return false;
281 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000282 }
caryclark630240d2014-09-19 06:33:31 -0700283 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000284}
285
caryclark@google.com570863f2013-09-16 15:55:01 +0000286void SkOpContour::calcPartialCoincidentWinding() {
287 int count = fPartialCoincidences.count();
288#if DEBUG_CONCIDENT
289 if (count > 0) {
290 SkDebugf("%s count=%d\n", __FUNCTION__, count);
291 }
292#endif
293 for (int index = 0; index < count; ++index) {
294 SkCoincidence& coincidence = fPartialCoincidences[index];
caryclarkdac1d172014-06-17 05:15:38 -0700295 calcCommonCoincidentWinding(coincidence);
296 }
297 // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
298 // are also coincident
299 for (int index = 0; index < count - 1; ++index) {
300 const SkCoincidence& coincidence = fPartialCoincidences[index];
301 int thisIndex = coincidence.fSegments[0];
302 SkOpContour* otherContour = coincidence.fOther;
303 int otherIndex = coincidence.fSegments[1];
304 for (int idx2 = 1; idx2 < count; ++idx2) {
305 const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
306 int innerThisIndex = innerCoin.fSegments[0];
307 if (thisIndex == innerThisIndex) {
308 checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
309 }
310 if (this == otherContour && otherIndex == innerThisIndex) {
311 checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
312 }
313 SkOpContour* innerOtherContour = innerCoin.fOther;
314 innerThisIndex = innerCoin.fSegments[1];
315 if (this == innerOtherContour && thisIndex == innerThisIndex) {
316 checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
317 }
318 if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
319 checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
320 }
321 }
322 }
323}
324
325void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
326 const SkCoincidence& twoCoin, int twoIdx, bool partial) {
327 SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
328 SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
329 // look for common overlap
330 double min = SK_ScalarMax;
331 double max = SK_ScalarMin;
332 double min1 = oneCoin.fTs[!oneIdx][0];
333 double max1 = oneCoin.fTs[!oneIdx][1];
334 double min2 = twoCoin.fTs[!twoIdx][0];
335 double max2 = twoCoin.fTs[!twoIdx][1];
336 bool cancelers = (min1 < max1) != (min2 < max2);
337 if (min1 > max1) {
338 SkTSwap(min1, max1);
339 }
340 if (min2 > max2) {
341 SkTSwap(min2, max2);
342 }
343 if (between(min1, min2, max1)) {
344 min = min2;
345 }
346 if (between(min1, max2, max1)) {
347 max = max2;
348 }
349 if (between(min2, min1, max2)) {
350 min = SkTMin(min, min1);
351 }
352 if (between(min2, max1, max2)) {
353 max = SkTMax(max, max1);
354 }
355 if (min >= max) {
356 return; // no overlap
357 }
358 // look to see if opposite are different segments
359 int seg1Index = oneCoin.fSegments[oneIdx];
360 int seg2Index = twoCoin.fSegments[twoIdx];
361 if (seg1Index == seg2Index) {
362 return;
363 }
364 SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
365 SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
366 SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
367 SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
368 // find opposite t value ranges corresponding to reference min/max range
369 const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
370 const int refSegIndex = oneCoin.fSegments[!oneIdx];
371 const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
372 int seg1Start = segment1->findOtherT(min, refSegment);
373 int seg1End = segment1->findOtherT(max, refSegment);
374 int seg2Start = segment2->findOtherT(min, refSegment);
375 int seg2End = segment2->findOtherT(max, refSegment);
376 // if the opposite pairs already contain min/max, we're done
377 if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
378 return;
379 }
380 double loEnd = SkTMin(min1, min2);
381 double hiEnd = SkTMax(max1, max2);
382 // insert the missing coincident point(s)
383 double missingT1 = -1;
384 double otherT1 = -1;
385 if (seg1Start < 0) {
386 if (seg2Start < 0) {
387 return;
388 }
389 missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
390 segment2, seg1End);
391 if (missingT1 < 0) {
392 return;
393 }
394 const SkOpSpan* missingSpan = &segment2->span(seg2Start);
395 otherT1 = missingSpan->fT;
396 } else if (seg2Start < 0) {
397 SkASSERT(seg1Start >= 0);
398 missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
399 segment1, seg2End);
400 if (missingT1 < 0) {
401 return;
402 }
403 const SkOpSpan* missingSpan = &segment1->span(seg1Start);
404 otherT1 = missingSpan->fT;
405 }
406 SkPoint missingPt1;
407 SkOpSegment* addTo1 = NULL;
408 SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
409 int minTIndex = refSegment->findExactT(min, addOther1);
410 SkASSERT(minTIndex >= 0);
411 if (missingT1 >= 0) {
412 missingPt1 = refSegment->span(minTIndex).fPt;
413 addTo1 = seg1Start < 0 ? segment1 : segment2;
414 }
415 double missingT2 = -1;
416 double otherT2 = -1;
417 if (seg1End < 0) {
418 if (seg2End < 0) {
419 return;
420 }
421 missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
422 segment2, seg1Start);
423 if (missingT2 < 0) {
424 return;
425 }
426 const SkOpSpan* missingSpan = &segment2->span(seg2End);
427 otherT2 = missingSpan->fT;
428 } else if (seg2End < 0) {
429 SkASSERT(seg1End >= 0);
430 missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
431 segment1, seg2Start);
432 if (missingT2 < 0) {
433 return;
434 }
435 const SkOpSpan* missingSpan = &segment1->span(seg1End);
436 otherT2 = missingSpan->fT;
437 }
438 SkPoint missingPt2;
439 SkOpSegment* addTo2 = NULL;
440 SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
441 int maxTIndex = refSegment->findExactT(max, addOther2);
442 SkASSERT(maxTIndex >= 0);
443 if (missingT2 >= 0) {
444 missingPt2 = refSegment->span(maxTIndex).fPt;
445 addTo2 = seg1End < 0 ? segment1 : segment2;
446 }
447 if (missingT1 >= 0) {
448 addTo1->pinT(missingPt1, &missingT1);
449 addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
450 } else {
451 SkASSERT(minTIndex >= 0);
452 missingPt1 = refSegment->span(minTIndex).fPt;
453 }
454 if (missingT2 >= 0) {
455 addTo2->pinT(missingPt2, &missingT2);
456 addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
457 } else {
458 SkASSERT(minTIndex >= 0);
459 missingPt2 = refSegment->span(maxTIndex).fPt;
460 }
461 if (!partial) {
462 return;
463 }
464 if (cancelers) {
465 if (missingT1 >= 0) {
caryclark5e27e0e2014-08-12 07:46:33 -0700466 if (addTo1->reversePoints(missingPt1, missingPt2)) {
467 SkTSwap(missingPt1, missingPt2);
468 }
caryclarkdac1d172014-06-17 05:15:38 -0700469 addTo1->addTCancel(missingPt1, missingPt2, addOther1);
470 } else {
caryclark5e27e0e2014-08-12 07:46:33 -0700471 if (addTo2->reversePoints(missingPt1, missingPt2)) {
472 SkTSwap(missingPt1, missingPt2);
473 }
caryclarkdac1d172014-06-17 05:15:38 -0700474 addTo2->addTCancel(missingPt1, missingPt2, addOther2);
475 }
476 } else if (missingT1 >= 0) {
caryclark630240d2014-09-19 06:33:31 -0700477 SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
478 addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
caryclarkdac1d172014-06-17 05:15:38 -0700479 } else {
caryclark630240d2014-09-19 06:33:31 -0700480 SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
481 addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
caryclark@google.com570863f2013-09-16 15:55:01 +0000482 }
483}
484
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000485void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
486 int count = coincidences.count();
487#if DEBUG_CONCIDENT
488 if (count > 0) {
489 SkDebugf("%s count=%d\n", __FUNCTION__, count);
490 }
491#endif
492 // look for a lineup where the partial implies another adjoining coincidence
493 for (int index = 0; index < count; ++index) {
494 const SkCoincidence& coincidence = coincidences[index];
495 int thisIndex = coincidence.fSegments[0];
496 SkOpSegment& thisOne = fSegments[thisIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700497 if (thisOne.done()) {
498 continue;
499 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000500 SkOpContour* otherContour = coincidence.fOther;
501 int otherIndex = coincidence.fSegments[1];
502 SkOpSegment& other = otherContour->fSegments[otherIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700503 if (other.done()) {
504 continue;
505 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000506 double startT = coincidence.fTs[0][0];
507 double endT = coincidence.fTs[0][1];
508 if (startT == endT) { // this can happen in very large compares
509 continue;
510 }
511 double oStartT = coincidence.fTs[1][0];
512 double oEndT = coincidence.fTs[1][1];
513 if (oStartT == oEndT) {
514 continue;
515 }
516 bool swapStart = startT > endT;
517 bool swapOther = oStartT > oEndT;
caryclarkdac1d172014-06-17 05:15:38 -0700518 const SkPoint* startPt = &coincidence.fPts[0][0];
519 const SkPoint* endPt = &coincidence.fPts[0][1];
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000520 if (swapStart) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000521 SkTSwap(startT, endT);
522 SkTSwap(oStartT, oEndT);
523 SkTSwap(startPt, endPt);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000524 }
525 bool cancel = swapOther != swapStart;
526 int step = swapStart ? -1 : 1;
527 int oStep = swapOther ? -1 : 1;
528 double oMatchStart = cancel ? oEndT : oStartT;
529 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
530 bool added = false;
531 if (oMatchStart != 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000532 const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
533 added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000534 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000535 if (!cancel && startT != 0 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000536 (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000537 }
538 }
539 double oMatchEnd = cancel ? oStartT : oEndT;
540 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
541 bool added = false;
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000542 if (cancel && endT != 1 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000543 (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000544 }
545 }
546 }
547}
548
caryclark630240d2014-09-19 06:33:31 -0700549bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
caryclarkdac1d172014-06-17 05:15:38 -0700550 if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
caryclark630240d2014-09-19 06:33:31 -0700551 return true;
caryclarkdac1d172014-06-17 05:15:38 -0700552 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000553 int thisIndex = coincidence.fSegments[0];
554 SkOpSegment& thisOne = fSegments[thisIndex];
555 if (thisOne.done()) {
caryclark630240d2014-09-19 06:33:31 -0700556 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000557 }
558 SkOpContour* otherContour = coincidence.fOther;
559 int otherIndex = coincidence.fSegments[1];
560 SkOpSegment& other = otherContour->fSegments[otherIndex];
561 if (other.done()) {
caryclark630240d2014-09-19 06:33:31 -0700562 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000563 }
564 double startT = coincidence.fTs[0][0];
565 double endT = coincidence.fTs[0][1];
caryclarkdac1d172014-06-17 05:15:38 -0700566 const SkPoint* startPt = &coincidence.fPts[0][0];
567 const SkPoint* endPt = &coincidence.fPts[0][1];
caryclark@google.com570863f2013-09-16 15:55:01 +0000568 bool cancelers;
569 if ((cancelers = startT > endT)) {
570 SkTSwap<double>(startT, endT);
571 SkTSwap<const SkPoint*>(startPt, endPt);
572 }
caryclark361b8b02014-09-08 10:25:38 -0700573 bump_out_close_span(&startT, &endT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000574 SkASSERT(!approximately_negative(endT - startT));
575 double oStartT = coincidence.fTs[1][0];
576 double oEndT = coincidence.fTs[1][1];
577 if (oStartT > oEndT) {
578 SkTSwap<double>(oStartT, oEndT);
579 cancelers ^= true;
580 }
caryclark361b8b02014-09-08 10:25:38 -0700581 bump_out_close_span(&oStartT, &oEndT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000582 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark630240d2014-09-19 06:33:31 -0700583 bool success = true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000584 if (cancelers) {
585 thisOne.addTCancel(*startPt, *endPt, &other);
586 } else {
caryclark630240d2014-09-19 06:33:31 -0700587 success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
caryclark@google.com570863f2013-09-16 15:55:01 +0000588 }
589#if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000590 thisOne.debugShowTs("p");
591 other.debugShowTs("o");
caryclark@google.com570863f2013-09-16 15:55:01 +0000592#endif
caryclark630240d2014-09-19 06:33:31 -0700593 return success;
caryclark@google.com570863f2013-09-16 15:55:01 +0000594}
595
caryclarkdac1d172014-06-17 05:15:38 -0700596void SkOpContour::resolveNearCoincidence() {
597 int count = fCoincidences.count();
598 for (int index = 0; index < count; ++index) {
599 SkCoincidence& coincidence = fCoincidences[index];
600 if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
601 continue;
602 }
603 int thisIndex = coincidence.fSegments[0];
604 SkOpSegment& thisOne = fSegments[thisIndex];
605 SkOpContour* otherContour = coincidence.fOther;
606 int otherIndex = coincidence.fSegments[1];
607 SkOpSegment& other = otherContour->fSegments[otherIndex];
608 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
609 // OPTIMIZATION: remove from coincidence array
610 continue;
611 }
612 #if DEBUG_CONCIDENT
613 thisOne.debugShowTs("-");
614 other.debugShowTs("o");
615 #endif
616 double startT = coincidence.fTs[0][0];
617 double endT = coincidence.fTs[0][1];
618 bool cancelers;
619 if ((cancelers = startT > endT)) {
620 SkTSwap<double>(startT, endT);
621 }
622 if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
623 if (endT <= 1 - FLT_EPSILON) {
624 endT += FLT_EPSILON;
625 SkASSERT(endT <= 1);
626 } else {
627 startT -= FLT_EPSILON;
628 SkASSERT(startT >= 0);
629 }
630 }
631 SkASSERT(!approximately_negative(endT - startT));
632 double oStartT = coincidence.fTs[1][0];
633 double oEndT = coincidence.fTs[1][1];
634 if (oStartT > oEndT) {
635 SkTSwap<double>(oStartT, oEndT);
636 cancelers ^= true;
637 }
638 SkASSERT(!approximately_negative(oEndT - oStartT));
639 if (cancelers) {
640 thisOne.blindCancel(coincidence, &other);
641 } else {
642 thisOne.blindCoincident(coincidence, &other);
643 }
644 }
645}
646
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000647void SkOpContour::sortAngles() {
648 int segmentCount = fSegments.count();
649 for (int test = 0; test < segmentCount; ++test) {
650 fSegments[test].sortAngles();
651 }
652}
653
caryclark@google.com07393ca2013-04-08 11:47:37 +0000654void SkOpContour::sortSegments() {
655 int segmentCount = fSegments.count();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000656 fSortedSegments.push_back_n(segmentCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000657 for (int test = 0; test < segmentCount; ++test) {
caryclark@google.comd892bd82013-06-17 14:10:36 +0000658 fSortedSegments[test] = &fSegments[test];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000659 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000660 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000661 fFirstSorted = 0;
662}
663
664void SkOpContour::toPath(SkPathWriter* path) const {
665 int segmentCount = fSegments.count();
666 const SkPoint& pt = fSegments.front().pts()[0];
667 path->deferredMove(pt);
668 for (int test = 0; test < segmentCount; ++test) {
669 fSegments[test].addCurveTo(0, 1, path, true);
670 }
671 path->close();
672}
673
674void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
675 SkOpSegment** topStart) {
676 int segmentCount = fSortedSegments.count();
677 SkASSERT(segmentCount > 0);
678 int sortedIndex = fFirstSorted;
679 fDone = true; // may be cleared below
680 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
681 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
682 if (testSegment->done()) {
683 if (sortedIndex == fFirstSorted) {
684 ++fFirstSorted;
685 }
686 continue;
687 }
688 fDone = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000689 SkPoint testXY = testSegment->activeLeftTop(NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000690 if (*topStart) {
691 if (testXY.fY < topLeft.fY) {
692 continue;
693 }
694 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
695 continue;
696 }
697 if (bestXY->fY < testXY.fY) {
698 continue;
699 }
700 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
701 continue;
702 }
703 }
704 *topStart = testSegment;
705 *bestXY = testXY;
706 }
707}
708
709SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
710 int segmentCount = fSegments.count();
711 for (int test = 0; test < segmentCount; ++test) {
712 SkOpSegment* testSegment = &fSegments[test];
713 if (testSegment->done()) {
714 continue;
715 }
716 testSegment->undoneSpan(start, end);
717 return testSegment;
718 }
719 return NULL;
720}
721
722#if DEBUG_SHOW_WINDING
723int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
724 int count = fSegments.count();
725 int sum = 0;
726 for (int index = 0; index < count; ++index) {
727 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
728 }
729// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
730 return sum;
731}
732
caryclark@google.com570863f2013-09-16 15:55:01 +0000733void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000734// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
735// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
736 int ofInterest = 1 << 5 | 1 << 8;
737 int total = 0;
738 int index;
739 for (index = 0; index < contourList.count(); ++index) {
740 total += contourList[index]->segments().count();
741 }
742 int sum = 0;
743 for (index = 0; index < contourList.count(); ++index) {
744 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
745 }
746// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
747}
748#endif
749
750void SkOpContour::setBounds() {
751 int count = fSegments.count();
752 if (count == 0) {
753 SkDebugf("%s empty contour\n", __FUNCTION__);
754 SkASSERT(0);
755 // FIXME: delete empty contour?
756 return;
757 }
758 fBounds = fSegments.front().bounds();
759 for (int index = 1; index < count; ++index) {
760 fBounds.add(fSegments[index].bounds());
761 }
762}