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" |
| 11 #include "SkPathOpsCurve.h" |
11 #include "SkPathOpsLine.h" | 12 #include "SkPathOpsLine.h" |
12 #include "SkPathOpsQuad.h" | 13 #include "SkPathOpsQuad.h" |
13 #include "SkPathOpsRect.h" | 14 #include "SkPathOpsRect.h" |
14 #include "SkTSort.h" | 15 #include "SkTSort.h" |
15 | 16 |
16 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework | 17 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework |
17 | 18 |
18 // give up when changing t no longer moves point | 19 // give up when changing t no longer moves point |
19 // also, copy point rather than recompute it when it does change | 20 // also, copy point rather than recompute it when it does change |
20 double SkDCubic::binarySearch(double min, double max, double axisIntercept, | 21 double SkDCubic::binarySearch(double min, double max, double axisIntercept, |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 | 68 |
68 // FIXME: cache keep the bounds and/or precision with the caller? | 69 // FIXME: cache keep the bounds and/or precision with the caller? |
69 double SkDCubic::calcPrecision() const { | 70 double SkDCubic::calcPrecision() const { |
70 SkDRect dRect; | 71 SkDRect dRect; |
71 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? | 72 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? |
72 double width = dRect.fRight - dRect.fLeft; | 73 double width = dRect.fRight - dRect.fLeft; |
73 double height = dRect.fBottom - dRect.fTop; | 74 double height = dRect.fBottom - dRect.fTop; |
74 return (width > height ? width : height) / gPrecisionUnit; | 75 return (width > height ? width : height) / gPrecisionUnit; |
75 } | 76 } |
76 | 77 |
77 bool SkDCubic::clockwise() const { | 78 bool SkDCubic::clockwise(bool* swap) const { |
78 double sum = (fPts[0].fX - fPts[3].fX) * (fPts[0].fY + fPts[3].fY); | 79 SkDPoint lastPt = fPts[kPointLast]; |
79 for (int idx = 0; idx < 3; ++idx) { | 80 SkDPoint firstPt = fPts[0]; |
80 sum += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx]
.fY); | 81 double sum = 0; |
| 82 // pick the control point furthest from the line connecting the end points |
| 83 SkLineParameters lineParameters; |
| 84 lineParameters.cubicEndPoints(*this, 0, 3); |
| 85 lineParameters.normalize(); |
| 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); |
| 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); |
| 90 largest = SkTMax(largest, -tiniest); |
| 91 double pt1dist = lineParameters.controlPtDistance(*this, 1); |
| 92 double pt2dist = lineParameters.controlPtDistance(*this, 2); |
| 93 #if DEBUG_SWAP_TOP |
| 94 SkDebugf("%s pt1dist=%1.9g pt2dist=%1.9g\n", __FUNCTION__, pt1dist, pt2dist)
; |
| 95 #endif |
| 96 int furthest; |
| 97 if (!approximately_zero_when_compared_to(pt1dist, largest) |
| 98 && !approximately_zero_when_compared_to(pt2dist, largest) && pt1dist
* pt2dist < 0) { |
| 99 furthest = 2; |
| 100 } else { |
| 101 furthest = fabs(pt1dist) < fabs(pt2dist) ? 2 : 1; |
81 } | 102 } |
| 103 for (int idx = 1; idx <= 3; ++idx) { |
| 104 sum += (firstPt.fX - lastPt.fX) * (firstPt.fY + lastPt.fY); |
| 105 lastPt = firstPt; |
| 106 firstPt = idx == 1 ? fPts[furthest] : fPts[kPointLast]; |
| 107 } |
| 108 *swap = sum > 0 && !this->monotonicInY(); |
82 return sum <= 0; | 109 return sum <= 0; |
83 } | 110 } |
84 | 111 |
| 112 bool SkDCubic::Clockwise(const SkPoint* pts, double startT, double endT, bool* s
wap) { |
| 113 SkDCubic cubic; |
| 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); |
| 135 return part.clockwise(swap); |
| 136 } |
| 137 |
85 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { | 138 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { |
86 *A = src[6]; // d | 139 *A = src[6]; // d |
87 *B = src[4] * 3; // 3*c | 140 *B = src[4] * 3; // 3*c |
88 *C = src[2] * 3; // 3*b | 141 *C = src[2] * 3; // 3*b |
89 *D = src[0]; // a | 142 *D = src[0]; // a |
90 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d | 143 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d |
91 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c | 144 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c |
92 *C -= 3 * *D; // C = -3*a + 3*b | 145 *C -= 3 * *D; // C = -3*a + 3*b |
93 } | 146 } |
94 | 147 |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); | 232 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); |
180 largest = SkTMax(largest, -tiniest); | 233 largest = SkTMax(largest, -tiniest); |
181 double distance = lineParameters.controlPtDistance(*this, 1); | 234 double distance = lineParameters.controlPtDistance(*this, 1); |
182 if (!approximately_zero_when_compared_to(distance, largest)) { | 235 if (!approximately_zero_when_compared_to(distance, largest)) { |
183 return false; | 236 return false; |
184 } | 237 } |
185 distance = lineParameters.controlPtDistance(*this, 2); | 238 distance = lineParameters.controlPtDistance(*this, 2); |
186 return approximately_zero_when_compared_to(distance, largest); | 239 return approximately_zero_when_compared_to(distance, largest); |
187 } | 240 } |
188 | 241 |
189 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { | 242 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t, CubicType*
resultType) { |
190 SkScalar d[3]; | 243 SkScalar d[3]; |
191 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); | 244 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); |
192 if (cubicType == kLoop_SkCubicType) { | 245 if (cubicType == kLoop_SkCubicType) { |
193 // crib code from gpu path utils that finds t values where loop self-int
ersects | 246 // crib code from gpu path utils that finds t values where loop self-int
ersects |
194 // use it to find mid of t values which should be a friendly place to ch
op | 247 // use it to find mid of t values which should be a friendly place to ch
op |
195 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); | 248 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); |
196 SkScalar ls = d[1] - tempSqrt; | 249 SkScalar ls = d[1] - tempSqrt; |
197 SkScalar lt = 2.f * d[0]; | 250 SkScalar lt = 2.f * d[0]; |
198 SkScalar ms = d[1] + tempSqrt; | 251 SkScalar ms = d[1] + tempSqrt; |
199 SkScalar mt = 2.f * d[0]; | 252 SkScalar mt = 2.f * d[0]; |
200 if (between(0, ls, lt) || between(0, ms, mt)) { | 253 if (between(0, ls, lt) || between(0, ms, mt)) { |
201 ls = ls / lt; | 254 ls = ls / lt; |
202 ms = ms / mt; | 255 ms = ms / mt; |
203 SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms)); | 256 SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms)); |
204 SkScalar larger = SkTMin(1.f, SkTMax(ls, ms)); | 257 SkScalar larger = SkTMin(1.f, SkTMax(ls, ms)); |
205 *t = (smaller + larger) / 2; | 258 *t = (smaller + larger) / 2; |
| 259 *resultType = kSplitAtLoop_SkDCubicType; |
206 return *t > 0 && *t < 1; | 260 return *t > 0 && *t < 1; |
207 } | 261 } |
208 } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubi
cType) { | 262 } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubi
cType) { |
209 SkDCubic cubic; | 263 SkDCubic cubic; |
210 cubic.set(pointsPtr); | 264 cubic.set(pointsPtr); |
211 double inflectionTs[2]; | 265 double inflectionTs[2]; |
212 int infTCount = cubic.findInflections(inflectionTs); | 266 int infTCount = cubic.findInflections(inflectionTs); |
213 if (infTCount == 2) { | 267 if (infTCount == 2) { |
214 double maxCurvature[3]; | 268 double maxCurvature[3]; |
215 int roots = cubic.findMaxCurvature(maxCurvature); | 269 int roots = cubic.findMaxCurvature(maxCurvature); |
| 270 #if DEBUG_CUBIC_SPLIT |
| 271 SkDebugf("%s\n", __FUNCTION__); |
| 272 cubic.dump(); |
| 273 for (int index = 0; index < infTCount; ++index) { |
| 274 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index])
; |
| 275 SkDPoint pt = cubic.ptAtT(inflectionTs[index]); |
| 276 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]); |
| 277 SkDLine perp = {{pt - dPt, pt + dPt}}; |
| 278 perp.dump(); |
| 279 } |
| 280 for (int index = 0; index < roots; ++index) { |
| 281 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]); |
| 282 SkDPoint pt = cubic.ptAtT(maxCurvature[index]); |
| 283 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]); |
| 284 SkDLine perp = {{pt - dPt, pt + dPt}}; |
| 285 perp.dump(); |
| 286 } |
| 287 #endif |
216 for (int index = 0; index < roots; ++index) { | 288 for (int index = 0; index < roots; ++index) { |
217 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1
])) { | 289 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1
])) { |
218 *t = maxCurvature[index]; | 290 *t = maxCurvature[index]; |
| 291 *resultType = kSplitAtMaxCurvature_SkDCubicType; |
219 return true; | 292 return true; |
220 } | 293 } |
221 } | 294 } |
222 } else if (infTCount == 1) { | 295 } else if (infTCount == 1) { |
223 *t = inflectionTs[0]; | 296 *t = inflectionTs[0]; |
| 297 *resultType = kSplitAtInflection_SkDCubicType; |
224 return *t > 0 && *t < 1; | 298 return *t > 0 && *t < 1; |
225 } | 299 } |
226 return false; | |
227 } | 300 } |
228 return false; | 301 return false; |
229 } | 302 } |
230 | 303 |
231 bool SkDCubic::monotonicInY() const { | 304 bool SkDCubic::monotonicInY() const { |
232 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 305 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
233 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); | 306 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); |
234 } | 307 } |
235 | 308 |
236 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const
{ | 309 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const
{ |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 double coeffX[4], coeffY[4]; | 512 double coeffX[4], coeffY[4]; |
440 int i; | 513 int i; |
441 formulate_F1DotF2(&fPts[0].fX, coeffX); | 514 formulate_F1DotF2(&fPts[0].fX, coeffX); |
442 formulate_F1DotF2(&fPts[0].fY, coeffY); | 515 formulate_F1DotF2(&fPts[0].fY, coeffY); |
443 for (i = 0; i < 4; i++) { | 516 for (i = 0; i < 4; i++) { |
444 coeffX[i] = coeffX[i] + coeffY[i]; | 517 coeffX[i] = coeffX[i] + coeffY[i]; |
445 } | 518 } |
446 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); | 519 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); |
447 } | 520 } |
448 | 521 |
449 SkDPoint SkDCubic::top(double startT, double endT) const { | 522 SkDPoint SkDCubic::top(double startT, double endT, double* topT) const { |
450 SkDCubic sub = subDivide(startT, endT); | 523 SkDCubic sub = subDivide(startT, endT); |
451 SkDPoint topPt = sub[0]; | 524 SkDPoint topPt = sub[0]; |
| 525 *topT = startT; |
452 if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX))
{ | 526 if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX))
{ |
| 527 *topT = endT; |
453 topPt = sub[3]; | 528 topPt = sub[3]; |
454 } | 529 } |
455 double extremeTs[2]; | 530 double extremeTs[2]; |
456 if (!sub.monotonicInY()) { | 531 if (!sub.monotonicInY()) { |
457 int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extr
emeTs); | 532 int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extr
emeTs); |
458 for (int index = 0; index < roots; ++index) { | 533 for (int index = 0; index < roots; ++index) { |
459 double t = startT + (endT - startT) * extremeTs[index]; | 534 double t = startT + (endT - startT) * extremeTs[index]; |
460 SkDPoint mid = ptAtT(t); | 535 SkDPoint mid = ptAtT(t); |
461 if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX))
{ | 536 if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX))
{ |
| 537 *topT = t; |
462 topPt = mid; | 538 topPt = mid; |
463 } | 539 } |
464 } | 540 } |
465 } | 541 } |
466 return topPt; | 542 return topPt; |
467 } | 543 } |
468 | 544 |
469 SkDPoint SkDCubic::ptAtT(double t) const { | 545 SkDPoint SkDCubic::ptAtT(double t) const { |
470 if (0 == t) { | 546 if (0 == t) { |
471 return fPts[0]; | 547 return fPts[0]; |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
639 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; | 715 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; |
640 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; | 716 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; |
641 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; | 717 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; |
642 dst.pts[6] = fPts[3]; | 718 dst.pts[6] = fPts[3]; |
643 return dst; | 719 return dst; |
644 } | 720 } |
645 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); | 721 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); |
646 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); | 722 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); |
647 return dst; | 723 return dst; |
648 } | 724 } |
OLD | NEW |