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 "SkGeometry.h"
8 #include "SkLineParameters.h"
9 #include "SkPathOpsConic.h"
10 #include "SkPathOpsCubic.h"
11 #include "SkPathOpsCurve.h"
12 #include "SkPathOpsLine.h"
13 #include "SkPathOpsQuad.h"
14 #include "SkPathOpsRect.h"
15 #include "SkTSort.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 = t - step;
39         SkASSERT(priorT >= min);
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 // FIXME: cache keep the bounds and/or precision with the caller?
calcPrecision() const79 double SkDCubic::calcPrecision() const {
80     SkDRect dRect;
81     dRect.setBounds(*this);  // OPTIMIZATION: just use setRawBounds ?
82     double width = dRect.fRight - dRect.fLeft;
83     double height = dRect.fBottom - dRect.fTop;
84     return (width > height ? width : height) / gPrecisionUnit;
85 }
86 
87 
88 /* classic one t subdivision */
interp_cubic_coords(const double * src,double * dst,double t)89 static void interp_cubic_coords(const double* src, double* dst, double t) {
90     double ab = SkDInterp(src[0], src[2], t);
91     double bc = SkDInterp(src[2], src[4], t);
92     double cd = SkDInterp(src[4], src[6], t);
93     double abc = SkDInterp(ab, bc, t);
94     double bcd = SkDInterp(bc, cd, t);
95     double abcd = SkDInterp(abc, bcd, t);
96 
97     dst[0] = src[0];
98     dst[2] = ab;
99     dst[4] = abc;
100     dst[6] = abcd;
101     dst[8] = bcd;
102     dst[10] = cd;
103     dst[12] = src[6];
104 }
105 
chopAt(double t) const106 SkDCubicPair SkDCubic::chopAt(double t) const {
107     SkDCubicPair dst;
108     if (t == 0.5) {
109         dst.pts[0] = fPts[0];
110         dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2;
111         dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2;
112         dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4;
113         dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4;
114         dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8;
115         dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8;
116         dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4;
117         dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
118         dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
119         dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
120         dst.pts[6] = fPts[3];
121         return dst;
122     }
123     interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
124     interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
125     return dst;
126 }
127 
Coefficients(const double * src,double * A,double * B,double * C,double * D)128 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) {
129     *A = src[6];  // d
130     *B = src[4] * 3;  // 3*c
131     *C = src[2] * 3;  // 3*b
132     *D = src[0];  // a
133     *A -= *D - *C + *B;     // A =   -a + 3*b - 3*c + d
134     *B += 3 * *D - 2 * *C;  // B =  3*a - 6*b + 3*c
135     *C -= 3 * *D;           // C = -3*a + 3*b
136 }
137 
endsAreExtremaInXOrY() const138 bool SkDCubic::endsAreExtremaInXOrY() const {
139     return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
140             && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
141             || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
142             && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
143 }
144 
145 // Do a quick reject by rotating all points relative to a line formed by
146 // a pair of one cubic's points. If the 2nd cubic's points
147 // are on the line or on the opposite side from the 1st cubic's 'odd man', the
148 // curves at most intersect at the endpoints.
149 /* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
150    if returning false, check contains true if the the cubic pair have only the end point in common
151 */
hullIntersects(const SkDPoint * pts,int ptCount,bool * isLinear) const152 bool SkDCubic::hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const {
153     bool linear = true;
154     char hullOrder[4];
155     int hullCount = convexHull(hullOrder);
156     int end1 = hullOrder[0];
157     int hullIndex = 0;
158     const SkDPoint* endPt[2];
159     endPt[0] = &fPts[end1];
160     do {
161         hullIndex = (hullIndex + 1) % hullCount;
162         int end2 = hullOrder[hullIndex];
163         endPt[1] = &fPts[end2];
164         double origX = endPt[0]->fX;
165         double origY = endPt[0]->fY;
166         double adj = endPt[1]->fX - origX;
167         double opp = endPt[1]->fY - origY;
168         int oddManMask = other_two(end1, end2);
169         int oddMan = end1 ^ oddManMask;
170         double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
171         int oddMan2 = end2 ^ oddManMask;
172         double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
173         if (sign * sign2 < 0) {
174             continue;
175         }
176         if (approximately_zero(sign)) {
177             sign = sign2;
178             if (approximately_zero(sign)) {
179                 continue;
180             }
181         }
182         linear = false;
183         bool foundOutlier = false;
184         for (int n = 0; n < ptCount; ++n) {
185             double test = (pts[n].fY - origY) * adj - (pts[n].fX - origX) * opp;
186             if (test * sign > 0 && !precisely_zero(test)) {
187                 foundOutlier = true;
188                 break;
189             }
190         }
191         if (!foundOutlier) {
192             return false;
193         }
194         endPt[0] = endPt[1];
195         end1 = end2;
196     } while (hullIndex);
197     *isLinear = linear;
198     return true;
199 }
200 
hullIntersects(const SkDCubic & c2,bool * isLinear) const201 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
202     return hullIntersects(c2.fPts, c2.kPointCount, isLinear);
203 }
204 
hullIntersects(const SkDQuad & quad,bool * isLinear) const205 bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
206     return hullIntersects(quad.fPts, quad.kPointCount, isLinear);
207 }
208 
hullIntersects(const SkDConic & conic,bool * isLinear) const209 bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
210 
211     return hullIntersects(conic.fPts, isLinear);
212 }
213 
isLinear(int startIndex,int endIndex) const214 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
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 = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(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 = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(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 = SkTMax(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 
ComplexBreak(const SkPoint pointsPtr[4],SkScalar * t,CubicType * resultType)232 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t, CubicType* resultType) {
233     SkScalar d[3];
234     SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
235     if (cubicType == kLoop_SkCubicType) {
236         // crib code from gpu path utils that finds t values where loop self-intersects
237         // use it to find mid of t values which should be a friendly place to chop
238         SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
239         SkScalar ls = d[1] - tempSqrt;
240         SkScalar lt = 2.f * d[0];
241         SkScalar ms = d[1] + tempSqrt;
242         SkScalar mt = 2.f * d[0];
243         if (between(0, ls, lt) || between(0, ms, mt)) {
244             ls = ls / lt;
245             ms = ms / mt;
246             SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
247             SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
248             *t = (smaller + larger) / 2;
249             *resultType = kSplitAtLoop_SkDCubicType;
250             return *t > 0 && *t < 1;
251         }
252     } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) {
253         SkDCubic cubic;
254         cubic.set(pointsPtr);
255         double inflectionTs[2];
256         int infTCount = cubic.findInflections(inflectionTs);
257         if (infTCount == 2) {
258             double maxCurvature[3];
259             int roots = cubic.findMaxCurvature(maxCurvature);
260 #if DEBUG_CUBIC_SPLIT
261             SkDebugf("%s\n", __FUNCTION__);
262             cubic.dump();
263             for (int index = 0; index < infTCount; ++index) {
264                 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
265                 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
266                 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
267                 SkDLine perp = {{pt - dPt, pt + dPt}};
268                 perp.dump();
269             }
270             for (int index = 0; index < roots; ++index) {
271                 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
272                 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
273                 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
274                 SkDLine perp = {{pt - dPt, pt + dPt}};
275                 perp.dump();
276             }
277 #endif
278             for (int index = 0; index < roots; ++index) {
279                 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
280                     *t = maxCurvature[index];
281                     *resultType = kSplitAtMaxCurvature_SkDCubicType;
282                     return true;
283                 }
284             }
285         } else if (infTCount == 1) {
286             *t = inflectionTs[0];
287             *resultType = kSplitAtInflection_SkDCubicType;
288             return *t > 0 && *t < 1;
289         }
290     }
291     return false;
292 }
293 
monotonicInX() const294 bool SkDCubic::monotonicInX() const {
295     return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
296             && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
297 }
298 
monotonicInY() const299 bool SkDCubic::monotonicInY() const {
300     return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
301             && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
302 }
303 
otherPts(int index,const SkDPoint * o1Pts[kPointCount-1]) const304 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
305     int offset = (int) !SkToBool(index);
306     o1Pts[0] = &fPts[offset];
307     o1Pts[1] = &fPts[++offset];
308     o1Pts[2] = &fPts[++offset];
309 }
310 
searchRoots(double extremeTs[6],int extrema,double axisIntercept,SearchAxis xAxis,double * validRoots) const311 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
312         SearchAxis xAxis, double* validRoots) const {
313     extrema += findInflections(&extremeTs[extrema]);
314     extremeTs[extrema++] = 0;
315     extremeTs[extrema] = 1;
316     SkTQSort(extremeTs, extremeTs + extrema);
317     int validCount = 0;
318     for (int index = 0; index < extrema; ) {
319         double min = extremeTs[index];
320         double max = extremeTs[++index];
321         if (min == max) {
322             continue;
323         }
324         double newT = binarySearch(min, max, axisIntercept, xAxis);
325         if (newT >= 0) {
326             validRoots[validCount++] = newT;
327         }
328     }
329     return validCount;
330 }
331 
332 // cubic roots
333 
334 static const double PI = 3.141592653589793;
335 
336 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
RootsValidT(double A,double B,double C,double D,double t[3])337 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
338     double s[3];
339     int realRoots = RootsReal(A, B, C, D, s);
340     int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
341     for (int index = 0; index < realRoots; ++index) {
342         double tValue = s[index];
343         if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
344             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
345                 if (approximately_equal(t[idx2], 1)) {
346                     goto nextRoot;
347                 }
348             }
349             SkASSERT(foundRoots < 3);
350             t[foundRoots++] = 1;
351         } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
352             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
353                 if (approximately_equal(t[idx2], 0)) {
354                     goto nextRoot;
355                 }
356             }
357             SkASSERT(foundRoots < 3);
358             t[foundRoots++] = 0;
359         }
360 nextRoot:
361         ;
362     }
363     return foundRoots;
364 }
365 
RootsReal(double A,double B,double C,double D,double s[3])366 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
367 #ifdef SK_DEBUG
368     // create a string mathematica understands
369     // GDB set print repe 15 # if repeated digits is a bother
370     //     set print elements 400 # if line doesn't fit
371     char str[1024];
372     sk_bzero(str, sizeof(str));
373     SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
374             A, B, C, D);
375     SkPathOpsDebug::MathematicaIze(str, sizeof(str));
376 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
377     SkDebugf("%s\n", str);
378 #endif
379 #endif
380     if (approximately_zero(A)
381             && approximately_zero_when_compared_to(A, B)
382             && approximately_zero_when_compared_to(A, C)
383             && approximately_zero_when_compared_to(A, D)) {  // we're just a quadratic
384         return SkDQuad::RootsReal(B, C, D, s);
385     }
386     if (approximately_zero_when_compared_to(D, A)
387             && approximately_zero_when_compared_to(D, B)
388             && approximately_zero_when_compared_to(D, C)) {  // 0 is one root
389         int num = SkDQuad::RootsReal(A, B, C, s);
390         for (int i = 0; i < num; ++i) {
391             if (approximately_zero(s[i])) {
392                 return num;
393             }
394         }
395         s[num++] = 0;
396         return num;
397     }
398     if (approximately_zero(A + B + C + D)) {  // 1 is one root
399         int num = SkDQuad::RootsReal(A, A + B, -D, s);
400         for (int i = 0; i < num; ++i) {
401             if (AlmostDequalUlps(s[i], 1)) {
402                 return num;
403             }
404         }
405         s[num++] = 1;
406         return num;
407     }
408     double a, b, c;
409     {
410         double invA = 1 / A;
411         a = B * invA;
412         b = C * invA;
413         c = D * invA;
414     }
415     double a2 = a * a;
416     double Q = (a2 - b * 3) / 9;
417     double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
418     double R2 = R * R;
419     double Q3 = Q * Q * Q;
420     double R2MinusQ3 = R2 - Q3;
421     double adiv3 = a / 3;
422     double r;
423     double* roots = s;
424     if (R2MinusQ3 < 0) {   // we have 3 real roots
425         double theta = acos(R / sqrt(Q3));
426         double neg2RootQ = -2 * sqrt(Q);
427 
428         r = neg2RootQ * cos(theta / 3) - adiv3;
429         *roots++ = r;
430 
431         r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
432         if (!AlmostDequalUlps(s[0], r)) {
433             *roots++ = r;
434         }
435         r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
436         if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
437             *roots++ = r;
438         }
439     } else {  // we have 1 real root
440         double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
441         double A = fabs(R) + sqrtR2MinusQ3;
442         A = SkDCubeRoot(A);
443         if (R > 0) {
444             A = -A;
445         }
446         if (A != 0) {
447             A += Q / A;
448         }
449         r = A - adiv3;
450         *roots++ = r;
451         if (AlmostDequalUlps((double) R2, (double) Q3)) {
452             r = -A / 2 - adiv3;
453             if (!AlmostDequalUlps(s[0], r)) {
454                 *roots++ = r;
455             }
456         }
457     }
458     return static_cast<int>(roots - s);
459 }
460 
461 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
462 // c(t)  = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
463 // c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
464 //       = 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)465 static double derivative_at_t(const double* src, double t) {
466     double one_t = 1 - t;
467     double a = src[0];
468     double b = src[2];
469     double c = src[4];
470     double d = src[6];
471     return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
472 }
473 
474 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const475 SkDVector SkDCubic::dxdyAtT(double t) const {
476     SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
477     return result;
478 }
479 
480 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const481 int SkDCubic::findInflections(double tValues[]) const {
482     double Ax = fPts[1].fX - fPts[0].fX;
483     double Ay = fPts[1].fY - fPts[0].fY;
484     double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
485     double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
486     double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
487     double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
488     return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
489 }
490 
formulate_F1DotF2(const double src[],double coeff[4])491 static void formulate_F1DotF2(const double src[], double coeff[4]) {
492     double a = src[2] - src[0];
493     double b = src[4] - 2 * src[2] + src[0];
494     double c = src[6] + 3 * (src[2] - src[4]) - src[0];
495     coeff[0] = c * c;
496     coeff[1] = 3 * b * c;
497     coeff[2] = 2 * b * b + c * a;
498     coeff[3] = a * b;
499 }
500 
501 /** SkDCubic'(t) = At^2 + Bt + C, where
502     A = 3(-a + 3(b - c) + d)
503     B = 6(a - 2b + c)
504     C = 3(b - a)
505     Solve for t, keeping only those that fit between 0 < t < 1
506 */
FindExtrema(const double src[],double tValues[2])507 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
508     // we divide A,B,C by 3 to simplify
509     double a = src[0];
510     double b = src[2];
511     double c = src[4];
512     double d = src[6];
513     double A = d - a + 3 * (b - c);
514     double B = 2 * (a - b - b + c);
515     double C = b - a;
516 
517     return SkDQuad::RootsValidT(A, B, C, tValues);
518 }
519 
520 /*  from SkGeometry.cpp
521     Looking for F' dot F'' == 0
522 
523     A = b - a
524     B = c - 2b + a
525     C = d - 3c + 3b - a
526 
527     F' = 3Ct^2 + 6Bt + 3A
528     F'' = 6Ct + 6B
529 
530     F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
531 */
findMaxCurvature(double tValues[]) const532 int SkDCubic::findMaxCurvature(double tValues[]) const {
533     double coeffX[4], coeffY[4];
534     int i;
535     formulate_F1DotF2(&fPts[0].fX, coeffX);
536     formulate_F1DotF2(&fPts[0].fY, coeffY);
537     for (i = 0; i < 4; i++) {
538         coeffX[i] = coeffX[i] + coeffY[i];
539     }
540     return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
541 }
542 
ptAtT(double t) const543 SkDPoint SkDCubic::ptAtT(double t) const {
544     if (0 == t) {
545         return fPts[0];
546     }
547     if (1 == t) {
548         return fPts[3];
549     }
550     double one_t = 1 - t;
551     double one_t2 = one_t * one_t;
552     double a = one_t2 * one_t;
553     double b = 3 * one_t2 * t;
554     double t2 = t * t;
555     double c = 3 * one_t * t2;
556     double d = t2 * t;
557     SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
558             a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
559     return result;
560 }
561 
562 /*
563  Given a cubic c, t1, and t2, find a small cubic segment.
564 
565  The new cubic is defined as points A, B, C, and D, where
566  s1 = 1 - t1
567  s2 = 1 - t2
568  A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
569  D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
570 
571  We don't have B or C. So We define two equations to isolate them.
572  First, compute two reference T values 1/3 and 2/3 from t1 to t2:
573 
574  c(at (2*t1 + t2)/3) == E
575  c(at (t1 + 2*t2)/3) == F
576 
577  Next, compute where those values must be if we know the values of B and C:
578 
579  _12   =  A*2/3 + B*1/3
580  12_   =  A*1/3 + B*2/3
581  _23   =  B*2/3 + C*1/3
582  23_   =  B*1/3 + C*2/3
583  _34   =  C*2/3 + D*1/3
584  34_   =  C*1/3 + D*2/3
585  _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
586  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
587  _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
588  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
589  _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
590        =  A*8/27 + B*12/27 + C*6/27 + D*1/27
591        =  E
592  1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
593        =  A*1/27 + B*6/27 + C*12/27 + D*8/27
594        =  F
595  E*27  =  A*8    + B*12   + C*6     + D
596  F*27  =  A      + B*6    + C*12    + D*8
597 
598 Group the known values on one side:
599 
600  M       = E*27 - A*8 - D     = B*12 + C* 6
601  N       = F*27 - A   - D*8   = B* 6 + C*12
602  M*2 - N = B*18
603  N*2 - M = C*18
604  B       = (M*2 - N)/18
605  C       = (N*2 - M)/18
606  */
607 
interp_cubic_coords(const double * src,double t)608 static double interp_cubic_coords(const double* src, double t) {
609     double ab = SkDInterp(src[0], src[2], t);
610     double bc = SkDInterp(src[2], src[4], t);
611     double cd = SkDInterp(src[4], src[6], t);
612     double abc = SkDInterp(ab, bc, t);
613     double bcd = SkDInterp(bc, cd, t);
614     double abcd = SkDInterp(abc, bcd, t);
615     return abcd;
616 }
617 
subDivide(double t1,double t2) const618 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
619     if (t1 == 0 || t2 == 1) {
620         if (t1 == 0 && t2 == 1) {
621             return *this;
622         }
623         SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
624         SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
625         return dst;
626     }
627     SkDCubic dst;
628     double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
629     double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
630     double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
631     double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
632     double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
633     double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
634     double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
635     double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
636     double mx = ex * 27 - ax * 8 - dx;
637     double my = ey * 27 - ay * 8 - dy;
638     double nx = fx * 27 - ax - dx * 8;
639     double ny = fy * 27 - ay - dy * 8;
640     /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
641     /* by = */ dst[1].fY = (my * 2 - ny) / 18;
642     /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
643     /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
644     // FIXME: call align() ?
645     return dst;
646 }
647 
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const648 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
649                          double t1, double t2, SkDPoint dst[2]) const {
650     SkASSERT(t1 != t2);
651     // this approach assumes that the control points computed directly are accurate enough
652     SkDCubic sub = subDivide(t1, t2);
653     dst[0] = sub[1] + (a - sub[0]);
654     dst[1] = sub[2] + (d - sub[3]);
655     if (t1 == 0 || t2 == 0) {
656         align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
657     }
658     if (t1 == 1 || t2 == 1) {
659         align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
660     }
661     if (AlmostBequalUlps(dst[0].fX, a.fX)) {
662         dst[0].fX = a.fX;
663     }
664     if (AlmostBequalUlps(dst[0].fY, a.fY)) {
665         dst[0].fY = a.fY;
666     }
667     if (AlmostBequalUlps(dst[1].fX, d.fX)) {
668         dst[1].fX = d.fX;
669     }
670     if (AlmostBequalUlps(dst[1].fY, d.fY)) {
671         dst[1].fY = d.fY;
672     }
673 }
674 
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const675 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
676     double extremeTs[2];
677     double topT = -1;
678     int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
679     for (int index = 0; index < roots; ++index) {
680         double t = startT + (endT - startT) * extremeTs[index];
681         SkDPoint mid = dCurve.ptAtT(t);
682         if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
683             topT = t;
684             *topPt = mid;
685         }
686     }
687     return topT;
688 }
689