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