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 SkOPASSERT(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 // 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, c2.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, quad.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 = 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
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 SkScalar d[3];
252 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
253 switch (cubicType) {
254 case kLoop_SkCubicType: {
255 // crib code from gpu path utils that finds t values where loop self-intersects
256 // use it to find mid of t values which should be a friendly place to chop
257 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
258 SkScalar ls = d[1] - tempSqrt;
259 SkScalar lt = 2.f * d[0];
260 SkScalar ms = d[1] + tempSqrt;
261 SkScalar mt = 2.f * d[0];
262 if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
263 ls = ls / lt;
264 ms = ms / mt;
265 SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
266 t[0] = (ls + ms) / 2;
267 SkASSERT(roughly_between(0, *t, 1));
268 return (int) (t[0] > 0 && t[0] < 1);
269 }
270 }
271 // fall through if no t value found
272 case kSerpentine_SkCubicType:
273 case kCusp_SkCubicType: {
274 double inflectionTs[2];
275 int infTCount = cubic.findInflections(inflectionTs);
276 double maxCurvature[3];
277 int roots = cubic.findMaxCurvature(maxCurvature);
278 #if DEBUG_CUBIC_SPLIT
279 SkDebugf("%s\n", __FUNCTION__);
280 cubic.dump();
281 for (int index = 0; index < infTCount; ++index) {
282 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
283 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
284 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
285 SkDLine perp = {{pt - dPt, pt + dPt}};
286 perp.dump();
287 }
288 for (int index = 0; index < roots; ++index) {
289 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
290 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
291 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
292 SkDLine perp = {{pt - dPt, pt + dPt}};
293 perp.dump();
294 }
295 #endif
296 if (infTCount == 2) {
297 for (int index = 0; index < roots; ++index) {
298 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
299 t[0] = maxCurvature[index];
300 return (int) (t[0] > 0 && t[0] < 1);
301 }
302 }
303 } else {
304 int resultCount = 0;
305 // FIXME: constant found through experimentation -- maybe there's a better way....
306 double precision = cubic.calcPrecision() * 2;
307 for (int index = 0; index < roots; ++index) {
308 double testT = maxCurvature[index];
309 if (0 >= testT || testT >= 1) {
310 continue;
311 }
312 // don't call dxdyAtT since we want (0,0) results
313 SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
314 derivative_at_t(&cubic.fPts[0].fY, testT) };
315 double dPtLen = dPt.length();
316 if (dPtLen < precision) {
317 t[resultCount++] = testT;
318 }
319 }
320 if (!resultCount && infTCount == 1) {
321 t[0] = inflectionTs[0];
322 resultCount = (int) (t[0] > 0 && t[0] < 1);
323 }
324 return resultCount;
325 }
326 }
327 default:
328 ;
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);
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)
RootsValidT(double A,double B,double C,double D,double t[3])380 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
381 double s[3];
382 int realRoots = RootsReal(A, B, C, D, s);
383 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
384 for (int index = 0; index < realRoots; ++index) {
385 double tValue = s[index];
386 if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
387 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
388 if (approximately_equal(t[idx2], 1)) {
389 goto nextRoot;
390 }
391 }
392 SkASSERT(foundRoots < 3);
393 t[foundRoots++] = 1;
394 } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
395 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
396 if (approximately_equal(t[idx2], 0)) {
397 goto nextRoot;
398 }
399 }
400 SkASSERT(foundRoots < 3);
401 t[foundRoots++] = 0;
402 }
403 nextRoot:
404 ;
405 }
406 return foundRoots;
407 }
408
RootsReal(double A,double B,double C,double D,double s[3])409 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
410 #ifdef SK_DEBUG
411 // create a string mathematica understands
412 // GDB set print repe 15 # if repeated digits is a bother
413 // set print elements 400 # if line doesn't fit
414 char str[1024];
415 sk_bzero(str, sizeof(str));
416 SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
417 A, B, C, D);
418 SkPathOpsDebug::MathematicaIze(str, sizeof(str));
419 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
420 SkDebugf("%s\n", str);
421 #endif
422 #endif
423 if (approximately_zero(A)
424 && approximately_zero_when_compared_to(A, B)
425 && approximately_zero_when_compared_to(A, C)
426 && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic
427 return SkDQuad::RootsReal(B, C, D, s);
428 }
429 if (approximately_zero_when_compared_to(D, A)
430 && approximately_zero_when_compared_to(D, B)
431 && approximately_zero_when_compared_to(D, C)) { // 0 is one root
432 int num = SkDQuad::RootsReal(A, B, C, s);
433 for (int i = 0; i < num; ++i) {
434 if (approximately_zero(s[i])) {
435 return num;
436 }
437 }
438 s[num++] = 0;
439 return num;
440 }
441 if (approximately_zero(A + B + C + D)) { // 1 is one root
442 int num = SkDQuad::RootsReal(A, A + B, -D, s);
443 for (int i = 0; i < num; ++i) {
444 if (AlmostDequalUlps(s[i], 1)) {
445 return num;
446 }
447 }
448 s[num++] = 1;
449 return num;
450 }
451 double a, b, c;
452 {
453 double invA = 1 / A;
454 a = B * invA;
455 b = C * invA;
456 c = D * invA;
457 }
458 double a2 = a * a;
459 double Q = (a2 - b * 3) / 9;
460 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
461 double R2 = R * R;
462 double Q3 = Q * Q * Q;
463 double R2MinusQ3 = R2 - Q3;
464 double adiv3 = a / 3;
465 double r;
466 double* roots = s;
467 if (R2MinusQ3 < 0) { // we have 3 real roots
468 // the divide/root can, due to finite precisions, be slightly outside of -1...1
469 double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.));
470 double neg2RootQ = -2 * sqrt(Q);
471
472 r = neg2RootQ * cos(theta / 3) - adiv3;
473 *roots++ = r;
474
475 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
476 if (!AlmostDequalUlps(s[0], r)) {
477 *roots++ = r;
478 }
479 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
480 if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
481 *roots++ = r;
482 }
483 } else { // we have 1 real root
484 double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
485 double A = fabs(R) + sqrtR2MinusQ3;
486 A = SkDCubeRoot(A);
487 if (R > 0) {
488 A = -A;
489 }
490 if (A != 0) {
491 A += Q / A;
492 }
493 r = A - adiv3;
494 *roots++ = r;
495 if (AlmostDequalUlps((double) R2, (double) Q3)) {
496 r = -A / 2 - adiv3;
497 if (!AlmostDequalUlps(s[0], r)) {
498 *roots++ = r;
499 }
500 }
501 }
502 return static_cast<int>(roots - s);
503 }
504
505 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const506 SkDVector SkDCubic::dxdyAtT(double t) const {
507 SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
508 if (result.fX == 0 && result.fY == 0) {
509 if (t == 0) {
510 result = fPts[2] - fPts[0];
511 } else if (t == 1) {
512 result = fPts[3] - fPts[1];
513 } else {
514 // incomplete
515 SkDebugf("!c");
516 }
517 if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
518 result = fPts[3] - fPts[0];
519 }
520 }
521 return result;
522 }
523
524 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const525 int SkDCubic::findInflections(double tValues[]) const {
526 double Ax = fPts[1].fX - fPts[0].fX;
527 double Ay = fPts[1].fY - fPts[0].fY;
528 double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
529 double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
530 double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
531 double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
532 return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
533 }
534
formulate_F1DotF2(const double src[],double coeff[4])535 static void formulate_F1DotF2(const double src[], double coeff[4]) {
536 double a = src[2] - src[0];
537 double b = src[4] - 2 * src[2] + src[0];
538 double c = src[6] + 3 * (src[2] - src[4]) - src[0];
539 coeff[0] = c * c;
540 coeff[1] = 3 * b * c;
541 coeff[2] = 2 * b * b + c * a;
542 coeff[3] = a * b;
543 }
544
545 /** SkDCubic'(t) = At^2 + Bt + C, where
546 A = 3(-a + 3(b - c) + d)
547 B = 6(a - 2b + c)
548 C = 3(b - a)
549 Solve for t, keeping only those that fit between 0 < t < 1
550 */
FindExtrema(const double src[],double tValues[2])551 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
552 // we divide A,B,C by 3 to simplify
553 double a = src[0];
554 double b = src[2];
555 double c = src[4];
556 double d = src[6];
557 double A = d - a + 3 * (b - c);
558 double B = 2 * (a - b - b + c);
559 double C = b - a;
560
561 return SkDQuad::RootsValidT(A, B, C, tValues);
562 }
563
564 /* from SkGeometry.cpp
565 Looking for F' dot F'' == 0
566
567 A = b - a
568 B = c - 2b + a
569 C = d - 3c + 3b - a
570
571 F' = 3Ct^2 + 6Bt + 3A
572 F'' = 6Ct + 6B
573
574 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
575 */
findMaxCurvature(double tValues[]) const576 int SkDCubic::findMaxCurvature(double tValues[]) const {
577 double coeffX[4], coeffY[4];
578 int i;
579 formulate_F1DotF2(&fPts[0].fX, coeffX);
580 formulate_F1DotF2(&fPts[0].fY, coeffY);
581 for (i = 0; i < 4; i++) {
582 coeffX[i] = coeffX[i] + coeffY[i];
583 }
584 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
585 }
586
ptAtT(double t) const587 SkDPoint SkDCubic::ptAtT(double t) const {
588 if (0 == t) {
589 return fPts[0];
590 }
591 if (1 == t) {
592 return fPts[3];
593 }
594 double one_t = 1 - t;
595 double one_t2 = one_t * one_t;
596 double a = one_t2 * one_t;
597 double b = 3 * one_t2 * t;
598 double t2 = t * t;
599 double c = 3 * one_t * t2;
600 double d = t2 * t;
601 SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
602 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
603 return result;
604 }
605
606 /*
607 Given a cubic c, t1, and t2, find a small cubic segment.
608
609 The new cubic is defined as points A, B, C, and D, where
610 s1 = 1 - t1
611 s2 = 1 - t2
612 A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
613 D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
614
615 We don't have B or C. So We define two equations to isolate them.
616 First, compute two reference T values 1/3 and 2/3 from t1 to t2:
617
618 c(at (2*t1 + t2)/3) == E
619 c(at (t1 + 2*t2)/3) == F
620
621 Next, compute where those values must be if we know the values of B and C:
622
623 _12 = A*2/3 + B*1/3
624 12_ = A*1/3 + B*2/3
625 _23 = B*2/3 + C*1/3
626 23_ = B*1/3 + C*2/3
627 _34 = C*2/3 + D*1/3
628 34_ = C*1/3 + D*2/3
629 _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
630 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
631 _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
632 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
633 _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
634 = A*8/27 + B*12/27 + C*6/27 + D*1/27
635 = E
636 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
637 = A*1/27 + B*6/27 + C*12/27 + D*8/27
638 = F
639 E*27 = A*8 + B*12 + C*6 + D
640 F*27 = A + B*6 + C*12 + D*8
641
642 Group the known values on one side:
643
644 M = E*27 - A*8 - D = B*12 + C* 6
645 N = F*27 - A - D*8 = B* 6 + C*12
646 M*2 - N = B*18
647 N*2 - M = C*18
648 B = (M*2 - N)/18
649 C = (N*2 - M)/18
650 */
651
interp_cubic_coords(const double * src,double t)652 static double interp_cubic_coords(const double* src, double t) {
653 double ab = SkDInterp(src[0], src[2], t);
654 double bc = SkDInterp(src[2], src[4], t);
655 double cd = SkDInterp(src[4], src[6], t);
656 double abc = SkDInterp(ab, bc, t);
657 double bcd = SkDInterp(bc, cd, t);
658 double abcd = SkDInterp(abc, bcd, t);
659 return abcd;
660 }
661
subDivide(double t1,double t2) const662 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
663 if (t1 == 0 || t2 == 1) {
664 if (t1 == 0 && t2 == 1) {
665 return *this;
666 }
667 SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
668 SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
669 return dst;
670 }
671 SkDCubic dst;
672 double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
673 double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
674 double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
675 double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
676 double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
677 double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
678 double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
679 double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
680 double mx = ex * 27 - ax * 8 - dx;
681 double my = ey * 27 - ay * 8 - dy;
682 double nx = fx * 27 - ax - dx * 8;
683 double ny = fy * 27 - ay - dy * 8;
684 /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
685 /* by = */ dst[1].fY = (my * 2 - ny) / 18;
686 /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
687 /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
688 // FIXME: call align() ?
689 return dst;
690 }
691
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const692 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
693 double t1, double t2, SkDPoint dst[2]) const {
694 SkASSERT(t1 != t2);
695 // this approach assumes that the control points computed directly are accurate enough
696 SkDCubic sub = subDivide(t1, t2);
697 dst[0] = sub[1] + (a - sub[0]);
698 dst[1] = sub[2] + (d - sub[3]);
699 if (t1 == 0 || t2 == 0) {
700 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
701 }
702 if (t1 == 1 || t2 == 1) {
703 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
704 }
705 if (AlmostBequalUlps(dst[0].fX, a.fX)) {
706 dst[0].fX = a.fX;
707 }
708 if (AlmostBequalUlps(dst[0].fY, a.fY)) {
709 dst[0].fY = a.fY;
710 }
711 if (AlmostBequalUlps(dst[1].fX, d.fX)) {
712 dst[1].fX = d.fX;
713 }
714 if (AlmostBequalUlps(dst[1].fY, d.fY)) {
715 dst[1].fY = d.fY;
716 }
717 }
718
toFloatPoints(SkPoint * pts) const719 bool SkDCubic::toFloatPoints(SkPoint* pts) const {
720 const double* dCubic = &fPts[0].fX;
721 SkScalar* cubic = &pts[0].fX;
722 for (int index = 0; index < kPointCount * 2; ++index) {
723 *cubic++ = SkDoubleToScalar(*dCubic++);
724 }
725 return SkScalarsAreFinite(&pts->fX, kPointCount * 2);
726 }
727
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const728 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
729 double extremeTs[2];
730 double topT = -1;
731 int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
732 for (int index = 0; index < roots; ++index) {
733 double t = startT + (endT - startT) * extremeTs[index];
734 SkDPoint mid = dCurve.ptAtT(t);
735 if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
736 topT = t;
737 *topPt = mid;
738 }
739 }
740 return topT;
741 }
742