| 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 |