| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 #include "SkGeometry.h" | 7 #include "SkGeometry.h" |
| 8 #include "SkLineParameters.h" | 8 #include "SkLineParameters.h" |
| 9 #include "SkPathOpsConic.h" | 9 #include "SkPathOpsConic.h" |
| 10 #include "SkPathOpsCubic.h" | 10 #include "SkPathOpsCubic.h" |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 | 68 |
| 69 // FIXME: cache keep the bounds and/or precision with the caller? | 69 // FIXME: cache keep the bounds and/or precision with the caller? |
| 70 double SkDCubic::calcPrecision() const { | 70 double SkDCubic::calcPrecision() const { |
| 71 SkDRect dRect; | 71 SkDRect dRect; |
| 72 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? | 72 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? |
| 73 double width = dRect.fRight - dRect.fLeft; | 73 double width = dRect.fRight - dRect.fLeft; |
| 74 double height = dRect.fBottom - dRect.fTop; | 74 double height = dRect.fBottom - dRect.fTop; |
| 75 return (width > height ? width : height) / gPrecisionUnit; | 75 return (width > height ? width : height) / gPrecisionUnit; |
| 76 } | 76 } |
| 77 | 77 |
| 78 bool SkDCubic::clockwise(bool* swap) const { | 78 bool SkDCubic::clockwise(const SkDCubic& whole, bool* swap) const { |
| 79 SkDPoint lastPt = fPts[kPointLast]; | 79 SkDPoint lastPt = fPts[kPointLast]; |
| 80 SkDPoint firstPt = fPts[0]; | 80 SkDPoint firstPt = fPts[0]; |
| 81 double sum = 0; | 81 double sum = 0; |
| 82 // pick the control point furthest from the line connecting the end points | 82 // pick the control point furthest from the line connecting the end points |
| 83 SkLineParameters lineParameters; | 83 SkLineParameters lineParameters; |
| 84 lineParameters.cubicEndPoints(*this, 0, 3); | 84 lineParameters.cubicEndPoints(*this, 0, 3); |
| 85 lineParameters.normalize(); | 85 lineParameters.normalize(); |
| 86 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX
, fPts[0].fY), | 86 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX
, fPts[0].fY), |
| 87 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); | 87 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); |
| 88 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX
, fPts[0].fY), | 88 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX
, fPts[0].fY), |
| 89 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); | 89 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); |
| 90 largest = SkTMax(largest, -tiniest); | 90 largest = SkTMax(largest, -tiniest); |
| 91 double pt1dist = lineParameters.controlPtDistance(*this, 1); | 91 double pt1dist = lineParameters.controlPtDistance(*this, 1); |
| 92 double pt2dist = lineParameters.controlPtDistance(*this, 2); | 92 double pt2dist = lineParameters.controlPtDistance(*this, 2); |
| 93 #if DEBUG_SWAP_TOP | 93 #if DEBUG_SWAP_TOP |
| 94 SkDebugf("%s pt1dist=%1.9g pt2dist=%1.9g\n", __FUNCTION__, pt1dist, pt2dist)
; | 94 SkDebugf("%s pt1dist=%1.9g pt2dist=%1.9g\n", __FUNCTION__, pt1dist, pt2dist)
; |
| 95 #endif | 95 #endif |
| 96 int furthest; | 96 int furthest; |
| 97 if (!approximately_zero_when_compared_to(pt1dist, largest) | 97 if (!approximately_zero_when_compared_to(pt1dist, largest) |
| 98 && !approximately_zero_when_compared_to(pt2dist, largest) && pt1dist
* pt2dist < 0) { | 98 && !approximately_zero_when_compared_to(pt2dist, largest) && pt1dist
* pt2dist < 0) { |
| 99 furthest = 2; | 99 furthest = 2; |
| 100 } else { | 100 } else { |
| 101 furthest = fabs(pt1dist) < fabs(pt2dist) ? 2 : 1; | 101 furthest = fabs(pt1dist) < fabs(pt2dist) ? 2 : 1; |
| 102 } | 102 } |
| 103 for (int idx = 1; idx <= 3; ++idx) { | 103 for (int idx = 1; idx <= 3; ++idx) { |
| 104 sum += (firstPt.fX - lastPt.fX) * (firstPt.fY + lastPt.fY); | 104 sum += (firstPt.fX - lastPt.fX) * (firstPt.fY + lastPt.fY); |
| 105 lastPt = firstPt; | 105 lastPt = firstPt; |
| 106 firstPt = idx == 1 ? fPts[furthest] : fPts[kPointLast]; | 106 firstPt = idx == 1 ? fPts[furthest] : fPts[kPointLast]; |
| 107 } | 107 } |
| 108 *swap = sum > 0 && !this->monotonicInY(); | 108 *swap = sum > 0 && !this->monotonicInY() && !whole.monotonicInY(); |
| 109 return sum <= 0; | 109 return sum <= 0; |
| 110 } | 110 } |
| 111 | 111 |
| 112 bool SkDCubic::Clockwise(const SkPoint* pts, double startT, double endT, bool* s
wap) { | 112 bool SkDCubic::Clockwise(const SkPoint* pts, double startT, double endT, bool* s
wap) { |
| 113 SkDCubic cubic; | 113 SkDCubic cubic; |
| 114 cubic.set(pts); | 114 cubic.set(pts); |
| 115 #if 0 | |
| 116 bool flip = startT > endT; | |
| 117 double inflectionTs[2]; | |
| 118 int inflections = cubic.findInflections(inflectionTs); | |
| 119 for (int index = 0; index < inflections; ++index) { | |
| 120 double inflectionT = inflectionTs[index]; | |
| 121 if (between(startT, inflectionT, endT)) { | |
| 122 if (flip) { | |
| 123 if (!roughly_equal(inflectionT, endT)) { | |
| 124 startT = inflectionT; | |
| 125 } | |
| 126 } else { | |
| 127 if (!roughly_equal(inflectionT, startT)) { | |
| 128 endT = inflectionT; | |
| 129 } | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 #endif | |
| 134 SkDCubic part = cubic.subDivide(startT, endT); | 115 SkDCubic part = cubic.subDivide(startT, endT); |
| 135 return part.clockwise(swap); | 116 return part.clockwise(cubic, swap); |
| 136 } | 117 } |
| 137 | 118 |
| 138 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { | 119 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { |
| 139 *A = src[6]; // d | 120 *A = src[6]; // d |
| 140 *B = src[4] * 3; // 3*c | 121 *B = src[4] * 3; // 3*c |
| 141 *C = src[2] * 3; // 3*b | 122 *C = src[2] * 3; // 3*b |
| 142 *D = src[0]; // a | 123 *D = src[0]; // a |
| 143 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d | 124 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d |
| 144 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c | 125 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c |
| 145 *C -= 3 * *D; // C = -3*a + 3*b | 126 *C -= 3 * *D; // C = -3*a + 3*b |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 } | 275 } |
| 295 } else if (infTCount == 1) { | 276 } else if (infTCount == 1) { |
| 296 *t = inflectionTs[0]; | 277 *t = inflectionTs[0]; |
| 297 *resultType = kSplitAtInflection_SkDCubicType; | 278 *resultType = kSplitAtInflection_SkDCubicType; |
| 298 return *t > 0 && *t < 1; | 279 return *t > 0 && *t < 1; |
| 299 } | 280 } |
| 300 } | 281 } |
| 301 return false; | 282 return false; |
| 302 } | 283 } |
| 303 | 284 |
| 285 bool SkDCubic::monotonicInX() const { |
| 286 return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX) |
| 287 && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX); |
| 288 } |
| 289 |
| 304 bool SkDCubic::monotonicInY() const { | 290 bool SkDCubic::monotonicInY() const { |
| 305 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 291 return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
| 306 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); | 292 && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY); |
| 307 } | 293 } |
| 308 | 294 |
| 309 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const
{ | 295 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const
{ |
| 310 int offset = (int) !SkToBool(index); | 296 int offset = (int) !SkToBool(index); |
| 311 o1Pts[0] = &fPts[offset]; | 297 o1Pts[0] = &fPts[offset]; |
| 312 o1Pts[1] = &fPts[++offset]; | 298 o1Pts[1] = &fPts[++offset]; |
| 313 o1Pts[2] = &fPts[++offset]; | 299 o1Pts[2] = &fPts[++offset]; |
| 314 } | 300 } |
| 315 | 301 |
| 316 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
, | 302 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
, |
| (...skipping 19 matching lines...) Expand all Loading... |
| 336 | 322 |
| 337 // cubic roots | 323 // cubic roots |
| 338 | 324 |
| 339 static const double PI = 3.141592653589793; | 325 static const double PI = 3.141592653589793; |
| 340 | 326 |
| 341 // from SkGeometry.cpp (and Numeric Solutions, 5.6) | 327 // from SkGeometry.cpp (and Numeric Solutions, 5.6) |
| 342 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { | 328 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { |
| 343 double s[3]; | 329 double s[3]; |
| 344 int realRoots = RootsReal(A, B, C, D, s); | 330 int realRoots = RootsReal(A, B, C, D, s); |
| 345 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); | 331 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); |
| 332 for (int index = 0; index < realRoots; ++index) { |
| 333 double tValue = s[index]; |
| 334 if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) { |
| 335 for (int idx2 = 0; idx2 < foundRoots; ++idx2) { |
| 336 if (approximately_equal(t[idx2], 1)) { |
| 337 goto nextRoot; |
| 338 } |
| 339 } |
| 340 SkASSERT(foundRoots < 3); |
| 341 t[foundRoots++] = 1; |
| 342 } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tVal
ue, 0)) { |
| 343 for (int idx2 = 0; idx2 < foundRoots; ++idx2) { |
| 344 if (approximately_equal(t[idx2], 0)) { |
| 345 goto nextRoot; |
| 346 } |
| 347 } |
| 348 SkASSERT(foundRoots < 3); |
| 349 t[foundRoots++] = 0; |
| 350 } |
| 351 nextRoot: |
| 352 ; |
| 353 } |
| 346 return foundRoots; | 354 return foundRoots; |
| 347 } | 355 } |
| 348 | 356 |
| 349 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { | 357 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { |
| 350 #ifdef SK_DEBUG | 358 #ifdef SK_DEBUG |
| 351 // create a string mathematica understands | 359 // create a string mathematica understands |
| 352 // GDB set print repe 15 # if repeated digits is a bother | 360 // GDB set print repe 15 # if repeated digits is a bother |
| 353 // set print elements 400 # if line doesn't fit | 361 // set print elements 400 # if line doesn't fit |
| 354 char str[1024]; | 362 char str[1024]; |
| 355 sk_bzero(str, sizeof(str)); | 363 sk_bzero(str, sizeof(str)); |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 coeff[2] = 2 * b * b + c * a; | 488 coeff[2] = 2 * b * b + c * a; |
| 481 coeff[3] = a * b; | 489 coeff[3] = a * b; |
| 482 } | 490 } |
| 483 | 491 |
| 484 /** SkDCubic'(t) = At^2 + Bt + C, where | 492 /** SkDCubic'(t) = At^2 + Bt + C, where |
| 485 A = 3(-a + 3(b - c) + d) | 493 A = 3(-a + 3(b - c) + d) |
| 486 B = 6(a - 2b + c) | 494 B = 6(a - 2b + c) |
| 487 C = 3(b - a) | 495 C = 3(b - a) |
| 488 Solve for t, keeping only those that fit between 0 < t < 1 | 496 Solve for t, keeping only those that fit between 0 < t < 1 |
| 489 */ | 497 */ |
| 490 int SkDCubic::FindExtrema(double a, double b, double c, double d, double tValues
[2]) { | 498 int SkDCubic::FindExtrema(const double src[], double tValues[2]) { |
| 491 // we divide A,B,C by 3 to simplify | 499 // we divide A,B,C by 3 to simplify |
| 492 double A = d - a + 3*(b - c); | 500 double a = src[0]; |
| 493 double B = 2*(a - b - b + c); | 501 double b = src[2]; |
| 502 double c = src[4]; |
| 503 double d = src[6]; |
| 504 double A = d - a + 3 * (b - c); |
| 505 double B = 2 * (a - b - b + c); |
| 494 double C = b - a; | 506 double C = b - a; |
| 495 | 507 |
| 496 return SkDQuad::RootsValidT(A, B, C, tValues); | 508 return SkDQuad::RootsValidT(A, B, C, tValues); |
| 497 } | 509 } |
| 498 | 510 |
| 499 /* from SkGeometry.cpp | 511 /* from SkGeometry.cpp |
| 500 Looking for F' dot F'' == 0 | 512 Looking for F' dot F'' == 0 |
| 501 | 513 |
| 502 A = b - a | 514 A = b - a |
| 503 B = c - 2b + a | 515 B = c - 2b + a |
| 504 C = d - 3c + 3b - a | 516 C = d - 3c + 3b - a |
| 505 | 517 |
| 506 F' = 3Ct^2 + 6Bt + 3A | 518 F' = 3Ct^2 + 6Bt + 3A |
| 507 F'' = 6Ct + 6B | 519 F'' = 6Ct + 6B |
| 508 | 520 |
| 509 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB | 521 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB |
| 510 */ | 522 */ |
| 511 int SkDCubic::findMaxCurvature(double tValues[]) const { | 523 int SkDCubic::findMaxCurvature(double tValues[]) const { |
| 512 double coeffX[4], coeffY[4]; | 524 double coeffX[4], coeffY[4]; |
| 513 int i; | 525 int i; |
| 514 formulate_F1DotF2(&fPts[0].fX, coeffX); | 526 formulate_F1DotF2(&fPts[0].fX, coeffX); |
| 515 formulate_F1DotF2(&fPts[0].fY, coeffY); | 527 formulate_F1DotF2(&fPts[0].fY, coeffY); |
| 516 for (i = 0; i < 4; i++) { | 528 for (i = 0; i < 4; i++) { |
| 517 coeffX[i] = coeffX[i] + coeffY[i]; | 529 coeffX[i] = coeffX[i] + coeffY[i]; |
| 518 } | 530 } |
| 519 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); | 531 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); |
| 520 } | 532 } |
| 521 | 533 |
| 522 SkDPoint SkDCubic::top(double startT, double endT, double* topT) const { | |
| 523 SkDCubic sub = subDivide(startT, endT); | |
| 524 SkDPoint topPt = sub[0]; | |
| 525 *topT = startT; | |
| 526 if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX))
{ | |
| 527 *topT = endT; | |
| 528 topPt = sub[3]; | |
| 529 } | |
| 530 double extremeTs[2]; | |
| 531 if (!sub.monotonicInY()) { | |
| 532 int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extr
emeTs); | |
| 533 for (int index = 0; index < roots; ++index) { | |
| 534 double t = startT + (endT - startT) * extremeTs[index]; | |
| 535 SkDPoint mid = ptAtT(t); | |
| 536 if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX))
{ | |
| 537 *topT = t; | |
| 538 topPt = mid; | |
| 539 } | |
| 540 } | |
| 541 } | |
| 542 return topPt; | |
| 543 } | |
| 544 | |
| 545 SkDPoint SkDCubic::ptAtT(double t) const { | 534 SkDPoint SkDCubic::ptAtT(double t) const { |
| 546 if (0 == t) { | 535 if (0 == t) { |
| 547 return fPts[0]; | 536 return fPts[0]; |
| 548 } | 537 } |
| 549 if (1 == t) { | 538 if (1 == t) { |
| 550 return fPts[3]; | 539 return fPts[3]; |
| 551 } | 540 } |
| 552 double one_t = 1 - t; | 541 double one_t = 1 - t; |
| 553 double one_t2 = one_t * one_t; | 542 double one_t2 = one_t * one_t; |
| 554 double a = one_t2 * one_t; | 543 double a = one_t2 * one_t; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 715 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; | 704 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; |
| 716 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; | 705 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; |
| 717 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; | 706 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; |
| 718 dst.pts[6] = fPts[3]; | 707 dst.pts[6] = fPts[3]; |
| 719 return dst; | 708 return dst; |
| 720 } | 709 } |
| 721 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); | 710 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); |
| 722 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); | 711 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); |
| 723 return dst; | 712 return dst; |
| 724 } | 713 } |
| OLD | NEW |