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 |