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