Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: src/pathops/SkDQuadIntersection.cpp

Issue 633393002: harden pathops for pathological test (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: exclude new test that asserts in debug Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pathops/SkDLineIntersection.cpp ('k') | src/pathops/SkIntersections.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Another approach is to start with the implicit form of one curve and solve 1 // Another approach is to start with the implicit form of one curve and solve
2 // (seek implicit coefficients in QuadraticParameter.cpp 2 // (seek implicit coefficients in QuadraticParameter.cpp
3 // by substituting in the parametric form of the other. 3 // by substituting in the parametric form of the other.
4 // The downside of this approach is that early rejects are difficult to come by. 4 // The downside of this approach is that early rejects are difficult to come by.
5 // http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormu la.html#step 5 // http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormu la.html#step
6 6
7 #include "SkDQuadImplicit.h" 7 #include "SkDQuadImplicit.h"
8 #include "SkIntersections.h" 8 #include "SkIntersections.h"
9 #include "SkPathOpsLine.h" 9 #include "SkPathOpsLine.h"
10 #include "SkQuarticRoot.h" 10 #include "SkQuarticRoot.h"
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 for (index = 0; index < count; ++index) { 66 for (index = 0; index < count; ++index) {
67 if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_l ess(roots[index])) { 67 if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_l ess(roots[index])) {
68 continue; 68 continue;
69 } 69 }
70 double t = 1 - roots[index]; 70 double t = 1 - roots[index];
71 if (approximately_less_than_zero(t)) { 71 if (approximately_less_than_zero(t)) {
72 t = 0; 72 t = 0;
73 } else if (approximately_greater_than_one(t)) { 73 } else if (approximately_greater_than_one(t)) {
74 t = 1; 74 t = 1;
75 } 75 }
76 SkASSERT(t >= 0 && t <= 1);
76 valid[result++] = t; 77 valid[result++] = t;
77 } 78 }
78 return result; 79 return result;
79 } 80 }
80 81
81 static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) { 82 static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
82 // the idea here is to see at minimum do a quick reject by rotating all points 83 // the idea here is to see at minimum do a quick reject by rotating all points
83 // to either side of the line formed by connecting the endpoints 84 // to either side of the line formed by connecting the endpoints
84 // if the opposite curves points are on the line or on the other side, the 85 // if the opposite curves points are on the line or on the other side, the
85 // curves at most intersect at the endpoints 86 // curves at most intersect at the endpoints
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 236
236 static double flat_measure(const SkDQuad& q) { 237 static double flat_measure(const SkDQuad& q) {
237 SkDVector mid = q[1] - q[0]; 238 SkDVector mid = q[1] - q[0];
238 SkDVector dxy = q[2] - q[0]; 239 SkDVector dxy = q[2] - q[0];
239 double length = dxy.length(); // OPTIMIZE: get rid of sqrt 240 double length = dxy.length(); // OPTIMIZE: get rid of sqrt
240 return fabs(mid.cross(dxy) / length); 241 return fabs(mid.cross(dxy) / length);
241 } 242 }
242 243
243 // FIXME ? should this measure both and then use the quad that is the flattest a s the line? 244 // FIXME ? should this measure both and then use the quad that is the flattest a s the line?
244 static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) { 245 static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
245 double measure = flat_measure(q1); 246 if (i->flatMeasure()) {
246 // OPTIMIZE: (get rid of sqrt) use approximately_zero 247 // for backward compatibility, use the old method when called from cubic s
247 if (!approximately_zero_sqrt(measure)) { 248 // FIXME: figure out how to fix cubics when it calls the new path
248 return false; 249 double measure = flat_measure(q1);
250 // OPTIMIZE: (get rid of sqrt) use approximately_zero
251 if (!approximately_zero_sqrt(measure)) { // approximately_zero_sqrt
252 return false;
253 }
254 } else {
255 if (!q1.isLinear(0, 2)) {
256 return false;
257 }
249 } 258 }
250 return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL); 259 return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
251 } 260 }
252 261
253 // FIXME: if flat measure is sufficiently large, then probably the quartic solut ion failed 262 // FIXME: if flat measure is sufficiently large, then probably the quartic solut ion failed
254 // avoid imprecision incurred with chopAt 263 // avoid imprecision incurred with chopAt
255 static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkD Quad* q2, 264 static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkD Quad* q2,
256 double s2, double e2, SkIntersections* i) { 265 double s2, double e2, SkIntersections* i) {
257 double m1 = flat_measure(*q1); 266 double m1 = flat_measure(*q1);
258 double m2 = flat_measure(*q2); 267 double m2 = flat_measure(*q2);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 int calcMask = ~0; 307 int calcMask = ~0;
299 do { 308 do {
300 if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed); 309 if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
301 if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed); 310 if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed);
302 if (t1[1].approximatelyEqual(t2[1])) { 311 if (t1[1].approximatelyEqual(t2[1])) {
303 *pt = t1[1]; 312 *pt = t1[1];
304 #if ONE_OFF_DEBUG 313 #if ONE_OFF_DEBUG
305 SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __ FUNCTION__, 314 SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __ FUNCTION__,
306 t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY); 315 t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY);
307 #endif 316 #endif
317 if (*t1Seed < 0) {
318 *t1Seed = 0;
319 } else if (*t1Seed > 1) {
320 *t1Seed = 1;
321 }
322 if (*t2Seed < 0) {
323 *t2Seed = 0;
324 } else if (*t2Seed > 1) {
325 *t2Seed = 1;
326 }
308 return true; 327 return true;
309 } 328 }
310 if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep) ); 329 if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep) );
311 if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep) ); 330 if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep) );
312 if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep) ); 331 if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep) );
313 if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *t2Seed + tStep) ); 332 if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *t2Seed + tStep) );
314 double dist[3][3]; 333 double dist[3][3];
315 // OPTIMIZE: using calcMask value permits skipping some distance calcuat ions 334 // OPTIMIZE: using calcMask value permits skipping some distance calcuat ions
316 // if prior loop's results are moved to correct slot for reuse 335 // if prior loop's results are moved to correct slot for reuse
317 dist[1][1] = t1[1].distanceSquared(t2[1]); 336 dist[1][1] = t1[1].distanceSquared(t2[1]);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 if (swap) { 410 if (swap) {
392 i->insert(testT, impTs[0][index], tmpLine[0]); 411 i->insert(testT, impTs[0][index], tmpLine[0]);
393 } else { 412 } else {
394 i->insert(impTs[0][index], testT, tmpLine[0]); 413 i->insert(impTs[0][index], testT, tmpLine[0]);
395 } 414 }
396 } 415 }
397 } 416 }
398 417
399 int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { 418 int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
400 fMax = 4; 419 fMax = 4;
420 bool exactMatch = false;
401 // if the quads share an end point, check to see if they overlap 421 // if the quads share an end point, check to see if they overlap
402 for (int i1 = 0; i1 < 3; i1 += 2) { 422 for (int i1 = 0; i1 < 3; i1 += 2) {
403 for (int i2 = 0; i2 < 3; i2 += 2) { 423 for (int i2 = 0; i2 < 3; i2 += 2) {
404 if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) { 424 if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
405 insert(i1 >> 1, i2 >> 1, q1[i1]); 425 insert(i1 >> 1, i2 >> 1, q1[i1]);
426 exactMatch = true;
406 } 427 }
407 } 428 }
408 } 429 }
409 SkASSERT(fUsed < 3); 430 SkASSERT(fUsed < 3);
410 if (only_end_pts_in_common(q1, q2)) { 431 if (only_end_pts_in_common(q1, q2)) {
411 return fUsed; 432 return fUsed;
412 } 433 }
413 if (only_end_pts_in_common(q2, q1)) { 434 if (only_end_pts_in_common(q2, q1)) {
414 return fUsed; 435 return fUsed;
415 } 436 }
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 SkDQuadImplicit i1(q1); 483 SkDQuadImplicit i1(q1);
463 SkDQuadImplicit i2(q2); 484 SkDQuadImplicit i2(q2);
464 int index; 485 int index;
465 bool flip1 = q1[2] == q2[0]; 486 bool flip1 = q1[2] == q2[0];
466 bool flip2 = q1[0] == q2[2]; 487 bool flip2 = q1[0] == q2[2];
467 bool useCubic = q1[0] == q2[0]; 488 bool useCubic = q1[0] == q2[0];
468 double roots1[4]; 489 double roots1[4];
469 int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0); 490 int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
470 // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1 491 // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
471 double roots1Copy[4]; 492 double roots1Copy[4];
493 SkDEBUGCODE(sk_bzero(roots1Copy, sizeof(roots1Copy)));
472 int r1Count = addValidRoots(roots1, rootCount, roots1Copy); 494 int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
473 SkDPoint pts1[4]; 495 SkDPoint pts1[4];
474 for (index = 0; index < r1Count; ++index) { 496 for (index = 0; index < r1Count; ++index) {
475 pts1[index] = q1.ptAtT(roots1Copy[index]); 497 pts1[index] = q1.ptAtT(roots1Copy[index]);
476 } 498 }
477 double roots2[4]; 499 double roots2[4];
478 int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0); 500 int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
479 double roots2Copy[4]; 501 double roots2Copy[4];
480 int r2Count = addValidRoots(roots2, rootCount2, roots2Copy); 502 int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
481 SkDPoint pts2[4]; 503 SkDPoint pts2[4];
482 for (index = 0; index < r2Count; ++index) { 504 for (index = 0; index < r2Count; ++index) {
483 pts2[index] = q2.ptAtT(roots2Copy[index]); 505 pts2[index] = q2.ptAtT(roots2Copy[index]);
484 } 506 }
507 bool triedBinary = false;
485 if (r1Count == r2Count && r1Count <= 1) { 508 if (r1Count == r2Count && r1Count <= 1) {
486 if (r1Count == 1 && used() == 0) { 509 if (r1Count == 1 && used() == 0) {
487 if (pts1[0].approximatelyEqual(pts2[0])) { 510 if (pts1[0].approximatelyEqual(pts2[0])) {
488 insert(roots1Copy[0], roots2Copy[0], pts1[0]); 511 insert(roots1Copy[0], roots2Copy[0], pts1[0]);
489 } else { 512 } else {
490 // find intersection by chasing t 513 // find intersection by chasing t
514 triedBinary = true;
491 if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) { 515 if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
492 insert(roots1Copy[0], roots2Copy[0], pts1[0]); 516 insert(roots1Copy[0], roots2Copy[0], pts1[0]);
493 } 517 }
494 } 518 }
495 } 519 }
496 return fUsed; 520 return fUsed;
497 } 521 }
498 int closest[4]; 522 int closest[4];
499 double dist[4]; 523 double dist[4];
500 bool foundSomething = false; 524 bool foundSomething = false;
(...skipping 20 matching lines...) Expand all
521 closest[outer] = -1; 545 closest[outer] = -1;
522 } 546 }
523 dist[index] = distance; 547 dist[index] = distance;
524 closest[index] = ndex2; 548 closest[index] = ndex2;
525 foundSomething = true; 549 foundSomething = true;
526 next: 550 next:
527 ; 551 ;
528 } 552 }
529 } 553 }
530 if (r1Count && r2Count && !foundSomething) { 554 if (r1Count && r2Count && !foundSomething) {
555 if (exactMatch) {
556 SkASSERT(fUsed > 0);
557 return fUsed;
558 }
531 relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this); 559 relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
560 if (fUsed) {
561 return fUsed;
562 }
563 // maybe the curves are nearly coincident
564 if (!triedBinary && binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
565 insert(roots1Copy[0], roots2Copy[0], pts1[0]);
566 }
532 return fUsed; 567 return fUsed;
533 } 568 }
534 int used = 0; 569 int used = 0;
535 do { 570 do {
536 double lowest = DBL_MAX; 571 double lowest = DBL_MAX;
537 int lowestIndex = -1; 572 int lowestIndex = -1;
538 for (index = 0; index < r1Count; ++index) { 573 for (index = 0; index < r1Count; ++index) {
539 if (closest[index] < 0) { 574 if (closest[index] < 0) {
540 continue; 575 continue;
541 } 576 }
542 if (roots1Copy[index] < lowest) { 577 if (roots1Copy[index] < lowest) {
543 lowestIndex = index; 578 lowestIndex = index;
544 lowest = roots1Copy[index]; 579 lowest = roots1Copy[index];
545 } 580 }
546 } 581 }
547 if (lowestIndex < 0) { 582 if (lowestIndex < 0) {
548 break; 583 break;
549 } 584 }
550 insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]], 585 insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
551 pts1[lowestIndex]); 586 pts1[lowestIndex]);
552 closest[lowestIndex] = -1; 587 closest[lowestIndex] = -1;
553 } while (++used < r1Count); 588 } while (++used < r1Count);
554 return fUsed; 589 return fUsed;
555 } 590 }
591
592 void SkIntersections::alignQuadPts(const SkPoint q1[3], const SkPoint q2[3]) {
593 for (int index = 0; index < used(); ++index) {
594 const SkPoint result = pt(index).asSkPoint();
595 if (q1[0] == result || q1[2] == result || q2[0] == result || q2[2] == re sult) {
596 continue;
597 }
598 if (SkDPoint::ApproximatelyEqual(q1[0], result)) {
599 fPt[index].set(q1[0]);
600 // SkASSERT(way_roughly_zero(fT[0][index])); // this value can be bi gger than way rough
601 fT[0][index] = 0;
602 } else if (SkDPoint::ApproximatelyEqual(q1[2], result)) {
603 fPt[index].set(q1[2]);
604 // SkASSERT(way_roughly_equal(fT[0][index], 1));
605 fT[0][index] = 1;
606 }
607 if (SkDPoint::ApproximatelyEqual(q2[0], result)) {
608 fPt[index].set(q2[0]);
609 // SkASSERT(way_roughly_zero(fT[1][index]));
610 fT[1][index] = 0;
611 } else if (SkDPoint::ApproximatelyEqual(q2[2], result)) {
612 fPt[index].set(q2[2]);
613 // SkASSERT(way_roughly_equal(fT[1][index], 1));
614 fT[1][index] = 1;
615 }
616 }
617 }
OLDNEW
« no previous file with comments | « src/pathops/SkDLineIntersection.cpp ('k') | src/pathops/SkIntersections.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698