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 "SkLineParameters.h" | 7 #include "SkLineParameters.h" |
8 #include "SkPathOpsCubic.h" | 8 #include "SkPathOpsCubic.h" |
9 #include "SkPathOpsLine.h" | 9 #include "SkPathOpsLine.h" |
10 #include "SkPathOpsQuad.h" | 10 #include "SkPathOpsQuad.h" |
11 #include "SkPathOpsRect.h" | 11 #include "SkPathOpsRect.h" |
| 12 #include "SkTSort.h" |
12 | 13 |
13 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework | 14 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework |
14 | 15 |
| 16 // give up when changing t no longer moves point |
| 17 // also, copy point rather than recompute it when it does change |
| 18 double SkDCubic::binarySearch(double min, double max, double axisIntercept, |
| 19 SearchAxis xAxis) const { |
| 20 double t = (min + max) / 2; |
| 21 double step = (t - min) / 2; |
| 22 SkDPoint cubicAtT = ptAtT(t); |
| 23 double calcPos = (&cubicAtT.fX)[xAxis]; |
| 24 double calcDist = calcPos - axisIntercept; |
| 25 do { |
| 26 double priorT = t - step; |
| 27 SkASSERT(priorT >= min); |
| 28 SkDPoint lessPt = ptAtT(priorT); |
| 29 if (approximately_equal(lessPt.fX, cubicAtT.fX) |
| 30 && approximately_equal(lessPt.fY, cubicAtT.fY)) { |
| 31 return -1; // binary search found no point at this axis intercept |
| 32 } |
| 33 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept; |
| 34 #if DEBUG_CUBIC_BINARY_SEARCH |
| 35 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, cal
cPos, calcDist, |
| 36 step, lessDist); |
| 37 #endif |
| 38 double lastStep = step; |
| 39 step /= 2; |
| 40 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) { |
| 41 t = priorT; |
| 42 } else { |
| 43 double nextT = t + lastStep; |
| 44 SkASSERT(nextT <= max); |
| 45 SkDPoint morePt = ptAtT(nextT); |
| 46 if (approximately_equal(morePt.fX, cubicAtT.fX) |
| 47 && approximately_equal(morePt.fY, cubicAtT.fY)) { |
| 48 return -1; // binary search found no point at this axis interce
pt |
| 49 } |
| 50 double moreDist = (&morePt.fX)[xAxis] - axisIntercept; |
| 51 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) { |
| 52 continue; |
| 53 } |
| 54 t = nextT; |
| 55 } |
| 56 SkDPoint testAtT = ptAtT(t); |
| 57 cubicAtT = testAtT; |
| 58 calcPos = (&cubicAtT.fX)[xAxis]; |
| 59 calcDist = calcPos - axisIntercept; |
| 60 } while (!approximately_equal(calcPos, axisIntercept)); |
| 61 return t; |
| 62 } |
| 63 |
15 // FIXME: cache keep the bounds and/or precision with the caller? | 64 // FIXME: cache keep the bounds and/or precision with the caller? |
16 double SkDCubic::calcPrecision() const { | 65 double SkDCubic::calcPrecision() const { |
17 SkDRect dRect; | 66 SkDRect dRect; |
18 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? | 67 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? |
19 double width = dRect.fRight - dRect.fLeft; | 68 double width = dRect.fRight - dRect.fLeft; |
20 double height = dRect.fBottom - dRect.fTop; | 69 double height = dRect.fBottom - dRect.fTop; |
21 return (width > height ? width : height) / gPrecisionUnit; | 70 return (width > height ? width : height) / gPrecisionUnit; |
22 } | 71 } |
23 | 72 |
24 bool SkDCubic::clockwise() const { | 73 bool SkDCubic::clockwise() const { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 } | 135 } |
87 distance = lineParameters.controlPtDistance(*this, 2); | 136 distance = lineParameters.controlPtDistance(*this, 2); |
88 return approximately_zero(distance); | 137 return approximately_zero(distance); |
89 } | 138 } |
90 | 139 |
91 bool SkDCubic::monotonicInY() const { | 140 bool SkDCubic::monotonicInY() const { |
92 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 141 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
93 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); | 142 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); |
94 } | 143 } |
95 | 144 |
| 145 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
, |
| 146 SearchAxis xAxis, double* validRoots) const { |
| 147 extrema += findInflections(&extremeTs[extrema]); |
| 148 extremeTs[extrema++] = 0; |
| 149 extremeTs[extrema] = 1; |
| 150 SkTQSort(extremeTs, extremeTs + extrema); |
| 151 int validCount = 0; |
| 152 for (int index = 0; index < extrema; ) { |
| 153 double min = extremeTs[index]; |
| 154 double max = extremeTs[++index]; |
| 155 if (min == max) { |
| 156 continue; |
| 157 } |
| 158 double newT = binarySearch(min, max, axisIntercept, xAxis); |
| 159 if (newT >= 0) { |
| 160 validRoots[validCount++] = newT; |
| 161 } |
| 162 } |
| 163 return validCount; |
| 164 } |
| 165 |
96 bool SkDCubic::serpentine() const { | 166 bool SkDCubic::serpentine() const { |
97 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO
p53d | 167 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO
p53d |
98 double tValues[2]; | 168 double tValues[2]; |
99 // OPTIMIZATION : another case where caching the present of cubic inflection
s would be useful | 169 // OPTIMIZATION : another case where caching the present of cubic inflection
s would be useful |
100 return findInflections(tValues) > 1; | 170 return findInflections(tValues) > 1; |
101 #endif | 171 #endif |
102 if (!controlsContainedByEnds()) { | 172 if (!controlsContainedByEnds()) { |
103 return false; | 173 return false; |
104 } | 174 } |
105 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); | 175 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 double A = fabs(R) + sqrtR2MinusQ3; | 273 double A = fabs(R) + sqrtR2MinusQ3; |
204 A = SkDCubeRoot(A); | 274 A = SkDCubeRoot(A); |
205 if (R > 0) { | 275 if (R > 0) { |
206 A = -A; | 276 A = -A; |
207 } | 277 } |
208 if (A != 0) { | 278 if (A != 0) { |
209 A += Q / A; | 279 A += Q / A; |
210 } | 280 } |
211 r = A - adiv3; | 281 r = A - adiv3; |
212 *roots++ = r; | 282 *roots++ = r; |
213 if (AlmostDequalUlps(R2, Q3)) { | 283 if (AlmostDequalUlps((double) R2, (double) Q3)) { |
214 r = -A / 2 - adiv3; | 284 r = -A / 2 - adiv3; |
215 if (!AlmostDequalUlps(s[0], r)) { | 285 if (!AlmostDequalUlps(s[0], r)) { |
216 *roots++ = r; | 286 *roots++ = r; |
217 } | 287 } |
218 } | 288 } |
219 } | 289 } |
220 return static_cast<int>(roots - s); | 290 return static_cast<int>(roots - s); |
221 } | 291 } |
222 | 292 |
223 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf | 293 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; | 576 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; |
507 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; | 577 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; |
508 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; | 578 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; |
509 dst.pts[6] = fPts[3]; | 579 dst.pts[6] = fPts[3]; |
510 return dst; | 580 return dst; |
511 } | 581 } |
512 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); | 582 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); |
513 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); | 583 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); |
514 return dst; | 584 return dst; |
515 } | 585 } |
OLD | NEW |