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 | 12 |
13 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework | 13 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework |
14 | 14 |
| 15 int SkDCubic::AddValidTs(double allRoots[3], int realRoots, double validRoots[3]
) { |
| 16 int valid = SkDQuad::AddValidTs(allRoots, realRoots, validRoots); |
| 17 if (valid != 1) { |
| 18 return valid; |
| 19 } |
| 20 if (realRoots == 1) { |
| 21 return valid; |
| 22 } |
| 23 return -1; |
| 24 } |
| 25 |
| 26 double SkDCubic::binarySearch(double step, double t, double axisIntercept, bool
yAxis) const { |
| 27 do { |
| 28 SkDPoint cubicAtT = ptAtT(t); |
| 29 double calcDist = yAxis ? cubicAtT.fX : cubicAtT.fY; |
| 30 if (approximately_equal(calcDist, axisIntercept)) { |
| 31 break; |
| 32 } |
| 33 if (step == 0) { |
| 34 return -1; // binary search with this step failed |
| 35 } |
| 36 calcDist = fabs(calcDist - axisIntercept); |
| 37 double lastStep = step; |
| 38 step /= 2; |
| 39 SkDPoint lessPt = ptAtT(t - lastStep); |
| 40 double lessDist = fabs((yAxis ? lessPt.fX : lessPt.fY) - axisIntercept); |
| 41 if (calcDist > lessDist) { |
| 42 t -= step; |
| 43 t = SkTMax(0., t); |
| 44 } else { |
| 45 SkDPoint morePt = ptAtT(t + lastStep); |
| 46 double moreDist = fabs((yAxis ? morePt.fX : morePt.fY) - axisInterce
pt); |
| 47 if (calcDist <= moreDist) { |
| 48 continue; |
| 49 } |
| 50 t += step; |
| 51 t = SkTMin(1., t); |
| 52 } |
| 53 } while (true); |
| 54 return t; |
| 55 } |
| 56 |
15 // FIXME: cache keep the bounds and/or precision with the caller? | 57 // FIXME: cache keep the bounds and/or precision with the caller? |
16 double SkDCubic::calcPrecision() const { | 58 double SkDCubic::calcPrecision() const { |
17 SkDRect dRect; | 59 SkDRect dRect; |
18 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? | 60 dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? |
19 double width = dRect.fRight - dRect.fLeft; | 61 double width = dRect.fRight - dRect.fLeft; |
20 double height = dRect.fBottom - dRect.fTop; | 62 double height = dRect.fBottom - dRect.fTop; |
21 return (width > height ? width : height) / gPrecisionUnit; | 63 return (width > height ? width : height) / gPrecisionUnit; |
22 } | 64 } |
23 | 65 |
24 bool SkDCubic::clockwise() const { | 66 bool SkDCubic::clockwise() const { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 } | 128 } |
87 distance = lineParameters.controlPtDistance(*this, 2); | 129 distance = lineParameters.controlPtDistance(*this, 2); |
88 return approximately_zero(distance); | 130 return approximately_zero(distance); |
89 } | 131 } |
90 | 132 |
91 bool SkDCubic::monotonicInY() const { | 133 bool SkDCubic::monotonicInY() const { |
92 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 134 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
93 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); | 135 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); |
94 } | 136 } |
95 | 137 |
| 138 void SkDCubic::searchRoots(double allRoots[3], int realRoots, double* tPtr, doub
le axisIntercept, |
| 139 bool yAxis) const { |
| 140 double largest = SkTMax(fabs(allRoots[0]), fabs(allRoots[1])); |
| 141 if (realRoots == 3) { |
| 142 largest = SkTMax(largest, fabs(allRoots[2])); |
| 143 } |
| 144 int largeBits; |
| 145 if (largest <= 1) { |
| 146 double smallest = SkTMin(allRoots[0], allRoots[1]); |
| 147 if (realRoots == 3) { |
| 148 smallest = SkTMin(smallest, allRoots[2]); |
| 149 } |
| 150 SkASSERT(smallest < 0); |
| 151 SkASSERT(smallest >= -1); |
| 152 largeBits = 0; |
| 153 } else { |
| 154 (void) frexp(largest, &largeBits); |
| 155 SkASSERT(largeBits >= 0); |
| 156 SkASSERT(largeBits < 256); |
| 157 } |
| 158 double step = 1e-6; |
| 159 if (largeBits > 21) { |
| 160 step = 1e-1; |
| 161 } else if (largeBits > 18) { |
| 162 step = 1e-2; |
| 163 } else if (largeBits > 15) { |
| 164 step = 1e-3; |
| 165 } else if (largeBits > 12) { |
| 166 step = 1e-4; |
| 167 } else if (largeBits > 9) { |
| 168 step = 1e-5; |
| 169 } |
| 170 do { |
| 171 double newT = binarySearch(step, *tPtr, axisIntercept, yAxis); |
| 172 if (newT >= 0) { |
| 173 *tPtr = newT; |
| 174 return; |
| 175 } |
| 176 step *= 1.5; |
| 177 SkASSERT(step < 1); |
| 178 } while (true); |
| 179 } |
| 180 |
96 bool SkDCubic::serpentine() const { | 181 bool SkDCubic::serpentine() const { |
97 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO
p53d | 182 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO
p53d |
98 double tValues[2]; | 183 double tValues[2]; |
99 // OPTIMIZATION : another case where caching the present of cubic inflection
s would be useful | 184 // OPTIMIZATION : another case where caching the present of cubic inflection
s would be useful |
100 return findInflections(tValues) > 1; | 185 return findInflections(tValues) > 1; |
101 #endif | 186 #endif |
102 if (!controlsContainedByEnds()) { | 187 if (!controlsContainedByEnds()) { |
103 return false; | 188 return false; |
104 } | 189 } |
105 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); | 190 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); |
106 for (int idx = 0; idx < 2; ++idx) { | 191 for (int idx = 0; idx < 2; ++idx) { |
107 wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); | 192 wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); |
108 } | 193 } |
109 double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY); | 194 double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY); |
110 for (int idx = 1; idx < 3; ++idx) { | 195 for (int idx = 1; idx < 3; ++idx) { |
111 waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); | 196 waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); |
112 } | 197 } |
113 return wiggle * waggle < 0; | 198 return wiggle * waggle < 0; |
114 } | 199 } |
115 | 200 |
116 // cubic roots | 201 // cubic roots |
117 | 202 |
118 static const double PI = 3.141592653589793; | 203 static const double PI = 3.141592653589793; |
119 | 204 |
120 // from SkGeometry.cpp (and Numeric Solutions, 5.6) | 205 // from SkGeometry.cpp (and Numeric Solutions, 5.6) |
| 206 int SkDCubic::RootsValidT(double A, double B, double C, double D, double s[3], i
nt* sCount, |
| 207 double t[3]) { |
| 208 *sCount = RootsReal(A, B, C, D, s); |
| 209 int foundRoots = SkDCubic::AddValidTs(s, *sCount, t); |
| 210 return foundRoots; |
| 211 } |
| 212 |
121 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { | 213 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { |
122 double s[3]; | 214 double s[3]; |
123 int realRoots = RootsReal(A, B, C, D, s); | 215 int realRoots = RootsReal(A, B, C, D, s); |
124 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); | 216 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); |
125 return foundRoots; | 217 return foundRoots; |
126 } | 218 } |
127 | 219 |
128 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { | 220 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { |
129 #ifdef SK_DEBUG | 221 #ifdef SK_DEBUG |
130 // create a string mathematica understands | 222 // create a string mathematica understands |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 a = B * invA; | 265 a = B * invA; |
174 b = C * invA; | 266 b = C * invA; |
175 c = D * invA; | 267 c = D * invA; |
176 } | 268 } |
177 double a2 = a * a; | 269 double a2 = a * a; |
178 double Q = (a2 - b * 3) / 9; | 270 double Q = (a2 - b * 3) / 9; |
179 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; | 271 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; |
180 double R2 = R * R; | 272 double R2 = R * R; |
181 double Q3 = Q * Q * Q; | 273 double Q3 = Q * Q * Q; |
182 double R2MinusQ3 = R2 - Q3; | 274 double R2MinusQ3 = R2 - Q3; |
| 275 double R2Q3Max = SkTMax(R2, fabs(Q3)); |
| 276 if (R2Q3Max * FLT_EPSILON > fabs(R2MinusQ3)) { |
| 277 #if defined(__clang__) || defined(__GNUC__) |
| 278 __float128 R2_128 = (__float128) R * (__float128) R; |
| 279 __float128 Q3_128 = (__float128) Q * (__float128) Q * (__float128) Q; |
| 280 __float128 R2MinusQ3_128 = R2_128 - Q3_128; |
| 281 R2MinusQ3 = (double) R2MinusQ3_128; |
| 282 #else |
| 283 SkFloat128 R2_128 = SkFloat128Mul(R, R); |
| 284 SkFloat128 Q3_128 = SkFloat128Mul(Q, Q); |
| 285 Q3_128 = SkFloat128Mul(Q3_128, Q); |
| 286 R2MinusQ3 = SkFloat128Sub(R2_128, Q3_128); |
| 287 #endif |
| 288 } |
183 double adiv3 = a / 3; | 289 double adiv3 = a / 3; |
184 double r; | 290 double r; |
185 double* roots = s; | 291 double* roots = s; |
186 if (R2MinusQ3 < 0) { // we have 3 real roots | 292 if (R2MinusQ3 < 0) { // we have 3 real roots |
187 double theta = acos(R / sqrt(Q3)); | 293 double theta = acos(R / sqrt(Q3)); |
188 double neg2RootQ = -2 * sqrt(Q); | 294 double neg2RootQ = -2 * sqrt(Q); |
189 | 295 |
190 r = neg2RootQ * cos(theta / 3) - adiv3; | 296 r = neg2RootQ * cos(theta / 3) - adiv3; |
191 *roots++ = r; | 297 *roots++ = r; |
192 | 298 |
(...skipping 10 matching lines...) Expand all Loading... |
203 double A = fabs(R) + sqrtR2MinusQ3; | 309 double A = fabs(R) + sqrtR2MinusQ3; |
204 A = SkDCubeRoot(A); | 310 A = SkDCubeRoot(A); |
205 if (R > 0) { | 311 if (R > 0) { |
206 A = -A; | 312 A = -A; |
207 } | 313 } |
208 if (A != 0) { | 314 if (A != 0) { |
209 A += Q / A; | 315 A += Q / A; |
210 } | 316 } |
211 r = A - adiv3; | 317 r = A - adiv3; |
212 *roots++ = r; | 318 *roots++ = r; |
213 if (AlmostDequalUlps(R2, Q3)) { | 319 if (AlmostDequalUlps((double) R2, (double) Q3)) { |
214 r = -A / 2 - adiv3; | 320 r = -A / 2 - adiv3; |
215 if (!AlmostDequalUlps(s[0], r)) { | 321 if (!AlmostDequalUlps(s[0], r)) { |
216 *roots++ = r; | 322 *roots++ = r; |
217 } | 323 } |
218 } | 324 } |
219 } | 325 } |
220 return static_cast<int>(roots - s); | 326 return static_cast<int>(roots - s); |
221 } | 327 } |
222 | 328 |
223 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf | 329 // 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; | 612 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; | 613 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; |
508 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; | 614 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; |
509 dst.pts[6] = fPts[3]; | 615 dst.pts[6] = fPts[3]; |
510 return dst; | 616 return dst; |
511 } | 617 } |
512 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); | 618 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); |
513 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); | 619 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); |
514 return dst; | 620 return dst; |
515 } | 621 } |
OLD | NEW |