OLD | NEW |
| (Empty) |
1 /* | |
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 */ | |
7 add unit test for quadratic horizontal intersection | |
8 add unit test for cubic horizontal intersection with left/right | |
9 add unit test for ActiveEdge::calcLeft (can currently loop forever) | |
10 does ActiveEdge::isCoincidentWith need to support quad, cubic? | |
11 figure out why variation in ActiveEdge::tooCloseToCall isn't better | |
12 why does 'lastPtr - 2' in addIntersectingTs break testSimplifyTriangle22? | |
13 add code to promote quad to cubic, or add quad/cubic intersection | |
14 figure out why testSimplifySkinnyTriangle13 fails | |
15 | |
16 for quadratics and cubics, once various T values are added, see if consecutive | |
17 Ts have ys that go up instead of down. If so, the edge needs to be broken. | |
18 | |
19 when splitting curves at inflection pts, should I retain the original curve | |
20 data and note that the first/last T are no longer 0/1 ? | |
21 I need to figure this out before I can proceed | |
22 | |
23 would it make sense to leave the InEdge alone, and add multiple copies of | |
24 ActiveEdge, pointing to the same InEdge, where the copy has only the subset | |
25 of Ts that need to be walked in reverse order? | |
26 | |
27 | |
28 -- A Digression Which Shows Why Resolving Coincidence Does Not Make Sense -- | |
29 | |
30 Consider the following fine ASCII art: | |
31 | |
32 +------>-------+ +------>-------+ | |
33 | | | | | |
34 ^ V ^ V | |
35 | | | | | |
36 +------<-------+ +------<-------+ | |
37 +------>-------+ +------<-------+ | |
38 | | | | | |
39 ^ V V ^ | |
40 | | | | | |
41 +------<-------+ +------>-------+ | |
42 | |
43 (assume the bottom and top of the stacked rectangles are coincident) | |
44 | |
45 Simplifying said rectangles, regardless of rectangle direction, and regardless | |
46 of winding or even/odd, eliminates the coincident edge, i.e., the result is | |
47 always: | |
48 | |
49 +------>-------+ | |
50 | | | |
51 | | | |
52 | | | |
53 ^ V | |
54 | | | |
55 | | | |
56 | | | |
57 +------<-------+ | |
58 | |
59 But when the rectangles are enclosed in a larger rectangle: | |
60 | |
61 +-------->---------+ +-------->---------+ | |
62 | +------>-------+ | | +------>-------+ | | |
63 | | | | | | | | | |
64 | ^ V | | ^ V | | |
65 | | | | | | | | | |
66 | +------<-------+ | | +------<-------+ | | |
67 | +------>-------+ | | +------<-------+ | | |
68 | | | | | | | | | |
69 | ^ V | | V ^ | | |
70 | | | | | | | | | |
71 | +------<-------+ | | +------>-------+ | | |
72 +--------<---------+ +--------<---------+ | |
73 | |
74 Simplifying them gives different results depending on the winding setting: | |
75 | |
76 winding: | |
77 +-------->---------+ +-------->---------+ | |
78 | | | | | |
79 | | | | | |
80 | | | | | |
81 | | | | | |
82 | | | +------<-------+ | | |
83 | | | | | | | |
84 | | | V ^ | | |
85 | | | | | | | |
86 | | | +------>-------+ | | |
87 +--------<---------+ +--------<---------+ | |
88 | |
89 even odd: | |
90 +-------->---------+ +-------->---------+ | |
91 | +------<-------+ | | +------<-------+ | | |
92 | | | | | | | | | |
93 | | | | | | | | | |
94 | | | | | | | | | |
95 | | | | | | | | | |
96 | V ^ | | V ^ | | |
97 | | | | | | | | | |
98 | | | | | | | | | |
99 | | | | | | | | | |
100 | +------>-------+ | | +------>-------+ | | |
101 +--------<---------+ +--------<---------+ | |
102 | |
103 So, given the inner rectangles alone (e.g., given coincident pairs in some local | |
104 context), we can't know whether to keep the coincident edges or not. | |
105 | |
106 | |
107 -- Thoughts About Sortless Ops -- | |
108 | |
109 I can't come up with anything truly sortless. It seems that the crossings need | |
110 to be sorted to know which segment is next on the outside, although sometimes | |
111 we can use that it is not coincident just to follow the direction. | |
112 | |
113 If it is coincident or if there's more than two crossing segments, sorting | |
114 seems inevitable. | |
115 | |
116 Likewise, to resolve whether one contour is inside another, it seems that | |
117 sorting is required. Given a pair of segments on different contours, to know | |
118 if one is inside of the other, I need to know for each which side of the edge | |
119 is the inside/filled side. When the outer contour is walked, it seems like I | |
120 could record the inside info. I guess when the inner contour is found, its | |
121 inside sense is reversed (inside is above the top). But how do I know if the | |
122 next contour is inside another? Maybe shoot out a line and brute-force | |
123 intersect it with all the segments in all the other contours? If every contour | |
124 has an extra segment when the intersections are computed, this may not be as | |
125 crazy as it seems. | |
126 | |
127 Suppose each contour has one extra segment shooting straight up from the top | |
128 (or straight up from any point on the segment). This ray is not intersected | |
129 with the home contour, but is intersected with all other contours as part of | |
130 the normal intersection engine. If it is possible to get from the T values to | |
131 the other segments to the other contours, it would be straightforward to | |
132 count the contour crossings and determine if the home contour is in another | |
133 contour or not (if the count is even, not, if odd, is inside). By itself that | |
134 doesn't tell us about winding, but it's a start. | |
135 | |
136 | |
137 Since intersecting these rays is unrelated to computing other intersections, | |
138 it can be lazily done once the contour is found. | |
139 | |
140 So | |
141 repeat the following | |
142 find the top segment of all contours | |
143 trace the outside, marking touching first and last segments as inside | |
144 continue tracing the touched segments with reversed outside/inside sense | |
145 once the edges are exhausted, remaining must be disjoint contours | |
146 send a ray from a disjoint point through all other contours | |
147 count the crossings, determine if disjoint is inside or outside, then continue | |
148 | |
149 === | |
150 | |
151 On Quadratic (and Cubic) Intersections | |
152 | |
153 Currently, if only the end points touch, QuadracticIntersections does a lot of | |
154 work to figure that out. Can I test for that up front, then short circuit the | |
155 recursive search for the end points? | |
156 | |
157 Or, is there something defective in the current approach that makes the end | |
158 point recursion go so deep? I'm seeing 56 stack frames (about 28 divides, but | |
159 thankfully, no splits) to find one matching endpoint. | |
160 | |
161 | |
162 Bezier curve focus may allow more quickly determining that end points with | |
163 identical tangents are practically coicident for some range of T, but I don't | |
164 understand the math yet to know. | |
165 | |
166 Another approach is to determine how flat the curve is to make good guesses | |
167 about how far to move away in T before doing the intersection for the remainder | |
168 and/or to determine whether one curve is to the inside or outside of another. | |
169 According to Mike/Rob, the flatness for quadratics increases by 4 for each | |
170 subdivision, and a crude guess of the curvature can be had by comparing P1 to | |
171 (P0+P2)/2. By looking at the ULPS of the numbers, I can guess what value of | |
172 T may be far enough that the curves diverge but don't cross. | |
173 | |
174 ==== | |
175 | |
176 Code I May Not Need Any More | |
177 | |
178 static bool CoincidentCandidate(const Angle* current) { | |
179 const Segment* segment = current->segment(); | |
180 int min = SkMin32(current->start(), current->end()); | |
181 do { | |
182 const Span& span = segment->fTs[min]; | |
183 if (span.fCoincident == Span::kStart_Coincidence) { | |
184 return true; | |
185 } | |
186 } while (--min >= 0); | |
187 return false; | |
188 } | |
189 | |
190 static bool CoincidentHalf(const Angle* current, const Angle* next) { | |
191 const Segment* other = next->segment(); | |
192 const Segment* segment = current->segment(); | |
193 int min = SkMin32(current->start(), current->end()); | |
194 const Span& minSpan = segment->fTs[min]; | |
195 if (minSpan.fOther == other) { | |
196 return minSpan.fCoincident == Span::kStart_Coincidence; | |
197 } | |
198 int index = min; | |
199 int spanCount = segment->fTs.count(); | |
200 while (++index < spanCount) { | |
201 const Span& span = segment->fTs[index]; | |
202 if (minSpan.fT != span.fT) { | |
203 break; | |
204 } | |
205 if (span.fOther != other) { | |
206 continue; | |
207 } | |
208 return span.fCoincident == Span::kStart_Coincidence; | |
209 } | |
210 index = min; | |
211 while (--index >= 0) { | |
212 const Span& span = segment->fTs[index]; | |
213 if (span.fOther != other) { | |
214 continue; | |
215 } | |
216 return span.fCoincident == Span::kStart_Coincidence; | |
217 } | |
218 return false; | |
219 } | |
220 | |
221 static bool Coincident(const Angle* current, const Angle* next) { | |
222 return CoincidentHalf(current, next) && | |
223 CoincidentHalf(next, current); | |
224 } | |
225 | |
226 // If three lines cancel in a - b - c order, a - b may or may not | |
227 // eliminate the edge that describes the b - c cancellation. Check done to | |
228 // determine this. | |
229 static bool CoincidentCancels(const Angle* current, const Angle* next) { | |
230 int curMin = SkMin32(current->start(), current->end()); | |
231 if (current->segment()->fTs[curMin].fDone) { | |
232 return false; | |
233 } | |
234 int nextMin = SkMin32(next->start(), next->end()); | |
235 if (next->segment()->fTs[nextMin].fDone) { | |
236 return false; | |
237 } | |
238 return SkSign32(current->start() - current->end()) | |
239 != SkSign32(next->start() - next->end()); | |
240 } | |
241 | |
242 // FIXME: at this point, just have two functions for the different steps | |
243 int coincidentEnd(int from, int step) const { | |
244 double fromT = fTs[from].fT; | |
245 int count = fTs.count(); | |
246 int to = from; | |
247 while (step > 0 ? ++to < count : --to >= 0) { | |
248 const Span& span = fTs[to]; | |
249 if ((step > 0 ? span.fT - fromT : fromT - span.fT) >= FLT_EPSILON )
{ | |
250 // FIXME: we assume that if the T changes, we don't care about | |
251 // coincident -- but in nextSpan, we require that both the T | |
252 // and actual loc change to represent a span. This asymettry may | |
253 // be OK or may be trouble -- if trouble, probably will need to | |
254 // detect coincidence earlier or sort differently | |
255 break; | |
256 } | |
257 #if 01 | |
258 if (span.fCoincident == (step < 0 ? Span::kStart_Coincidence : | |
259 Span::kEnd_Coincidence)) { | |
260 from = to; | |
261 } | |
262 #else | |
263 from = to; | |
264 #endif | |
265 } | |
266 return from; | |
267 } | |
268 | |
269 // once past current span, if step>0, look for coicident==1 | |
270 // if step<0, look for coincident==-1 | |
271 int nextSpanEnd(int from, int step) const { | |
272 int result = nextSpan(from, step); | |
273 if (result < 0) { | |
274 return result; | |
275 } | |
276 return coincidentEnd(result, step); | |
277 } | |
278 | |
279 | |
280 void adjustFirst(const SkTDArray<Angle*>& sorted, int& first, int& winding, | |
281 bool outside) { | |
282 int firstIndex = first; | |
283 int angleCount = sorted.count(); | |
284 if (true || outside) { | |
285 const Angle* angle = sorted[firstIndex]; | |
286 int prior = firstIndex; | |
287 do { | |
288 if (--prior < 0) { | |
289 prior = angleCount - 1; | |
290 } | |
291 if (prior == firstIndex) { // all are coincident with each other | |
292 return; | |
293 } | |
294 if (!Coincident(sorted[prior], sorted[first])) { | |
295 return; | |
296 } | |
297 winding += angle->sign(); | |
298 first = prior; | |
299 angle = sorted[prior]; | |
300 winding -= angle->sign(); | |
301 } while (true); | |
302 } | |
303 do { | |
304 int next = first + 1; | |
305 if (next == angleCount) { | |
306 next = 0; | |
307 } | |
308 if (next == firstIndex) { // all are coincident with each other | |
309 return; | |
310 } | |
311 if (!Coincident(sorted[first], sorted[next])) { | |
312 return; | |
313 } | |
314 first = next; | |
315 } while (true); | |
316 } | |
317 | |
318 bool nextIsCoincident = CoincidentCandidate(nextAngle); | |
319 bool finalOrNoCoincident = true; | |
320 bool pairCoincides = false; | |
321 bool pairCancels = false; | |
322 if (nextIsCoincident) { | |
323 int followIndex = nextIndex + 1; | |
324 if (followIndex == angleCount) { | |
325 followIndex = 0; | |
326 } | |
327 const Angle* followAngle = sorted[followIndex]; | |
328 finalOrNoCoincident = !Coincident(nextAngle, followAngle); | |
329 if ((pairCoincides = Coincident(angle, nextAngle))) { | |
330 pairCancels = CoincidentCancels(angle, nextAngle); | |
331 } | |
332 } | |
333 if (pairCancels && !foundAngle && !nextSegment->done()) { | |
334 Segment* aSeg = angle->segment(); | |
335 // alreadyMarked |= aSeg == sorted[firstIndex]->segment(); | |
336 aSeg->markAndChaseCoincident(angle->start(), angle->end(), | |
337 nextSegment); | |
338 if (firstEdge) { | |
339 return NULL; | |
340 } | |
341 } | |
342 if (pairCoincides) { | |
343 if (pairCancels) { | |
344 goto doNext; | |
345 } | |
346 int minT = SkMin32(nextAngle->start(), nextAngle->end()); | |
347 bool markNext = abs(maxWinding) < abs(winding); | |
348 if (markNext) { | |
349 nextSegment->markDone(minT, winding); | |
350 } | |
351 int oldMinT = SkMin32(angle->start(), angle->end()); | |
352 if (true || !foundAngle) { | |
353 // SkASSERT(0); // do we ever get here? | |
354 Segment* aSeg = angle->segment(); | |
355 // alreadyMarked |= aSeg == sorted[firstIndex]->segment(); | |
356 aSeg->markDone(oldMinT, maxWinding); | |
357 } | |
358 } | |
359 | |
360 // OPTIMIZATION: uses tail recursion. Unwise? | |
361 void innerCoincidentChase(int step, Segment* other) { | |
362 // find other at index | |
363 // SkASSERT(!done()); | |
364 const Span* start = NULL; | |
365 const Span* end = NULL; | |
366 int index, startIndex, endIndex; | |
367 int count = fTs.count(); | |
368 for (index = 0; index < count; ++index) { | |
369 const Span& span = fTs[index]; | |
370 if (!span.fCoincident || span.fOther != other) { | |
371 continue; | |
372 } | |
373 if (!start) { | |
374 startIndex = index; | |
375 start = &span; | |
376 } else { | |
377 SkASSERT(!end); | |
378 endIndex = index; | |
379 end = &span; | |
380 } | |
381 } | |
382 if (!end) { | |
383 return; | |
384 } | |
385 bool thisDone = fTs[SkMin32(startIndex, endIndex)].fDone; | |
386 bool otherDone = other->fTs[SkMin32(start->fOtherIndex, | |
387 end->fOtherIndex)].fDone; | |
388 if (thisDone && otherDone) { | |
389 return; | |
390 } | |
391 Segment* next; | |
392 Segment* nextOther; | |
393 if (step < 0) { | |
394 next = start->fT == 0 ? NULL : this; | |
395 nextOther = other->fTs[start->fOtherIndex].fT > 1 - FLT_EPSILON ? NU
LL : other; | |
396 } else { | |
397 next = end->fT == 1 ? NULL : this; | |
398 nextOther = other->fTs[end->fOtherIndex].fT < FLT_EPSILON ? NULL : o
ther; | |
399 } | |
400 SkASSERT(!next || !nextOther); | |
401 for (index = 0; index < count; ++index) { | |
402 const Span& span = fTs[index]; | |
403 if (span.fCoincident || span.fOther == other) { | |
404 continue; | |
405 } | |
406 bool checkNext = !next && (step < 0 ? span.fT < FLT_EPSILON | |
407 && span.fOtherT > 1 - FLT_EPSILON : span.fT > 1 - FLT_EPSILON | |
408 && span.fOtherT < FLT_EPSILON); | |
409 bool checkOther = !nextOther && (step < 0 ? fabs(span.fT - start->fT
) < FLT_EPSILON | |
410 && span.fOtherT < FLT_EPSILON : fabs(span.fT - end->fT) < FLT_EP
SILON | |
411 && span.fOtherT > 1 - FLT_EPSILON); | |
412 if (!checkNext && !checkOther) { | |
413 continue; | |
414 } | |
415 Segment* oSegment = span.fOther; | |
416 if (oSegment->done()) { | |
417 continue; | |
418 } | |
419 int oCount = oSegment->fTs.count(); | |
420 for (int oIndex = 0; oIndex < oCount; ++oIndex) { | |
421 const Span& oSpan = oSegment->fTs[oIndex]; | |
422 if (oSpan.fT >= FLT_EPSILON && oSpan.fT <= 1 - FLT_EPSILON) { | |
423 continue; | |
424 } | |
425 if (!oSpan.fCoincident) { | |
426 continue; | |
427 } | |
428 if (checkNext && (oSpan.fT < FLT_EPSILON ^ step < 0)) { | |
429 next = oSegment; | |
430 checkNext = false; | |
431 } | |
432 if (checkOther && (oSpan.fT > 1 - FLT_EPSILON ^ step < 0)) { | |
433 nextOther = oSegment; | |
434 checkOther = false; | |
435 } | |
436 } | |
437 } | |
438 // this needs to walk both spans in lock step, skipping edges that | |
439 // are already marked done on one or the other | |
440 markCanceled(startIndex, endIndex); | |
441 if (next && nextOther) { | |
442 next->innerCoincidentChase(step, nextOther); | |
443 } | |
444 } | |
445 | |
446 // cancel coincident edges in lock step | |
447 void markCanceled(int start, int end) { | |
448 if (done()) { | |
449 return; | |
450 } | |
451 Segment* other = fTs[start].fOther; | |
452 if (other->done()) { | |
453 return; | |
454 } | |
455 if (start > end) { | |
456 SkTSwap<int>(start, end); | |
457 } | |
458 double maxT = fTs[end].fT - FLT_EPSILON; | |
459 int spanCount = fTs.count(); | |
460 // since these cancel, this walks up and other walks down | |
461 int oStart = fTs[start].fOtherIndex; | |
462 double oStartT = other->fTs[oStart].fT; | |
463 while (oStartT - other->fTs[--oStart].fT < FLT_EPSILON) | |
464 ; | |
465 double startT = fTs[start].fT; | |
466 while (start > 0 && startT - fTs[start - 1].fT < FLT_EPSILON) { | |
467 --start; | |
468 } | |
469 do { | |
470 Span* span = &fTs[start]; | |
471 Span* oSpan = &other->fTs[oStart]; | |
472 // find start of each, and see if both are not done | |
473 bool markDone = !span->fDone && !oSpan->fDone; | |
474 double spanT = span->fT; | |
475 do { | |
476 if (markDone) { | |
477 span->fCanceled = true; | |
478 #if DEBUG_MARK_DONE | |
479 const SkPoint& pt = xyAtT(span); | |
480 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n"
, | |
481 __FUNCTION__, fID, start, span->fT, pt.fX, pt.fY); | |
482 #endif | |
483 SkASSERT(!span->fDone); | |
484 span->fDone = true; | |
485 span->fWinding = 0; | |
486 fDoneSpans++; | |
487 } | |
488 if (++start == spanCount) { | |
489 break; | |
490 } | |
491 span = &fTs[start]; | |
492 } while (span->fT - spanT < FLT_EPSILON); | |
493 double oSpanT = oSpan->fT; | |
494 do { | |
495 if (markDone) { | |
496 oSpan->fCanceled = true; | |
497 #if DEBUG_MARK_DONE | |
498 const SkPoint& oPt = xyAtT(oSpan); | |
499 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n"
, | |
500 __FUNCTION__, other->fID, oStart, oSpan->fT, | |
501 oPt.fX, oPt.fY); | |
502 #endif | |
503 SkASSERT(!oSpan->fDone); | |
504 oSpan->fDone = true; | |
505 oSpan->fWinding = 0; | |
506 other->fDoneSpans++; | |
507 } | |
508 if (--oStart < 0) { | |
509 break; | |
510 } | |
511 oSpan = &other->fTs[oStart]; | |
512 } while (oSpanT - oSpan->fT < FLT_EPSILON); | |
513 } while (fTs[start].fT <= maxT); | |
514 } | |
515 | |
516 bool canceled(int start, int end) const { | |
517 int min = SkMin32(start, end); | |
518 return fTs[min].fCanceled; | |
519 } | |
520 | |
521 void markAndChaseCoincident(int index, int endIndex, Segment* other) { | |
522 int step = SkSign32(endIndex - index); | |
523 innerCoincidentChase(step, other); | |
524 } | |
525 | |
OLD | NEW |