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 "include/private/SkTPin.h"
8 #include "src/core/SkGeometry.h"
9 #include "src/core/SkTSort.h"
10 #include "src/pathops/SkLineParameters.h"
11 #include "src/pathops/SkPathOpsConic.h"
12 #include "src/pathops/SkPathOpsCubic.h"
13 #include "src/pathops/SkPathOpsCurve.h"
14 #include "src/pathops/SkPathOpsLine.h"
15 #include "src/pathops/SkPathOpsQuad.h"
16 #include "src/pathops/SkPathOpsRect.h"
17
18 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in test framework
19
align(int endIndex,int ctrlIndex,SkDPoint * dstPt) const20 void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
21 if (fPts[endIndex].fX == fPts[ctrlIndex].fX) {
22 dstPt->fX = fPts[endIndex].fX;
23 }
24 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
25 dstPt->fY = fPts[endIndex].fY;
26 }
27 }
28
29 // give up when changing t no longer moves point
30 // also, copy point rather than recompute it when it does change
binarySearch(double min,double max,double axisIntercept,SearchAxis xAxis) const31 double SkDCubic::binarySearch(double min, double max, double axisIntercept,
32 SearchAxis xAxis) const {
33 double t = (min + max) / 2;
34 double step = (t - min) / 2;
35 SkDPoint cubicAtT = ptAtT(t);
36 double calcPos = (&cubicAtT.fX)[xAxis];
37 double calcDist = calcPos - axisIntercept;
38 do {
39 double priorT = std::max(min, t - step);
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, SkDCubic::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, SkDQuad::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 = std::min(std::min(std::min(std::min(std::min(std::min(std::min(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 = std::max(std::max(std::max(std::max(std::max(std::max(std::max(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 = std::max(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 double tt[2], ss[2];
252 SkCubicType cubicType = SkClassifyCubic(pointsPtr, tt, ss);
253 switch (cubicType) {
254 case SkCubicType::kLoop: {
255 const double &td = tt[0], &te = tt[1], &sd = ss[0], &se = ss[1];
256 if (roughly_between(0, td, sd) && roughly_between(0, te, se)) {
257 t[0] = static_cast<SkScalar>((td * se + te * sd) / (2 * sd * se));
258 return (int) (t[0] > 0 && t[0] < 1);
259 }
260 }
261 [[fallthrough]]; // fall through if no t value found
262 case SkCubicType::kSerpentine:
263 case SkCubicType::kLocalCusp:
264 case SkCubicType::kCuspAtInfinity: {
265 double inflectionTs[2];
266 int infTCount = cubic.findInflections(inflectionTs);
267 double maxCurvature[3];
268 int roots = cubic.findMaxCurvature(maxCurvature);
269 #if DEBUG_CUBIC_SPLIT
270 SkDebugf("%s\n", __FUNCTION__);
271 cubic.dump();
272 for (int index = 0; index < infTCount; ++index) {
273 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
274 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
275 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
276 SkDLine perp = {{pt - dPt, pt + dPt}};
277 perp.dump();
278 }
279 for (int index = 0; index < roots; ++index) {
280 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
281 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
282 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
283 SkDLine perp = {{pt - dPt, pt + dPt}};
284 perp.dump();
285 }
286 #endif
287 if (infTCount == 2) {
288 for (int index = 0; index < roots; ++index) {
289 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
290 t[0] = maxCurvature[index];
291 return (int) (t[0] > 0 && t[0] < 1);
292 }
293 }
294 } else {
295 int resultCount = 0;
296 // FIXME: constant found through experimentation -- maybe there's a better way....
297 double precision = cubic.calcPrecision() * 2;
298 for (int index = 0; index < roots; ++index) {
299 double testT = maxCurvature[index];
300 if (0 >= testT || testT >= 1) {
301 continue;
302 }
303 // don't call dxdyAtT since we want (0,0) results
304 SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
305 derivative_at_t(&cubic.fPts[0].fY, testT) };
306 double dPtLen = dPt.length();
307 if (dPtLen < precision) {
308 t[resultCount++] = testT;
309 }
310 }
311 if (!resultCount && infTCount == 1) {
312 t[0] = inflectionTs[0];
313 resultCount = (int) (t[0] > 0 && t[0] < 1);
314 }
315 return resultCount;
316 }
317 break;
318 }
319 default:
320 break;
321 }
322 return 0;
323 }
324
monotonicInX() const325 bool SkDCubic::monotonicInX() const {
326 return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
327 && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
328 }
329
monotonicInY() const330 bool SkDCubic::monotonicInY() const {
331 return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
332 && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
333 }
334
otherPts(int index,const SkDPoint * o1Pts[kPointCount-1]) const335 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
336 int offset = (int) !SkToBool(index);
337 o1Pts[0] = &fPts[offset];
338 o1Pts[1] = &fPts[++offset];
339 o1Pts[2] = &fPts[++offset];
340 }
341
searchRoots(double extremeTs[6],int extrema,double axisIntercept,SearchAxis xAxis,double * validRoots) const342 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
343 SearchAxis xAxis, double* validRoots) const {
344 extrema += findInflections(&extremeTs[extrema]);
345 extremeTs[extrema++] = 0;
346 extremeTs[extrema] = 1;
347 SkASSERT(extrema < 6);
348 SkTQSort(extremeTs, extremeTs + extrema + 1);
349 int validCount = 0;
350 for (int index = 0; index < extrema; ) {
351 double min = extremeTs[index];
352 double max = extremeTs[++index];
353 if (min == max) {
354 continue;
355 }
356 double newT = binarySearch(min, max, axisIntercept, xAxis);
357 if (newT >= 0) {
358 if (validCount >= 3) {
359 return 0;
360 }
361 validRoots[validCount++] = newT;
362 }
363 }
364 return validCount;
365 }
366
367 // cubic roots
368
369 static const double PI = 3.141592653589793;
370
371 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
RootsValidT(double A,double B,double C,double D,double t[3])372 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
373 double s[3];
374 int realRoots = RootsReal(A, B, C, D, s);
375 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
376 for (int index = 0; index < realRoots; ++index) {
377 double tValue = s[index];
378 if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
379 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
380 if (approximately_equal(t[idx2], 1)) {
381 goto nextRoot;
382 }
383 }
384 SkASSERT(foundRoots < 3);
385 t[foundRoots++] = 1;
386 } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
387 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
388 if (approximately_equal(t[idx2], 0)) {
389 goto nextRoot;
390 }
391 }
392 SkASSERT(foundRoots < 3);
393 t[foundRoots++] = 0;
394 }
395 nextRoot:
396 ;
397 }
398 return foundRoots;
399 }
400
RootsReal(double A,double B,double C,double D,double s[3])401 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
402 #ifdef SK_DEBUG
403 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
404 // create a string mathematica understands
405 // GDB set print repe 15 # if repeated digits is a bother
406 // set print elements 400 # if line doesn't fit
407 char str[1024];
408 sk_bzero(str, sizeof(str));
409 SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
410 A, B, C, D);
411 SkPathOpsDebug::MathematicaIze(str, sizeof(str));
412 SkDebugf("%s\n", str);
413 #endif
414 #endif
415 if (approximately_zero(A)
416 && approximately_zero_when_compared_to(A, B)
417 && approximately_zero_when_compared_to(A, C)
418 && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic
419 return SkDQuad::RootsReal(B, C, D, s);
420 }
421 if (approximately_zero_when_compared_to(D, A)
422 && approximately_zero_when_compared_to(D, B)
423 && approximately_zero_when_compared_to(D, C)) { // 0 is one root
424 int num = SkDQuad::RootsReal(A, B, C, s);
425 for (int i = 0; i < num; ++i) {
426 if (approximately_zero(s[i])) {
427 return num;
428 }
429 }
430 s[num++] = 0;
431 return num;
432 }
433 if (approximately_zero(A + B + C + D)) { // 1 is one root
434 int num = SkDQuad::RootsReal(A, A + B, -D, s);
435 for (int i = 0; i < num; ++i) {
436 if (AlmostDequalUlps(s[i], 1)) {
437 return num;
438 }
439 }
440 s[num++] = 1;
441 return num;
442 }
443 double a, b, c;
444 {
445 double invA = 1 / A;
446 a = B * invA;
447 b = C * invA;
448 c = D * invA;
449 }
450 double a2 = a * a;
451 double Q = (a2 - b * 3) / 9;
452 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
453 double R2 = R * R;
454 double Q3 = Q * Q * Q;
455 double R2MinusQ3 = R2 - Q3;
456 double adiv3 = a / 3;
457 double r;
458 double* roots = s;
459 if (R2MinusQ3 < 0) { // we have 3 real roots
460 // the divide/root can, due to finite precisions, be slightly outside of -1...1
461 double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.));
462 double neg2RootQ = -2 * sqrt(Q);
463
464 r = neg2RootQ * cos(theta / 3) - adiv3;
465 *roots++ = r;
466
467 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
468 if (!AlmostDequalUlps(s[0], r)) {
469 *roots++ = r;
470 }
471 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
472 if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
473 *roots++ = r;
474 }
475 } else { // we have 1 real root
476 double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
477 A = fabs(R) + sqrtR2MinusQ3;
478 A = SkDCubeRoot(A);
479 if (R > 0) {
480 A = -A;
481 }
482 if (A != 0) {
483 A += Q / A;
484 }
485 r = A - adiv3;
486 *roots++ = r;
487 if (AlmostDequalUlps((double) R2, (double) Q3)) {
488 r = -A / 2 - adiv3;
489 if (!AlmostDequalUlps(s[0], r)) {
490 *roots++ = r;
491 }
492 }
493 }
494 return static_cast<int>(roots - s);
495 }
496
497 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const498 SkDVector SkDCubic::dxdyAtT(double t) const {
499 SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
500 if (result.fX == 0 && result.fY == 0) {
501 if (t == 0) {
502 result = fPts[2] - fPts[0];
503 } else if (t == 1) {
504 result = fPts[3] - fPts[1];
505 } else {
506 // incomplete
507 SkDebugf("!c");
508 }
509 if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
510 result = fPts[3] - fPts[0];
511 }
512 }
513 return result;
514 }
515
516 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const517 int SkDCubic::findInflections(double tValues[]) const {
518 double Ax = fPts[1].fX - fPts[0].fX;
519 double Ay = fPts[1].fY - fPts[0].fY;
520 double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
521 double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
522 double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
523 double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
524 return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
525 }
526
formulate_F1DotF2(const double src[],double coeff[4])527 static void formulate_F1DotF2(const double src[], double coeff[4]) {
528 double a = src[2] - src[0];
529 double b = src[4] - 2 * src[2] + src[0];
530 double c = src[6] + 3 * (src[2] - src[4]) - src[0];
531 coeff[0] = c * c;
532 coeff[1] = 3 * b * c;
533 coeff[2] = 2 * b * b + c * a;
534 coeff[3] = a * b;
535 }
536
537 /** SkDCubic'(t) = At^2 + Bt + C, where
538 A = 3(-a + 3(b - c) + d)
539 B = 6(a - 2b + c)
540 C = 3(b - a)
541 Solve for t, keeping only those that fit between 0 < t < 1
542 */
FindExtrema(const double src[],double tValues[2])543 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
544 // we divide A,B,C by 3 to simplify
545 double a = src[0];
546 double b = src[2];
547 double c = src[4];
548 double d = src[6];
549 double A = d - a + 3 * (b - c);
550 double B = 2 * (a - b - b + c);
551 double C = b - a;
552
553 return SkDQuad::RootsValidT(A, B, C, tValues);
554 }
555
556 /* from SkGeometry.cpp
557 Looking for F' dot F'' == 0
558
559 A = b - a
560 B = c - 2b + a
561 C = d - 3c + 3b - a
562
563 F' = 3Ct^2 + 6Bt + 3A
564 F'' = 6Ct + 6B
565
566 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
567 */
findMaxCurvature(double tValues[]) const568 int SkDCubic::findMaxCurvature(double tValues[]) const {
569 double coeffX[4], coeffY[4];
570 int i;
571 formulate_F1DotF2(&fPts[0].fX, coeffX);
572 formulate_F1DotF2(&fPts[0].fY, coeffY);
573 for (i = 0; i < 4; i++) {
574 coeffX[i] = coeffX[i] + coeffY[i];
575 }
576 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
577 }
578
ptAtT(double t) const579 SkDPoint SkDCubic::ptAtT(double t) const {
580 if (0 == t) {
581 return fPts[0];
582 }
583 if (1 == t) {
584 return fPts[3];
585 }
586 double one_t = 1 - t;
587 double one_t2 = one_t * one_t;
588 double a = one_t2 * one_t;
589 double b = 3 * one_t2 * t;
590 double t2 = t * t;
591 double c = 3 * one_t * t2;
592 double d = t2 * t;
593 SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
594 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
595 return result;
596 }
597
598 /*
599 Given a cubic c, t1, and t2, find a small cubic segment.
600
601 The new cubic is defined as points A, B, C, and D, where
602 s1 = 1 - t1
603 s2 = 1 - t2
604 A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
605 D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
606
607 We don't have B or C. So We define two equations to isolate them.
608 First, compute two reference T values 1/3 and 2/3 from t1 to t2:
609
610 c(at (2*t1 + t2)/3) == E
611 c(at (t1 + 2*t2)/3) == F
612
613 Next, compute where those values must be if we know the values of B and C:
614
615 _12 = A*2/3 + B*1/3
616 12_ = A*1/3 + B*2/3
617 _23 = B*2/3 + C*1/3
618 23_ = B*1/3 + C*2/3
619 _34 = C*2/3 + D*1/3
620 34_ = C*1/3 + D*2/3
621 _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
622 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
623 _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
624 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
625 _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
626 = A*8/27 + B*12/27 + C*6/27 + D*1/27
627 = E
628 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
629 = A*1/27 + B*6/27 + C*12/27 + D*8/27
630 = F
631 E*27 = A*8 + B*12 + C*6 + D
632 F*27 = A + B*6 + C*12 + D*8
633
634 Group the known values on one side:
635
636 M = E*27 - A*8 - D = B*12 + C* 6
637 N = F*27 - A - D*8 = B* 6 + C*12
638 M*2 - N = B*18
639 N*2 - M = C*18
640 B = (M*2 - N)/18
641 C = (N*2 - M)/18
642 */
643
interp_cubic_coords(const double * src,double t)644 static double interp_cubic_coords(const double* src, double t) {
645 double ab = SkDInterp(src[0], src[2], t);
646 double bc = SkDInterp(src[2], src[4], t);
647 double cd = SkDInterp(src[4], src[6], t);
648 double abc = SkDInterp(ab, bc, t);
649 double bcd = SkDInterp(bc, cd, t);
650 double abcd = SkDInterp(abc, bcd, t);
651 return abcd;
652 }
653
subDivide(double t1,double t2) const654 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
655 if (t1 == 0 || t2 == 1) {
656 if (t1 == 0 && t2 == 1) {
657 return *this;
658 }
659 SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
660 SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
661 return dst;
662 }
663 SkDCubic dst;
664 double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
665 double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
666 double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
667 double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
668 double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
669 double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
670 double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
671 double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
672 double mx = ex * 27 - ax * 8 - dx;
673 double my = ey * 27 - ay * 8 - dy;
674 double nx = fx * 27 - ax - dx * 8;
675 double ny = fy * 27 - ay - dy * 8;
676 /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
677 /* by = */ dst[1].fY = (my * 2 - ny) / 18;
678 /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
679 /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
680 // FIXME: call align() ?
681 return dst;
682 }
683
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const684 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
685 double t1, double t2, SkDPoint dst[2]) const {
686 SkASSERT(t1 != t2);
687 // this approach assumes that the control points computed directly are accurate enough
688 SkDCubic sub = subDivide(t1, t2);
689 dst[0] = sub[1] + (a - sub[0]);
690 dst[1] = sub[2] + (d - sub[3]);
691 if (t1 == 0 || t2 == 0) {
692 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
693 }
694 if (t1 == 1 || t2 == 1) {
695 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
696 }
697 if (AlmostBequalUlps(dst[0].fX, a.fX)) {
698 dst[0].fX = a.fX;
699 }
700 if (AlmostBequalUlps(dst[0].fY, a.fY)) {
701 dst[0].fY = a.fY;
702 }
703 if (AlmostBequalUlps(dst[1].fX, d.fX)) {
704 dst[1].fX = d.fX;
705 }
706 if (AlmostBequalUlps(dst[1].fY, d.fY)) {
707 dst[1].fY = d.fY;
708 }
709 }
710
toFloatPoints(SkPoint * pts) const711 bool SkDCubic::toFloatPoints(SkPoint* pts) const {
712 const double* dCubic = &fPts[0].fX;
713 SkScalar* cubic = &pts[0].fX;
714 for (int index = 0; index < kPointCount * 2; ++index) {
715 cubic[index] = SkDoubleToScalar(dCubic[index]);
716 if (SkScalarAbs(cubic[index]) < FLT_EPSILON_ORDERABLE_ERR) {
717 cubic[index] = 0;
718 }
719 }
720 return SkScalarsAreFinite(&pts->fX, kPointCount * 2);
721 }
722
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const723 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
724 double extremeTs[2];
725 double topT = -1;
726 int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
727 for (int index = 0; index < roots; ++index) {
728 double t = startT + (endT - startT) * extremeTs[index];
729 SkDPoint mid = dCurve.ptAtT(t);
730 if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
731 topT = t;
732 *topPt = mid;
733 }
734 }
735 return topT;
736 }
737
intersectRay(SkIntersections * i,const SkDLine & line) const738 int SkTCubic::intersectRay(SkIntersections* i, const SkDLine& line) const {
739 return i->intersectRay(fCubic, line);
740 }
741
hullIntersects(const SkDQuad & quad,bool * isLinear) const742 bool SkTCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
743 return quad.hullIntersects(fCubic, isLinear);
744 }
745
hullIntersects(const SkDConic & conic,bool * isLinear) const746 bool SkTCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
747 return conic.hullIntersects(fCubic, isLinear);
748 }
749
setBounds(SkDRect * rect) const750 void SkTCubic::setBounds(SkDRect* rect) const {
751 rect->setBounds(fCubic);
752 }
753