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