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)232 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
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             return *t > 0 && *t < 1;
250         }
251     } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) {
252         SkDCubic cubic;
253         cubic.set(pointsPtr);
254         double inflectionTs[2];
255         int infTCount = cubic.findInflections(inflectionTs);
256         if (infTCount == 2) {
257             double maxCurvature[3];
258             int roots = cubic.findMaxCurvature(maxCurvature);
259 #if DEBUG_CUBIC_SPLIT
260             SkDebugf("%s\n", __FUNCTION__);
261             cubic.dump();
262             for (int index = 0; index < infTCount; ++index) {
263                 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
264                 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
265                 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
266                 SkDLine perp = {{pt - dPt, pt + dPt}};
267                 perp.dump();
268             }
269             for (int index = 0; index < roots; ++index) {
270                 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
271                 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
272                 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
273                 SkDLine perp = {{pt - dPt, pt + dPt}};
274                 perp.dump();
275             }
276 #endif
277             for (int index = 0; index < roots; ++index) {
278                 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
279                     *t = maxCurvature[index];
280                     return true;
281                 }
282             }
283         } else if (infTCount == 1) {
284             *t = inflectionTs[0];
285             return *t > 0 && *t < 1;
286         }
287     }
288     return false;
289 }
290 
monotonicInX() const291 bool SkDCubic::monotonicInX() const {
292     return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
293             && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
294 }
295 
monotonicInY() const296 bool SkDCubic::monotonicInY() const {
297     return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
298             && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
299 }
300 
otherPts(int index,const SkDPoint * o1Pts[kPointCount-1]) const301 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
302     int offset = (int) !SkToBool(index);
303     o1Pts[0] = &fPts[offset];
304     o1Pts[1] = &fPts[++offset];
305     o1Pts[2] = &fPts[++offset];
306 }
307 
searchRoots(double extremeTs[6],int extrema,double axisIntercept,SearchAxis xAxis,double * validRoots) const308 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
309         SearchAxis xAxis, double* validRoots) const {
310     extrema += findInflections(&extremeTs[extrema]);
311     extremeTs[extrema++] = 0;
312     extremeTs[extrema] = 1;
313     SkTQSort(extremeTs, extremeTs + extrema);
314     int validCount = 0;
315     for (int index = 0; index < extrema; ) {
316         double min = extremeTs[index];
317         double max = extremeTs[++index];
318         if (min == max) {
319             continue;
320         }
321         double newT = binarySearch(min, max, axisIntercept, xAxis);
322         if (newT >= 0) {
323             validRoots[validCount++] = newT;
324         }
325     }
326     return validCount;
327 }
328 
329 // cubic roots
330 
331 static const double PI = 3.141592653589793;
332 
333 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
RootsValidT(double A,double B,double C,double D,double t[3])334 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
335     double s[3];
336     int realRoots = RootsReal(A, B, C, D, s);
337     int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
338     for (int index = 0; index < realRoots; ++index) {
339         double tValue = s[index];
340         if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
341             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
342                 if (approximately_equal(t[idx2], 1)) {
343                     goto nextRoot;
344                 }
345             }
346             SkASSERT(foundRoots < 3);
347             t[foundRoots++] = 1;
348         } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
349             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
350                 if (approximately_equal(t[idx2], 0)) {
351                     goto nextRoot;
352                 }
353             }
354             SkASSERT(foundRoots < 3);
355             t[foundRoots++] = 0;
356         }
357 nextRoot:
358         ;
359     }
360     return foundRoots;
361 }
362 
RootsReal(double A,double B,double C,double D,double s[3])363 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
364 #ifdef SK_DEBUG
365     // create a string mathematica understands
366     // GDB set print repe 15 # if repeated digits is a bother
367     //     set print elements 400 # if line doesn't fit
368     char str[1024];
369     sk_bzero(str, sizeof(str));
370     SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
371             A, B, C, D);
372     SkPathOpsDebug::MathematicaIze(str, sizeof(str));
373 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
374     SkDebugf("%s\n", str);
375 #endif
376 #endif
377     if (approximately_zero(A)
378             && approximately_zero_when_compared_to(A, B)
379             && approximately_zero_when_compared_to(A, C)
380             && approximately_zero_when_compared_to(A, D)) {  // we're just a quadratic
381         return SkDQuad::RootsReal(B, C, D, s);
382     }
383     if (approximately_zero_when_compared_to(D, A)
384             && approximately_zero_when_compared_to(D, B)
385             && approximately_zero_when_compared_to(D, C)) {  // 0 is one root
386         int num = SkDQuad::RootsReal(A, B, C, s);
387         for (int i = 0; i < num; ++i) {
388             if (approximately_zero(s[i])) {
389                 return num;
390             }
391         }
392         s[num++] = 0;
393         return num;
394     }
395     if (approximately_zero(A + B + C + D)) {  // 1 is one root
396         int num = SkDQuad::RootsReal(A, A + B, -D, s);
397         for (int i = 0; i < num; ++i) {
398             if (AlmostDequalUlps(s[i], 1)) {
399                 return num;
400             }
401         }
402         s[num++] = 1;
403         return num;
404     }
405     double a, b, c;
406     {
407         double invA = 1 / A;
408         a = B * invA;
409         b = C * invA;
410         c = D * invA;
411     }
412     double a2 = a * a;
413     double Q = (a2 - b * 3) / 9;
414     double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
415     double R2 = R * R;
416     double Q3 = Q * Q * Q;
417     double R2MinusQ3 = R2 - Q3;
418     double adiv3 = a / 3;
419     double r;
420     double* roots = s;
421     if (R2MinusQ3 < 0) {   // we have 3 real roots
422         double theta = acos(R / sqrt(Q3));
423         double neg2RootQ = -2 * sqrt(Q);
424 
425         r = neg2RootQ * cos(theta / 3) - adiv3;
426         *roots++ = r;
427 
428         r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
429         if (!AlmostDequalUlps(s[0], r)) {
430             *roots++ = r;
431         }
432         r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
433         if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
434             *roots++ = r;
435         }
436     } else {  // we have 1 real root
437         double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
438         double A = fabs(R) + sqrtR2MinusQ3;
439         A = SkDCubeRoot(A);
440         if (R > 0) {
441             A = -A;
442         }
443         if (A != 0) {
444             A += Q / A;
445         }
446         r = A - adiv3;
447         *roots++ = r;
448         if (AlmostDequalUlps((double) R2, (double) Q3)) {
449             r = -A / 2 - adiv3;
450             if (!AlmostDequalUlps(s[0], r)) {
451                 *roots++ = r;
452             }
453         }
454     }
455     return static_cast<int>(roots - s);
456 }
457 
458 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
459 // c(t)  = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
460 // c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
461 //       = 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)462 static double derivative_at_t(const double* src, double t) {
463     double one_t = 1 - t;
464     double a = src[0];
465     double b = src[2];
466     double c = src[4];
467     double d = src[6];
468     return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
469 }
470 
471 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const472 SkDVector SkDCubic::dxdyAtT(double t) const {
473     SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
474     if (result.fX == 0 && result.fY == 0) {
475         if (t == 0) {
476             result = fPts[2] - fPts[0];
477         } else if (t == 1) {
478             result = fPts[3] - fPts[1];
479         } else {
480             // incomplete
481             SkDebugf("!c");
482         }
483         if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
484             result = fPts[3] - fPts[0];
485         }
486     }
487     return result;
488 }
489 
490 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const491 int SkDCubic::findInflections(double tValues[]) const {
492     double Ax = fPts[1].fX - fPts[0].fX;
493     double Ay = fPts[1].fY - fPts[0].fY;
494     double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
495     double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
496     double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
497     double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
498     return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
499 }
500 
formulate_F1DotF2(const double src[],double coeff[4])501 static void formulate_F1DotF2(const double src[], double coeff[4]) {
502     double a = src[2] - src[0];
503     double b = src[4] - 2 * src[2] + src[0];
504     double c = src[6] + 3 * (src[2] - src[4]) - src[0];
505     coeff[0] = c * c;
506     coeff[1] = 3 * b * c;
507     coeff[2] = 2 * b * b + c * a;
508     coeff[3] = a * b;
509 }
510 
511 /** SkDCubic'(t) = At^2 + Bt + C, where
512     A = 3(-a + 3(b - c) + d)
513     B = 6(a - 2b + c)
514     C = 3(b - a)
515     Solve for t, keeping only those that fit between 0 < t < 1
516 */
FindExtrema(const double src[],double tValues[2])517 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
518     // we divide A,B,C by 3 to simplify
519     double a = src[0];
520     double b = src[2];
521     double c = src[4];
522     double d = src[6];
523     double A = d - a + 3 * (b - c);
524     double B = 2 * (a - b - b + c);
525     double C = b - a;
526 
527     return SkDQuad::RootsValidT(A, B, C, tValues);
528 }
529 
530 /*  from SkGeometry.cpp
531     Looking for F' dot F'' == 0
532 
533     A = b - a
534     B = c - 2b + a
535     C = d - 3c + 3b - a
536 
537     F' = 3Ct^2 + 6Bt + 3A
538     F'' = 6Ct + 6B
539 
540     F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
541 */
findMaxCurvature(double tValues[]) const542 int SkDCubic::findMaxCurvature(double tValues[]) const {
543     double coeffX[4], coeffY[4];
544     int i;
545     formulate_F1DotF2(&fPts[0].fX, coeffX);
546     formulate_F1DotF2(&fPts[0].fY, coeffY);
547     for (i = 0; i < 4; i++) {
548         coeffX[i] = coeffX[i] + coeffY[i];
549     }
550     return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
551 }
552 
ptAtT(double t) const553 SkDPoint SkDCubic::ptAtT(double t) const {
554     if (0 == t) {
555         return fPts[0];
556     }
557     if (1 == t) {
558         return fPts[3];
559     }
560     double one_t = 1 - t;
561     double one_t2 = one_t * one_t;
562     double a = one_t2 * one_t;
563     double b = 3 * one_t2 * t;
564     double t2 = t * t;
565     double c = 3 * one_t * t2;
566     double d = t2 * t;
567     SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
568             a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
569     return result;
570 }
571 
572 /*
573  Given a cubic c, t1, and t2, find a small cubic segment.
574 
575  The new cubic is defined as points A, B, C, and D, where
576  s1 = 1 - t1
577  s2 = 1 - t2
578  A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
579  D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
580 
581  We don't have B or C. So We define two equations to isolate them.
582  First, compute two reference T values 1/3 and 2/3 from t1 to t2:
583 
584  c(at (2*t1 + t2)/3) == E
585  c(at (t1 + 2*t2)/3) == F
586 
587  Next, compute where those values must be if we know the values of B and C:
588 
589  _12   =  A*2/3 + B*1/3
590  12_   =  A*1/3 + B*2/3
591  _23   =  B*2/3 + C*1/3
592  23_   =  B*1/3 + C*2/3
593  _34   =  C*2/3 + D*1/3
594  34_   =  C*1/3 + D*2/3
595  _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
596  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
597  _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
598  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
599  _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
600        =  A*8/27 + B*12/27 + C*6/27 + D*1/27
601        =  E
602  1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
603        =  A*1/27 + B*6/27 + C*12/27 + D*8/27
604        =  F
605  E*27  =  A*8    + B*12   + C*6     + D
606  F*27  =  A      + B*6    + C*12    + D*8
607 
608 Group the known values on one side:
609 
610  M       = E*27 - A*8 - D     = B*12 + C* 6
611  N       = F*27 - A   - D*8   = B* 6 + C*12
612  M*2 - N = B*18
613  N*2 - M = C*18
614  B       = (M*2 - N)/18
615  C       = (N*2 - M)/18
616  */
617 
interp_cubic_coords(const double * src,double t)618 static double interp_cubic_coords(const double* src, double t) {
619     double ab = SkDInterp(src[0], src[2], t);
620     double bc = SkDInterp(src[2], src[4], t);
621     double cd = SkDInterp(src[4], src[6], t);
622     double abc = SkDInterp(ab, bc, t);
623     double bcd = SkDInterp(bc, cd, t);
624     double abcd = SkDInterp(abc, bcd, t);
625     return abcd;
626 }
627 
subDivide(double t1,double t2) const628 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
629     if (t1 == 0 || t2 == 1) {
630         if (t1 == 0 && t2 == 1) {
631             return *this;
632         }
633         SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
634         SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
635         return dst;
636     }
637     SkDCubic dst;
638     double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
639     double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
640     double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
641     double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
642     double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
643     double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
644     double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
645     double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
646     double mx = ex * 27 - ax * 8 - dx;
647     double my = ey * 27 - ay * 8 - dy;
648     double nx = fx * 27 - ax - dx * 8;
649     double ny = fy * 27 - ay - dy * 8;
650     /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
651     /* by = */ dst[1].fY = (my * 2 - ny) / 18;
652     /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
653     /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
654     // FIXME: call align() ?
655     return dst;
656 }
657 
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const658 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
659                          double t1, double t2, SkDPoint dst[2]) const {
660     SkASSERT(t1 != t2);
661     // this approach assumes that the control points computed directly are accurate enough
662     SkDCubic sub = subDivide(t1, t2);
663     dst[0] = sub[1] + (a - sub[0]);
664     dst[1] = sub[2] + (d - sub[3]);
665     if (t1 == 0 || t2 == 0) {
666         align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
667     }
668     if (t1 == 1 || t2 == 1) {
669         align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
670     }
671     if (AlmostBequalUlps(dst[0].fX, a.fX)) {
672         dst[0].fX = a.fX;
673     }
674     if (AlmostBequalUlps(dst[0].fY, a.fY)) {
675         dst[0].fY = a.fY;
676     }
677     if (AlmostBequalUlps(dst[1].fX, d.fX)) {
678         dst[1].fX = d.fX;
679     }
680     if (AlmostBequalUlps(dst[1].fY, d.fY)) {
681         dst[1].fY = d.fY;
682     }
683 }
684 
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const685 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
686     double extremeTs[2];
687     double topT = -1;
688     int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
689     for (int index = 0; index < roots; ++index) {
690         double t = startT + (endT - startT) * extremeTs[index];
691         SkDPoint mid = dCurve.ptAtT(t);
692         if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
693             topT = t;
694             *topPt = mid;
695         }
696     }
697     return topT;
698 }
699