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