blob: 467fab31f57da1a92febb42e2cd59fc9159016bf [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();
16 if (pt0 == pt1) {
17 // FIXME: one could imagine a case where it would be incorrect to ignore this
18 // suppose two self-intersecting cubics overlap to be coincident --
19 // this needs to check that by some measure the t values are far enough apart
20 // or needs to check to see if the self-intersection bit was set on the cubic segment
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
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270void SkOpContour::calcCoincidentWinding() {
271 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];
caryclark@google.com570863f2013-09-16 15:55:01 +0000279 calcCommonCoincidentWinding(coincidence);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000280 }
281}
282
caryclark@google.com570863f2013-09-16 15:55:01 +0000283void SkOpContour::calcPartialCoincidentWinding() {
284 int count = fPartialCoincidences.count();
285#if DEBUG_CONCIDENT
286 if (count > 0) {
287 SkDebugf("%s count=%d\n", __FUNCTION__, count);
288 }
289#endif
290 for (int index = 0; index < count; ++index) {
291 SkCoincidence& coincidence = fPartialCoincidences[index];
caryclarkdac1d172014-06-17 05:15:38 -0700292 calcCommonCoincidentWinding(coincidence);
293 }
294 // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
295 // are also coincident
296 for (int index = 0; index < count - 1; ++index) {
297 const SkCoincidence& coincidence = fPartialCoincidences[index];
298 int thisIndex = coincidence.fSegments[0];
299 SkOpContour* otherContour = coincidence.fOther;
300 int otherIndex = coincidence.fSegments[1];
301 for (int idx2 = 1; idx2 < count; ++idx2) {
302 const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
303 int innerThisIndex = innerCoin.fSegments[0];
304 if (thisIndex == innerThisIndex) {
305 checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
306 }
307 if (this == otherContour && otherIndex == innerThisIndex) {
308 checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
309 }
310 SkOpContour* innerOtherContour = innerCoin.fOther;
311 innerThisIndex = innerCoin.fSegments[1];
312 if (this == innerOtherContour && thisIndex == innerThisIndex) {
313 checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
314 }
315 if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
316 checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
317 }
318 }
319 }
320}
321
322void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
323 const SkCoincidence& twoCoin, int twoIdx, bool partial) {
324 SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
325 SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
326 // look for common overlap
327 double min = SK_ScalarMax;
328 double max = SK_ScalarMin;
329 double min1 = oneCoin.fTs[!oneIdx][0];
330 double max1 = oneCoin.fTs[!oneIdx][1];
331 double min2 = twoCoin.fTs[!twoIdx][0];
332 double max2 = twoCoin.fTs[!twoIdx][1];
333 bool cancelers = (min1 < max1) != (min2 < max2);
334 if (min1 > max1) {
335 SkTSwap(min1, max1);
336 }
337 if (min2 > max2) {
338 SkTSwap(min2, max2);
339 }
340 if (between(min1, min2, max1)) {
341 min = min2;
342 }
343 if (between(min1, max2, max1)) {
344 max = max2;
345 }
346 if (between(min2, min1, max2)) {
347 min = SkTMin(min, min1);
348 }
349 if (between(min2, max1, max2)) {
350 max = SkTMax(max, max1);
351 }
352 if (min >= max) {
353 return; // no overlap
354 }
355 // look to see if opposite are different segments
356 int seg1Index = oneCoin.fSegments[oneIdx];
357 int seg2Index = twoCoin.fSegments[twoIdx];
358 if (seg1Index == seg2Index) {
359 return;
360 }
361 SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
362 SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
363 SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
364 SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
365 // find opposite t value ranges corresponding to reference min/max range
366 const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
367 const int refSegIndex = oneCoin.fSegments[!oneIdx];
368 const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
369 int seg1Start = segment1->findOtherT(min, refSegment);
370 int seg1End = segment1->findOtherT(max, refSegment);
371 int seg2Start = segment2->findOtherT(min, refSegment);
372 int seg2End = segment2->findOtherT(max, refSegment);
373 // if the opposite pairs already contain min/max, we're done
374 if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
375 return;
376 }
377 double loEnd = SkTMin(min1, min2);
378 double hiEnd = SkTMax(max1, max2);
379 // insert the missing coincident point(s)
380 double missingT1 = -1;
381 double otherT1 = -1;
382 if (seg1Start < 0) {
383 if (seg2Start < 0) {
384 return;
385 }
386 missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
387 segment2, seg1End);
388 if (missingT1 < 0) {
389 return;
390 }
391 const SkOpSpan* missingSpan = &segment2->span(seg2Start);
392 otherT1 = missingSpan->fT;
393 } else if (seg2Start < 0) {
394 SkASSERT(seg1Start >= 0);
395 missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
396 segment1, seg2End);
397 if (missingT1 < 0) {
398 return;
399 }
400 const SkOpSpan* missingSpan = &segment1->span(seg1Start);
401 otherT1 = missingSpan->fT;
402 }
403 SkPoint missingPt1;
404 SkOpSegment* addTo1 = NULL;
405 SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
406 int minTIndex = refSegment->findExactT(min, addOther1);
407 SkASSERT(minTIndex >= 0);
408 if (missingT1 >= 0) {
409 missingPt1 = refSegment->span(minTIndex).fPt;
410 addTo1 = seg1Start < 0 ? segment1 : segment2;
411 }
412 double missingT2 = -1;
413 double otherT2 = -1;
414 if (seg1End < 0) {
415 if (seg2End < 0) {
416 return;
417 }
418 missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
419 segment2, seg1Start);
420 if (missingT2 < 0) {
421 return;
422 }
423 const SkOpSpan* missingSpan = &segment2->span(seg2End);
424 otherT2 = missingSpan->fT;
425 } else if (seg2End < 0) {
426 SkASSERT(seg1End >= 0);
427 missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
428 segment1, seg2Start);
429 if (missingT2 < 0) {
430 return;
431 }
432 const SkOpSpan* missingSpan = &segment1->span(seg1End);
433 otherT2 = missingSpan->fT;
434 }
435 SkPoint missingPt2;
436 SkOpSegment* addTo2 = NULL;
437 SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
438 int maxTIndex = refSegment->findExactT(max, addOther2);
439 SkASSERT(maxTIndex >= 0);
440 if (missingT2 >= 0) {
441 missingPt2 = refSegment->span(maxTIndex).fPt;
442 addTo2 = seg1End < 0 ? segment1 : segment2;
443 }
444 if (missingT1 >= 0) {
445 addTo1->pinT(missingPt1, &missingT1);
446 addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
447 } else {
448 SkASSERT(minTIndex >= 0);
449 missingPt1 = refSegment->span(minTIndex).fPt;
450 }
451 if (missingT2 >= 0) {
452 addTo2->pinT(missingPt2, &missingT2);
453 addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
454 } else {
455 SkASSERT(minTIndex >= 0);
456 missingPt2 = refSegment->span(maxTIndex).fPt;
457 }
458 if (!partial) {
459 return;
460 }
461 if (cancelers) {
462 if (missingT1 >= 0) {
caryclark5e27e0e2014-08-12 07:46:33 -0700463 if (addTo1->reversePoints(missingPt1, missingPt2)) {
464 SkTSwap(missingPt1, missingPt2);
465 }
caryclarkdac1d172014-06-17 05:15:38 -0700466 addTo1->addTCancel(missingPt1, missingPt2, addOther1);
467 } else {
caryclark5e27e0e2014-08-12 07:46:33 -0700468 if (addTo2->reversePoints(missingPt1, missingPt2)) {
469 SkTSwap(missingPt1, missingPt2);
470 }
caryclarkdac1d172014-06-17 05:15:38 -0700471 addTo2->addTCancel(missingPt1, missingPt2, addOther2);
472 }
473 } else if (missingT1 >= 0) {
474 addTo1->addTCoincident(missingPt1, missingPt2, addTo1 == addTo2 ? missingT2 : otherT2,
475 addOther1);
476 } else {
477 addTo2->addTCoincident(missingPt2, missingPt1, addTo2 == addTo1 ? missingT1 : otherT1,
478 addOther2);
caryclark@google.com570863f2013-09-16 15:55:01 +0000479 }
480}
481
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000482void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
483 int count = coincidences.count();
484#if DEBUG_CONCIDENT
485 if (count > 0) {
486 SkDebugf("%s count=%d\n", __FUNCTION__, count);
487 }
488#endif
489 // look for a lineup where the partial implies another adjoining coincidence
490 for (int index = 0; index < count; ++index) {
491 const SkCoincidence& coincidence = coincidences[index];
492 int thisIndex = coincidence.fSegments[0];
493 SkOpSegment& thisOne = fSegments[thisIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700494 if (thisOne.done()) {
495 continue;
496 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000497 SkOpContour* otherContour = coincidence.fOther;
498 int otherIndex = coincidence.fSegments[1];
499 SkOpSegment& other = otherContour->fSegments[otherIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700500 if (other.done()) {
501 continue;
502 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000503 double startT = coincidence.fTs[0][0];
504 double endT = coincidence.fTs[0][1];
505 if (startT == endT) { // this can happen in very large compares
506 continue;
507 }
508 double oStartT = coincidence.fTs[1][0];
509 double oEndT = coincidence.fTs[1][1];
510 if (oStartT == oEndT) {
511 continue;
512 }
513 bool swapStart = startT > endT;
514 bool swapOther = oStartT > oEndT;
caryclarkdac1d172014-06-17 05:15:38 -0700515 const SkPoint* startPt = &coincidence.fPts[0][0];
516 const SkPoint* endPt = &coincidence.fPts[0][1];
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000517 if (swapStart) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000518 SkTSwap(startT, endT);
519 SkTSwap(oStartT, oEndT);
520 SkTSwap(startPt, endPt);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000521 }
522 bool cancel = swapOther != swapStart;
523 int step = swapStart ? -1 : 1;
524 int oStep = swapOther ? -1 : 1;
525 double oMatchStart = cancel ? oEndT : oStartT;
526 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
527 bool added = false;
528 if (oMatchStart != 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000529 const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
530 added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000531 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000532 if (!cancel && startT != 0 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000533 (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000534 }
535 }
536 double oMatchEnd = cancel ? oStartT : oEndT;
537 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
538 bool added = false;
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000539 if (cancel && endT != 1 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000540 (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000541 }
542 }
543 }
544}
545
caryclark@google.com570863f2013-09-16 15:55:01 +0000546void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
caryclarkdac1d172014-06-17 05:15:38 -0700547 if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
548 return;
549 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000550 int thisIndex = coincidence.fSegments[0];
551 SkOpSegment& thisOne = fSegments[thisIndex];
552 if (thisOne.done()) {
553 return;
554 }
555 SkOpContour* otherContour = coincidence.fOther;
556 int otherIndex = coincidence.fSegments[1];
557 SkOpSegment& other = otherContour->fSegments[otherIndex];
558 if (other.done()) {
559 return;
560 }
561 double startT = coincidence.fTs[0][0];
562 double endT = coincidence.fTs[0][1];
caryclarkdac1d172014-06-17 05:15:38 -0700563 const SkPoint* startPt = &coincidence.fPts[0][0];
564 const SkPoint* endPt = &coincidence.fPts[0][1];
caryclark@google.com570863f2013-09-16 15:55:01 +0000565 bool cancelers;
566 if ((cancelers = startT > endT)) {
567 SkTSwap<double>(startT, endT);
568 SkTSwap<const SkPoint*>(startPt, endPt);
569 }
caryclark361b8b02014-09-08 10:25:38 -0700570 bump_out_close_span(&startT, &endT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000571 SkASSERT(!approximately_negative(endT - startT));
572 double oStartT = coincidence.fTs[1][0];
573 double oEndT = coincidence.fTs[1][1];
574 if (oStartT > oEndT) {
575 SkTSwap<double>(oStartT, oEndT);
576 cancelers ^= true;
577 }
caryclark361b8b02014-09-08 10:25:38 -0700578 bump_out_close_span(&oStartT, &oEndT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000579 SkASSERT(!approximately_negative(oEndT - oStartT));
580 if (cancelers) {
581 thisOne.addTCancel(*startPt, *endPt, &other);
582 } else {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000583 thisOne.addTCoincident(*startPt, *endPt, endT, &other);
caryclark@google.com570863f2013-09-16 15:55:01 +0000584 }
585#if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000586 thisOne.debugShowTs("p");
587 other.debugShowTs("o");
caryclark@google.com570863f2013-09-16 15:55:01 +0000588#endif
589}
590
caryclarkdac1d172014-06-17 05:15:38 -0700591void SkOpContour::resolveNearCoincidence() {
592 int count = fCoincidences.count();
593 for (int index = 0; index < count; ++index) {
594 SkCoincidence& coincidence = fCoincidences[index];
595 if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
596 continue;
597 }
598 int thisIndex = coincidence.fSegments[0];
599 SkOpSegment& thisOne = fSegments[thisIndex];
600 SkOpContour* otherContour = coincidence.fOther;
601 int otherIndex = coincidence.fSegments[1];
602 SkOpSegment& other = otherContour->fSegments[otherIndex];
603 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
604 // OPTIMIZATION: remove from coincidence array
605 continue;
606 }
607 #if DEBUG_CONCIDENT
608 thisOne.debugShowTs("-");
609 other.debugShowTs("o");
610 #endif
611 double startT = coincidence.fTs[0][0];
612 double endT = coincidence.fTs[0][1];
613 bool cancelers;
614 if ((cancelers = startT > endT)) {
615 SkTSwap<double>(startT, endT);
616 }
617 if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
618 if (endT <= 1 - FLT_EPSILON) {
619 endT += FLT_EPSILON;
620 SkASSERT(endT <= 1);
621 } else {
622 startT -= FLT_EPSILON;
623 SkASSERT(startT >= 0);
624 }
625 }
626 SkASSERT(!approximately_negative(endT - startT));
627 double oStartT = coincidence.fTs[1][0];
628 double oEndT = coincidence.fTs[1][1];
629 if (oStartT > oEndT) {
630 SkTSwap<double>(oStartT, oEndT);
631 cancelers ^= true;
632 }
633 SkASSERT(!approximately_negative(oEndT - oStartT));
634 if (cancelers) {
635 thisOne.blindCancel(coincidence, &other);
636 } else {
637 thisOne.blindCoincident(coincidence, &other);
638 }
639 }
640}
641
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000642void SkOpContour::sortAngles() {
643 int segmentCount = fSegments.count();
644 for (int test = 0; test < segmentCount; ++test) {
645 fSegments[test].sortAngles();
646 }
647}
648
caryclark@google.com07393ca2013-04-08 11:47:37 +0000649void SkOpContour::sortSegments() {
650 int segmentCount = fSegments.count();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000651 fSortedSegments.push_back_n(segmentCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000652 for (int test = 0; test < segmentCount; ++test) {
caryclark@google.comd892bd82013-06-17 14:10:36 +0000653 fSortedSegments[test] = &fSegments[test];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000654 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000655 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000656 fFirstSorted = 0;
657}
658
659void SkOpContour::toPath(SkPathWriter* path) const {
660 int segmentCount = fSegments.count();
661 const SkPoint& pt = fSegments.front().pts()[0];
662 path->deferredMove(pt);
663 for (int test = 0; test < segmentCount; ++test) {
664 fSegments[test].addCurveTo(0, 1, path, true);
665 }
666 path->close();
667}
668
669void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
670 SkOpSegment** topStart) {
671 int segmentCount = fSortedSegments.count();
672 SkASSERT(segmentCount > 0);
673 int sortedIndex = fFirstSorted;
674 fDone = true; // may be cleared below
675 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
676 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
677 if (testSegment->done()) {
678 if (sortedIndex == fFirstSorted) {
679 ++fFirstSorted;
680 }
681 continue;
682 }
683 fDone = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000684 SkPoint testXY = testSegment->activeLeftTop(NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000685 if (*topStart) {
686 if (testXY.fY < topLeft.fY) {
687 continue;
688 }
689 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
690 continue;
691 }
692 if (bestXY->fY < testXY.fY) {
693 continue;
694 }
695 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
696 continue;
697 }
698 }
699 *topStart = testSegment;
700 *bestXY = testXY;
701 }
702}
703
704SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
705 int segmentCount = fSegments.count();
706 for (int test = 0; test < segmentCount; ++test) {
707 SkOpSegment* testSegment = &fSegments[test];
708 if (testSegment->done()) {
709 continue;
710 }
711 testSegment->undoneSpan(start, end);
712 return testSegment;
713 }
714 return NULL;
715}
716
717#if DEBUG_SHOW_WINDING
718int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
719 int count = fSegments.count();
720 int sum = 0;
721 for (int index = 0; index < count; ++index) {
722 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
723 }
724// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
725 return sum;
726}
727
caryclark@google.com570863f2013-09-16 15:55:01 +0000728void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000729// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
730// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
731 int ofInterest = 1 << 5 | 1 << 8;
732 int total = 0;
733 int index;
734 for (index = 0; index < contourList.count(); ++index) {
735 total += contourList[index]->segments().count();
736 }
737 int sum = 0;
738 for (index = 0; index < contourList.count(); ++index) {
739 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
740 }
741// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
742}
743#endif
744
745void SkOpContour::setBounds() {
746 int count = fSegments.count();
747 if (count == 0) {
748 SkDebugf("%s empty contour\n", __FUNCTION__);
749 SkASSERT(0);
750 // FIXME: delete empty contour?
751 return;
752 }
753 fBounds = fSegments.front().bounds();
754 for (int index = 1; index < count; ++index) {
755 fBounds.add(fSegments[index].bounds());
756 }
757}