1 /*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7 #include "src/core/SkGeometry.h"
8 #include "src/core/SkTSort.h"
9 #include "src/pathops/SkLineParameters.h"
10 #include "src/pathops/SkPathOpsConic.h"
11 #include "src/pathops/SkPathOpsCubic.h"
12 #include "src/pathops/SkPathOpsCurve.h"
13 #include "src/pathops/SkPathOpsLine.h"
14 #include "src/pathops/SkPathOpsQuad.h"
15 #include "src/pathops/SkPathOpsRect.h"
16
17 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in test framework
18
align(int endIndex,int ctrlIndex,SkDPoint * dstPt) const19 void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
20 if (fPts[endIndex].fX == fPts[ctrlIndex].fX) {
21 dstPt->fX = fPts[endIndex].fX;
22 }
23 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
24 dstPt->fY = fPts[endIndex].fY;
25 }
26 }
27
28 // give up when changing t no longer moves point
29 // also, copy point rather than recompute it when it does change
binarySearch(double min,double max,double axisIntercept,SearchAxis xAxis) const30 double SkDCubic::binarySearch(double min, double max, double axisIntercept,
31 SearchAxis xAxis) const {
32 double t = (min + max) / 2;
33 double step = (t - min) / 2;
34 SkDPoint cubicAtT = ptAtT(t);
35 double calcPos = (&cubicAtT.fX)[xAxis];
36 double calcDist = calcPos - axisIntercept;
37 do {
38 double priorT = std::max(min, t - step);
39 SkDPoint lessPt = ptAtT(priorT);
40 if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
41 && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
42 return -1; // binary search found no point at this axis intercept
43 }
44 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
45 #if DEBUG_CUBIC_BINARY_SEARCH
46 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, calcPos, calcDist,
47 step, lessDist);
48 #endif
49 double lastStep = step;
50 step /= 2;
51 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) {
52 t = priorT;
53 } else {
54 double nextT = t + lastStep;
55 if (nextT > max) {
56 return -1;
57 }
58 SkDPoint morePt = ptAtT(nextT);
59 if (approximately_equal_half(morePt.fX, cubicAtT.fX)
60 && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
61 return -1; // binary search found no point at this axis intercept
62 }
63 double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
64 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) {
65 continue;
66 }
67 t = nextT;
68 }
69 SkDPoint testAtT = ptAtT(t);
70 cubicAtT = testAtT;
71 calcPos = (&cubicAtT.fX)[xAxis];
72 calcDist = calcPos - axisIntercept;
73 } while (!approximately_equal(calcPos, axisIntercept));
74 return t;
75 }
76
77 // get the rough scale of the cubic; used to determine if curvature is extreme
calcPrecision() const78 double SkDCubic::calcPrecision() const {
79 return ((fPts[1] - fPts[0]).length()
80 + (fPts[2] - fPts[1]).length()
81 + (fPts[3] - fPts[2]).length()) / gPrecisionUnit;
82 }
83
84 /* classic one t subdivision */
interp_cubic_coords(const double * src,double * dst,double t)85 static void interp_cubic_coords(const double* src, double* dst, double t) {
86 double ab = SkDInterp(src[0], src[2], t);
87 double bc = SkDInterp(src[2], src[4], t);
88 double cd = SkDInterp(src[4], src[6], t);
89 double abc = SkDInterp(ab, bc, t);
90 double bcd = SkDInterp(bc, cd, t);
91 double abcd = SkDInterp(abc, bcd, t);
92
93 dst[0] = src[0];
94 dst[2] = ab;
95 dst[4] = abc;
96 dst[6] = abcd;
97 dst[8] = bcd;
98 dst[10] = cd;
99 dst[12] = src[6];
100 }
101
chopAt(double t) const102 SkDCubicPair SkDCubic::chopAt(double t) const {
103 SkDCubicPair dst;
104 if (t == 0.5) {
105 dst.pts[0] = fPts[0];
106 dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2;
107 dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2;
108 dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4;
109 dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4;
110 dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8;
111 dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8;
112 dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4;
113 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
114 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
115 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
116 dst.pts[6] = fPts[3];
117 return dst;
118 }
119 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
120 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
121 return dst;
122 }
123
Coefficients(const double * src,double * A,double * B,double * C,double * D)124 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) {
125 *A = src[6]; // d
126 *B = src[4] * 3; // 3*c
127 *C = src[2] * 3; // 3*b
128 *D = src[0]; // a
129 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d
130 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c
131 *C -= 3 * *D; // C = -3*a + 3*b
132 }
133
endsAreExtremaInXOrY() const134 bool SkDCubic::endsAreExtremaInXOrY() const {
135 return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
136 && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
137 || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
138 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
139 }
140
141 // Do a quick reject by rotating all points relative to a line formed by
142 // a pair of one cubic's points. If the 2nd cubic's points
143 // are on the line or on the opposite side from the 1st cubic's 'odd man', the
144 // curves at most intersect at the endpoints.
145 /* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
146 if returning false, check contains true if the the cubic pair have only the end point in common
147 */
hullIntersects(const SkDPoint * pts,int ptCount,bool * isLinear) const148 bool SkDCubic::hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const {
149 bool linear = true;
150 char hullOrder[4];
151 int hullCount = convexHull(hullOrder);
152 int end1 = hullOrder[0];
153 int hullIndex = 0;
154 const SkDPoint* endPt[2];
155 endPt[0] = &fPts[end1];
156 do {
157 hullIndex = (hullIndex + 1) % hullCount;
158 int end2 = hullOrder[hullIndex];
159 endPt[1] = &fPts[end2];
160 double origX = endPt[0]->fX;
161 double origY = endPt[0]->fY;
162 double adj = endPt[1]->fX - origX;
163 double opp = endPt[1]->fY - origY;
164 int oddManMask = other_two(end1, end2);
165 int oddMan = end1 ^ oddManMask;
166 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
167 int oddMan2 = end2 ^ oddManMask;
168 double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
169 if (sign * sign2 < 0) {
170 continue;
171 }
172 if (approximately_zero(sign)) {
173 sign = sign2;
174 if (approximately_zero(sign)) {
175 continue;
176 }
177 }
178 linear = false;
179 bool foundOutlier = false;
180 for (int n = 0; n < ptCount; ++n) {
181 double test = (pts[n].fY - origY) * adj - (pts[n].fX - origX) * opp;
182 if (test * sign > 0 && !precisely_zero(test)) {
183 foundOutlier = true;
184 break;
185 }
186 }
187 if (!foundOutlier) {
188 return false;
189 }
190 endPt[0] = endPt[1];
191 end1 = end2;
192 } while (hullIndex);
193 *isLinear = linear;
194 return true;
195 }
196
hullIntersects(const SkDCubic & c2,bool * isLinear) const197 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
198 return hullIntersects(c2.fPts, c2.kPointCount, isLinear);
199 }
200
hullIntersects(const SkDQuad & quad,bool * isLinear) const201 bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
202 return hullIntersects(quad.fPts, quad.kPointCount, isLinear);
203 }
204
hullIntersects(const SkDConic & conic,bool * isLinear) const205 bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
206
207 return hullIntersects(conic.fPts, isLinear);
208 }
209
isLinear(int startIndex,int endIndex) const210 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
211 if (fPts[0].approximatelyDEqual(fPts[3])) {
212 return ((const SkDQuad *) this)->isLinear(0, 2);
213 }
214 SkLineParameters lineParameters;
215 lineParameters.cubicEndPoints(*this, startIndex, endIndex);
216 // FIXME: maybe it's possible to avoid this and compare non-normalized
217 lineParameters.normalize();
218 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
219 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
220 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
221 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
222 largest = SkTMax(largest, -tiniest);
223 double distance = lineParameters.controlPtDistance(*this, 1);
224 if (!approximately_zero_when_compared_to(distance, largest)) {
225 return false;
226 }
227 distance = lineParameters.controlPtDistance(*this, 2);
228 return approximately_zero_when_compared_to(distance, largest);
229 }
230
231 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
232 // c(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
233 // c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
234 // = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
derivative_at_t(const double * src,double t)235 static double derivative_at_t(const double* src, double t) {
236 double one_t = 1 - t;
237 double a = src[0];
238 double b = src[2];
239 double c = src[4];
240 double d = src[6];
241 return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
242 }
243
ComplexBreak(const SkPoint pointsPtr[4],SkScalar * t)244 int SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
245 SkDCubic cubic;
246 cubic.set(pointsPtr);
247 if (cubic.monotonicInX() && cubic.monotonicInY()) {
248 return 0;
249 }
250 double tt[2], ss[2];
251 SkCubicType cubicType = SkClassifyCubic(pointsPtr, tt, ss);
252 switch (cubicType) {
253 case SkCubicType::kLoop: {
254 const double &td = tt[0], &te = tt[1], &sd = ss[0], &se = ss[1];
255 if (roughly_between(0, td, sd) && roughly_between(0, te, se)) {
256 t[0] = static_cast<SkScalar>((td * se + te * sd) / (2 * sd * se));
257 return (int) (t[0] > 0 && t[0] < 1);
258 }
259 }
260 // fall through if no t value found
261 case SkCubicType::kSerpentine:
262 case SkCubicType::kLocalCusp:
263 case SkCubicType::kCuspAtInfinity: {
264 double inflectionTs[2];
265 int infTCount = cubic.findInflections(inflectionTs);
266 double maxCurvature[3];
267 int roots = cubic.findMaxCurvature(maxCurvature);
268 #if DEBUG_CUBIC_SPLIT
269 SkDebugf("%s\n", __FUNCTION__);
270 cubic.dump();
271 for (int index = 0; index < infTCount; ++index) {
272 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
273 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
274 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
275 SkDLine perp = {{pt - dPt, pt + dPt}};
276 perp.dump();
277 }
278 for (int index = 0; index < roots; ++index) {
279 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
280 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
281 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
282 SkDLine perp = {{pt - dPt, pt + dPt}};
283 perp.dump();
284 }
285 #endif
286 if (infTCount == 2) {
287 for (int index = 0; index < roots; ++index) {
288 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
289 t[0] = maxCurvature[index];
290 return (int) (t[0] > 0 && t[0] < 1);
291 }
292 }
293 } else {
294 int resultCount = 0;
295 // FIXME: constant found through experimentation -- maybe there's a better way....
296 double precision = cubic.calcPrecision() * 2;
297 for (int index = 0; index < roots; ++index) {
298 double testT = maxCurvature[index];
299 if (0 >= testT || testT >= 1) {
300 continue;
301 }
302 // don't call dxdyAtT since we want (0,0) results
303 SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
304 derivative_at_t(&cubic.fPts[0].fY, testT) };
305 double dPtLen = dPt.length();
306 if (dPtLen < precision) {
307 t[resultCount++] = testT;
308 }
309 }
310 if (!resultCount && infTCount == 1) {
311 t[0] = inflectionTs[0];
312 resultCount = (int) (t[0] > 0 && t[0] < 1);
313 }
314 return resultCount;
315 }
316 }
317 default:
318 ;
319 }
320 return 0;
321 }
322
monotonicInX() const323 bool SkDCubic::monotonicInX() const {
324 return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
325 && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
326 }
327
monotonicInY() const328 bool SkDCubic::monotonicInY() const {
329 return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
330 && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
331 }
332
otherPts(int index,const SkDPoint * o1Pts[kPointCount-1]) const333 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
334 int offset = (int) !SkToBool(index);
335 o1Pts[0] = &fPts[offset];
336 o1Pts[1] = &fPts[++offset];
337 o1Pts[2] = &fPts[++offset];
338 }
339
searchRoots(double extremeTs[6],int extrema,double axisIntercept,SearchAxis xAxis,double * validRoots) const340 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
341 SearchAxis xAxis, double* validRoots) const {
342 extrema += findInflections(&extremeTs[extrema]);
343 extremeTs[extrema++] = 0;
344 extremeTs[extrema] = 1;
345 SkASSERT(extrema < 6);
346 SkTQSort(extremeTs, extremeTs + extrema);
347 int validCount = 0;
348 for (int index = 0; index < extrema; ) {
349 double min = extremeTs[index];
350 double max = extremeTs[++index];
351 if (min == max) {
352 continue;
353 }
354 double newT = binarySearch(min, max, axisIntercept, xAxis);
355 if (newT >= 0) {
356 if (validCount >= 3) {
357 return 0;
358 }
359 validRoots[validCount++] = newT;
360 }
361 }
362 return validCount;
363 }
364
365 // cubic roots
366
367 static const double PI = 3.141592653589793;
368
369 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
RootsValidT(double A,double B,double C,double D,double t[3])370 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
371 double s[3];
372 int realRoots = RootsReal(A, B, C, D, s);
373 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
374 for (int index = 0; index < realRoots; ++index) {
375 double tValue = s[index];
376 if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
377 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
378 if (approximately_equal(t[idx2], 1)) {
379 goto nextRoot;
380 }
381 }
382 SkASSERT(foundRoots < 3);
383 t[foundRoots++] = 1;
384 } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
385 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
386 if (approximately_equal(t[idx2], 0)) {
387 goto nextRoot;
388 }
389 }
390 SkASSERT(foundRoots < 3);
391 t[foundRoots++] = 0;
392 }
393 nextRoot:
394 ;
395 }
396 return foundRoots;
397 }
398
RootsReal(double A,double B,double C,double D,double s[3])399 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
400 #ifdef SK_DEBUG
401 // create a string mathematica understands
402 // GDB set print repe 15 # if repeated digits is a bother
403 // set print elements 400 # if line doesn't fit
404 char str[1024];
405 sk_bzero(str, sizeof(str));
406 SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
407 A, B, C, D);
408 SkPathOpsDebug::MathematicaIze(str, sizeof(str));
409 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
410 SkDebugf("%s\n", str);
411 #endif
412 #endif
413 if (approximately_zero(A)
414 && approximately_zero_when_compared_to(A, B)
415 && approximately_zero_when_compared_to(A, C)
416 && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic
417 return SkDQuad::RootsReal(B, C, D, s);
418 }
419 if (approximately_zero_when_compared_to(D, A)
420 && approximately_zero_when_compared_to(D, B)
421 && approximately_zero_when_compared_to(D, C)) { // 0 is one root
422 int num = SkDQuad::RootsReal(A, B, C, s);
423 for (int i = 0; i < num; ++i) {
424 if (approximately_zero(s[i])) {
425 return num;
426 }
427 }
428 s[num++] = 0;
429 return num;
430 }
431 if (approximately_zero(A + B + C + D)) { // 1 is one root
432 int num = SkDQuad::RootsReal(A, A + B, -D, s);
433 for (int i = 0; i < num; ++i) {
434 if (AlmostDequalUlps(s[i], 1)) {
435 return num;
436 }
437 }
438 s[num++] = 1;
439 return num;
440 }
441 double a, b, c;
442 {
443 double invA = 1 / A;
444 a = B * invA;
445 b = C * invA;
446 c = D * invA;
447 }
448 double a2 = a * a;
449 double Q = (a2 - b * 3) / 9;
450 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
451 double R2 = R * R;
452 double Q3 = Q * Q * Q;
453 double R2MinusQ3 = R2 - Q3;
454 double adiv3 = a / 3;
455 double r;
456 double* roots = s;
457 if (R2MinusQ3 < 0) { // we have 3 real roots
458 // the divide/root can, due to finite precisions, be slightly outside of -1...1
459 double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.));
460 double neg2RootQ = -2 * sqrt(Q);
461
462 r = neg2RootQ * cos(theta / 3) - adiv3;
463 *roots++ = r;
464
465 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
466 if (!AlmostDequalUlps(s[0], r)) {
467 *roots++ = r;
468 }
469 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
470 if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
471 *roots++ = r;
472 }
473 } else { // we have 1 real root
474 double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
475 double A = fabs(R) + sqrtR2MinusQ3;
476 A = SkDCubeRoot(A);
477 if (R > 0) {
478 A = -A;
479 }
480 if (A != 0) {
481 A += Q / A;
482 }
483 r = A - adiv3;
484 *roots++ = r;
485 if (AlmostDequalUlps((double) R2, (double) Q3)) {
486 r = -A / 2 - adiv3;
487 if (!AlmostDequalUlps(s[0], r)) {
488 *roots++ = r;
489 }
490 }
491 }
492 return static_cast<int>(roots - s);
493 }
494
495 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const496 SkDVector SkDCubic::dxdyAtT(double t) const {
497 SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
498 if (result.fX == 0 && result.fY == 0) {
499 if (t == 0) {
500 result = fPts[2] - fPts[0];
501 } else if (t == 1) {
502 result = fPts[3] - fPts[1];
503 } else {
504 // incomplete
505 SkDebugf("!c");
506 }
507 if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
508 result = fPts[3] - fPts[0];
509 }
510 }
511 return result;
512 }
513
514 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const515 int SkDCubic::findInflections(double tValues[]) const {
516 double Ax = fPts[1].fX - fPts[0].fX;
517 double Ay = fPts[1].fY - fPts[0].fY;
518 double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
519 double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
520 double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
521 double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
522 return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
523 }
524
formulate_F1DotF2(const double src[],double coeff[4])525 static void formulate_F1DotF2(const double src[], double coeff[4]) {
526 double a = src[2] - src[0];
527 double b = src[4] - 2 * src[2] + src[0];
528 double c = src[6] + 3 * (src[2] - src[4]) - src[0];
529 coeff[0] = c * c;
530 coeff[1] = 3 * b * c;
531 coeff[2] = 2 * b * b + c * a;
532 coeff[3] = a * b;
533 }
534
535 /** SkDCubic'(t) = At^2 + Bt + C, where
536 A = 3(-a + 3(b - c) + d)
537 B = 6(a - 2b + c)
538 C = 3(b - a)
539 Solve for t, keeping only those that fit between 0 < t < 1
540 */
FindExtrema(const double src[],double tValues[2])541 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
542 // we divide A,B,C by 3 to simplify
543 double a = src[0];
544 double b = src[2];
545 double c = src[4];
546 double d = src[6];
547 double A = d - a + 3 * (b - c);
548 double B = 2 * (a - b - b + c);
549 double C = b - a;
550
551 return SkDQuad::RootsValidT(A, B, C, tValues);
552 }
553
554 /* from SkGeometry.cpp
555 Looking for F' dot F'' == 0
556
557 A = b - a
558 B = c - 2b + a
559 C = d - 3c + 3b - a
560
561 F' = 3Ct^2 + 6Bt + 3A
562 F'' = 6Ct + 6B
563
564 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
565 */
findMaxCurvature(double tValues[]) const566 int SkDCubic::findMaxCurvature(double tValues[]) const {
567 double coeffX[4], coeffY[4];
568 int i;
569 formulate_F1DotF2(&fPts[0].fX, coeffX);
570 formulate_F1DotF2(&fPts[0].fY, coeffY);
571 for (i = 0; i < 4; i++) {
572 coeffX[i] = coeffX[i] + coeffY[i];
573 }
574 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
575 }
576
ptAtT(double t) const577 SkDPoint SkDCubic::ptAtT(double t) const {
578 if (0 == t) {
579 return fPts[0];
580 }
581 if (1 == t) {
582 return fPts[3];
583 }
584 double one_t = 1 - t;
585 double one_t2 = one_t * one_t;
586 double a = one_t2 * one_t;
587 double b = 3 * one_t2 * t;
588 double t2 = t * t;
589 double c = 3 * one_t * t2;
590 double d = t2 * t;
591 SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
592 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
593 return result;
594 }
595
596 /*
597 Given a cubic c, t1, and t2, find a small cubic segment.
598
599 The new cubic is defined as points A, B, C, and D, where
600 s1 = 1 - t1
601 s2 = 1 - t2
602 A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
603 D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
604
605 We don't have B or C. So We define two equations to isolate them.
606 First, compute two reference T values 1/3 and 2/3 from t1 to t2:
607
608 c(at (2*t1 + t2)/3) == E
609 c(at (t1 + 2*t2)/3) == F
610
611 Next, compute where those values must be if we know the values of B and C:
612
613 _12 = A*2/3 + B*1/3
614 12_ = A*1/3 + B*2/3
615 _23 = B*2/3 + C*1/3
616 23_ = B*1/3 + C*2/3
617 _34 = C*2/3 + D*1/3
618 34_ = C*1/3 + D*2/3
619 _123 = (A*2/3 + B*1/3)*2/3 + (B*2/3 + C*1/3)*1/3 = A*4/9 + B*4/9 + C*1/9
620 123_ = (A*1/3 + B*2/3)*1/3 + (B*1/3 + C*2/3)*2/3 = A*1/9 + B*4/9 + C*4/9
621 _234 = (B*2/3 + C*1/3)*2/3 + (C*2/3 + D*1/3)*1/3 = B*4/9 + C*4/9 + D*1/9
622 234_ = (B*1/3 + C*2/3)*1/3 + (C*1/3 + D*2/3)*2/3 = B*1/9 + C*4/9 + D*4/9
623 _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
624 = A*8/27 + B*12/27 + C*6/27 + D*1/27
625 = E
626 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
627 = A*1/27 + B*6/27 + C*12/27 + D*8/27
628 = F
629 E*27 = A*8 + B*12 + C*6 + D
630 F*27 = A + B*6 + C*12 + D*8
631
632 Group the known values on one side:
633
634 M = E*27 - A*8 - D = B*12 + C* 6
635 N = F*27 - A - D*8 = B* 6 + C*12
636 M*2 - N = B*18
637 N*2 - M = C*18
638 B = (M*2 - N)/18
639 C = (N*2 - M)/18
640 */
641
interp_cubic_coords(const double * src,double t)642 static double interp_cubic_coords(const double* src, double t) {
643 double ab = SkDInterp(src[0], src[2], t);
644 double bc = SkDInterp(src[2], src[4], t);
645 double cd = SkDInterp(src[4], src[6], t);
646 double abc = SkDInterp(ab, bc, t);
647 double bcd = SkDInterp(bc, cd, t);
648 double abcd = SkDInterp(abc, bcd, t);
649 return abcd;
650 }
651
subDivide(double t1,double t2) const652 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
653 if (t1 == 0 || t2 == 1) {
654 if (t1 == 0 && t2 == 1) {
655 return *this;
656 }
657 SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
658 SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
659 return dst;
660 }
661 SkDCubic dst;
662 double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
663 double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
664 double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
665 double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
666 double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
667 double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
668 double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
669 double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
670 double mx = ex * 27 - ax * 8 - dx;
671 double my = ey * 27 - ay * 8 - dy;
672 double nx = fx * 27 - ax - dx * 8;
673 double ny = fy * 27 - ay - dy * 8;
674 /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
675 /* by = */ dst[1].fY = (my * 2 - ny) / 18;
676 /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
677 /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
678 // FIXME: call align() ?
679 return dst;
680 }
681
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const682 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
683 double t1, double t2, SkDPoint dst[2]) const {
684 SkASSERT(t1 != t2);
685 // this approach assumes that the control points computed directly are accurate enough
686 SkDCubic sub = subDivide(t1, t2);
687 dst[0] = sub[1] + (a - sub[0]);
688 dst[1] = sub[2] + (d - sub[3]);
689 if (t1 == 0 || t2 == 0) {
690 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
691 }
692 if (t1 == 1 || t2 == 1) {
693 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
694 }
695 if (AlmostBequalUlps(dst[0].fX, a.fX)) {
696 dst[0].fX = a.fX;
697 }
698 if (AlmostBequalUlps(dst[0].fY, a.fY)) {
699 dst[0].fY = a.fY;
700 }
701 if (AlmostBequalUlps(dst[1].fX, d.fX)) {
702 dst[1].fX = d.fX;
703 }
704 if (AlmostBequalUlps(dst[1].fY, d.fY)) {
705 dst[1].fY = d.fY;
706 }
707 }
708
toFloatPoints(SkPoint * pts) const709 bool SkDCubic::toFloatPoints(SkPoint* pts) const {
710 const double* dCubic = &fPts[0].fX;
711 SkScalar* cubic = &pts[0].fX;
712 for (int index = 0; index < kPointCount * 2; ++index) {
713 cubic[index] = SkDoubleToScalar(dCubic[index]);
714 if (SkScalarAbs(cubic[index]) < FLT_EPSILON_ORDERABLE_ERR) {
715 cubic[index] = 0;
716 }
717 }
718 return SkScalarsAreFinite(&pts->fX, kPointCount * 2);
719 }
720
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const721 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
722 double extremeTs[2];
723 double topT = -1;
724 int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
725 for (int index = 0; index < roots; ++index) {
726 double t = startT + (endT - startT) * extremeTs[index];
727 SkDPoint mid = dCurve.ptAtT(t);
728 if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
729 topT = t;
730 *topPt = mid;
731 }
732 }
733 return topT;
734 }
735
intersectRay(SkIntersections * i,const SkDLine & line) const736 int SkTCubic::intersectRay(SkIntersections* i, const SkDLine& line) const {
737 return i->intersectRay(fCubic, line);
738 }
739
hullIntersects(const SkDQuad & quad,bool * isLinear) const740 bool SkTCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
741 return quad.hullIntersects(fCubic, isLinear);
742 }
743
hullIntersects(const SkDConic & conic,bool * isLinear) const744 bool SkTCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
745 return conic.hullIntersects(fCubic, isLinear);
746 }
747
setBounds(SkDRect * rect) const748 void SkTCubic::setBounds(SkDRect* rect) const {
749 rect->setBounds(fCubic);
750 }
751