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