Flutter Engine
The Flutter Engine
SkPathOpsCubic.cpp
Go to the documentation of this file.
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 */
8
12#include "src/base/SkTSort.h"
13#include "src/core/SkGeometry.h"
20
21#include <algorithm>
22#include <cmath>
23
24struct SkDLine;
25
26const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in test framework
27
28void 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
39double 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
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 */
94static 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
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
134void 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
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*/
158bool 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 }
183 sign = sign2;
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
207bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
209}
210
211bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
213}
214
215bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
216
217 return hullIntersects(conic.fPts, isLinear);
218}
219
220bool 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);
235 return false;
236 }
237 distance = lineParameters.controlPtDistance(*this, 2);
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
245static 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
254int SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
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
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
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
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
344void 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
351int 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
378static const double PI = 3.141592653589793;
379
380// from SkGeometry.cpp (and Numeric Solutions, 5.6)
381// // TODO(skbug.com/14063) Deduplicate with SkCubics::RootsValidT
382int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
383 double s[3];
384 int realRoots = RootsReal(A, B, C, D, s);
385 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
386 for (int index = 0; index < realRoots; ++index) {
387 double tValue = s[index];
388 if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
389 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
390 if (approximately_equal(t[idx2], 1)) {
391 goto nextRoot;
392 }
393 }
394 SkASSERT(foundRoots < 3);
395 t[foundRoots++] = 1;
396 } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
397 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
398 if (approximately_equal(t[idx2], 0)) {
399 goto nextRoot;
400 }
401 }
402 SkASSERT(foundRoots < 3);
403 t[foundRoots++] = 0;
404 }
405nextRoot:
406 ;
407 }
408 return foundRoots;
409}
410
411// TODO(skbug.com/14063) Deduplicate with SkCubics::RootsReal
412int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
413#ifdef SK_DEBUG
414 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
415 // create a string mathematica understands
416 // GDB set print repe 15 # if repeated digits is a bother
417 // set print elements 400 # if line doesn't fit
418 char str[1024];
419 sk_bzero(str, sizeof(str));
420 snprintf(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
421 A, B, C, D);
422 SkPathOpsDebug::MathematicaIze(str, sizeof(str));
423 SkDebugf("%s\n", str);
424 #endif
425#endif
429 && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic
430 return SkDQuad::RootsReal(B, C, D, s);
431 }
434 && approximately_zero_when_compared_to(D, C)) { // 0 is one root
435 int num = SkDQuad::RootsReal(A, B, C, s);
436 for (int i = 0; i < num; ++i) {
437 if (approximately_zero(s[i])) {
438 return num;
439 }
440 }
441 s[num++] = 0;
442 return num;
443 }
444 if (approximately_zero(A + B + C + D)) { // 1 is one root
445 int num = SkDQuad::RootsReal(A, A + B, -D, s);
446 for (int i = 0; i < num; ++i) {
447 if (AlmostDequalUlps(s[i], 1)) {
448 return num;
449 }
450 }
451 s[num++] = 1;
452 return num;
453 }
454 double a, b, c;
455 {
456 double invA = 1 / A;
457 a = B * invA;
458 b = C * invA;
459 c = D * invA;
460 }
461 double a2 = a * a;
462 double Q = (a2 - b * 3) / 9;
463 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
464 double R2 = R * R;
465 double Q3 = Q * Q * Q;
466 double R2MinusQ3 = R2 - Q3;
467 double adiv3 = a / 3;
468 double r;
469 double* roots = s;
470 if (R2MinusQ3 < 0) { // we have 3 real roots
471 // the divide/root can, due to finite precisions, be slightly outside of -1...1
472 double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.));
473 double neg2RootQ = -2 * sqrt(Q);
474
475 r = neg2RootQ * cos(theta / 3) - adiv3;
476 *roots++ = r;
477
478 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
479 if (!AlmostDequalUlps(s[0], r)) {
480 *roots++ = r;
481 }
482 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
483 if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
484 *roots++ = r;
485 }
486 } else { // we have 1 real root
487 double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
488 A = fabs(R) + sqrtR2MinusQ3;
489 A = std::cbrt(A); // cube root
490 if (R > 0) {
491 A = -A;
492 }
493 if (A != 0) {
494 A += Q / A;
495 }
496 r = A - adiv3;
497 *roots++ = r;
498 if (AlmostDequalUlps((double) R2, (double) Q3)) {
499 r = -A / 2 - adiv3;
500 if (!AlmostDequalUlps(s[0], r)) {
501 *roots++ = r;
502 }
503 }
504 }
505 return static_cast<int>(roots - s);
506}
507
508// OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
510 SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
511 if (result.fX == 0 && result.fY == 0) {
512 if (t == 0) {
513 result = fPts[2] - fPts[0];
514 } else if (t == 1) {
515 result = fPts[3] - fPts[1];
516 } else {
517 // incomplete
518 SkDebugf("!c");
519 }
520 if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
521 result = fPts[3] - fPts[0];
522 }
523 }
524 return result;
525}
526
527// OPTIMIZE? share code with formulate_F1DotF2
528// e.g. https://stackoverflow.com/a/35927917
529int SkDCubic::findInflections(double tValues[2]) const {
530 double Ax = fPts[1].fX - fPts[0].fX;
531 double Ay = fPts[1].fY - fPts[0].fY;
532 double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
533 double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
534 double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
535 double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
536 return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
537}
538
539static void formulate_F1DotF2(const double src[], double coeff[4]) {
540 double a = src[2] - src[0];
541 double b = src[4] - 2 * src[2] + src[0];
542 double c = src[6] + 3 * (src[2] - src[4]) - src[0];
543 coeff[0] = c * c;
544 coeff[1] = 3 * b * c;
545 coeff[2] = 2 * b * b + c * a;
546 coeff[3] = a * b;
547}
548
549/** SkDCubic'(t) = At^2 + Bt + C, where
550 A = 3(-a + 3(b - c) + d)
551 B = 6(a - 2b + c)
552 C = 3(b - a)
553 Solve for t, keeping only those that fit between 0 < t < 1
554*/
555int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
556 // we divide A,B,C by 3 to simplify
557 double a = src[0];
558 double b = src[2];
559 double c = src[4];
560 double d = src[6];
561 double A = d - a + 3 * (b - c);
562 double B = 2 * (a - b - b + c);
563 double C = b - a;
564
565 return SkDQuad::RootsValidT(A, B, C, tValues);
566}
567
568/* from SkGeometry.cpp
569 Looking for F' dot F'' == 0
570
571 A = b - a
572 B = c - 2b + a
573 C = d - 3c + 3b - a
574
575 F' = 3Ct^2 + 6Bt + 3A
576 F'' = 6Ct + 6B
577
578 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
579*/
580int SkDCubic::findMaxCurvature(double tValues[]) const {
581 double coeffX[4], coeffY[4];
582 int i;
583 formulate_F1DotF2(&fPts[0].fX, coeffX);
584 formulate_F1DotF2(&fPts[0].fY, coeffY);
585 for (i = 0; i < 4; i++) {
586 coeffX[i] = coeffX[i] + coeffY[i];
587 }
588 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
589}
590
591SkDPoint SkDCubic::ptAtT(double t) const {
592 if (0 == t) {
593 return fPts[0];
594 }
595 if (1 == t) {
596 return fPts[3];
597 }
598 double one_t = 1 - t;
599 double one_t2 = one_t * one_t;
600 double a = one_t2 * one_t;
601 double b = 3 * one_t2 * t;
602 double t2 = t * t;
603 double c = 3 * one_t * t2;
604 double d = t2 * t;
605 SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
606 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
607 return result;
608}
609
610/*
611 Given a cubic c, t1, and t2, find a small cubic segment.
612
613 The new cubic is defined as points A, B, C, and D, where
614 s1 = 1 - t1
615 s2 = 1 - t2
616 A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
617 D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
618
619 We don't have B or C. So We define two equations to isolate them.
620 First, compute two reference T values 1/3 and 2/3 from t1 to t2:
621
622 c(at (2*t1 + t2)/3) == E
623 c(at (t1 + 2*t2)/3) == F
624
625 Next, compute where those values must be if we know the values of B and C:
626
627 _12 = A*2/3 + B*1/3
628 12_ = A*1/3 + B*2/3
629 _23 = B*2/3 + C*1/3
630 23_ = B*1/3 + C*2/3
631 _34 = C*2/3 + D*1/3
632 34_ = C*1/3 + D*2/3
633 _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
634 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
635 _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
636 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
637 _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
638 = A*8/27 + B*12/27 + C*6/27 + D*1/27
639 = E
640 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
641 = A*1/27 + B*6/27 + C*12/27 + D*8/27
642 = F
643 E*27 = A*8 + B*12 + C*6 + D
644 F*27 = A + B*6 + C*12 + D*8
645
646Group the known values on one side:
647
648 M = E*27 - A*8 - D = B*12 + C* 6
649 N = F*27 - A - D*8 = B* 6 + C*12
650 M*2 - N = B*18
651 N*2 - M = C*18
652 B = (M*2 - N)/18
653 C = (N*2 - M)/18
654 */
655
656static double interp_cubic_coords(const double* src, double t) {
657 double ab = SkDInterp(src[0], src[2], t);
658 double bc = SkDInterp(src[2], src[4], t);
659 double cd = SkDInterp(src[4], src[6], t);
660 double abc = SkDInterp(ab, bc, t);
661 double bcd = SkDInterp(bc, cd, t);
662 double abcd = SkDInterp(abc, bcd, t);
663 return abcd;
664}
665
666SkDCubic SkDCubic::subDivide(double t1, double t2) const {
667 if (t1 == 0 || t2 == 1) {
668 if (t1 == 0 && t2 == 1) {
669 return *this;
670 }
671 SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
672 SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
673 return dst;
674 }
676 double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
677 double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
678 double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
679 double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
680 double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
681 double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
682 double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
683 double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
684 double mx = ex * 27 - ax * 8 - dx;
685 double my = ey * 27 - ay * 8 - dy;
686 double nx = fx * 27 - ax - dx * 8;
687 double ny = fy * 27 - ay - dy * 8;
688 /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
689 /* by = */ dst[1].fY = (my * 2 - ny) / 18;
690 /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
691 /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
692 // FIXME: call align() ?
693 return dst;
694}
695
697 double t1, double t2, SkDPoint dst[2]) const {
698 SkASSERT(t1 != t2);
699 // this approach assumes that the control points computed directly are accurate enough
700 SkDCubic sub = subDivide(t1, t2);
701 dst[0] = sub[1] + (a - sub[0]);
702 dst[1] = sub[2] + (d - sub[3]);
703 if (t1 == 0 || t2 == 0) {
704 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
705 }
706 if (t1 == 1 || t2 == 1) {
707 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
708 }
709 if (AlmostBequalUlps(dst[0].fX, a.fX)) {
710 dst[0].fX = a.fX;
711 }
712 if (AlmostBequalUlps(dst[0].fY, a.fY)) {
713 dst[0].fY = a.fY;
714 }
715 if (AlmostBequalUlps(dst[1].fX, d.fX)) {
716 dst[1].fX = d.fX;
717 }
718 if (AlmostBequalUlps(dst[1].fY, d.fY)) {
719 dst[1].fY = d.fY;
720 }
721}
722
724 const double* dCubic = &fPts[0].fX;
725 SkScalar* cubic = &pts[0].fX;
726 for (int index = 0; index < kPointCount * 2; ++index) {
727 cubic[index] = SkDoubleToScalar(dCubic[index]);
729 cubic[index] = 0;
730 }
731 }
732 return SkIsFinite(&pts->fX, kPointCount * 2);
733}
734
735double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
736 double extremeTs[2];
737 double topT = -1;
738 int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
739 for (int index = 0; index < roots; ++index) {
740 double t = startT + (endT - startT) * extremeTs[index];
741 SkDPoint mid = dCurve.ptAtT(t);
742 if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
743 topT = t;
744 *topPt = mid;
745 }
746 }
747 return topT;
748}
749
751 return i->intersectRay(fCubic, line);
752}
753
754bool SkTCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
755 return quad.hullIntersects(fCubic, isLinear);
756}
757
758bool SkTCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
759 return conic.hullIntersects(fCubic, isLinear);
760}
761
763 rect->setBounds(fCubic);
764}
static int step(int x, SkScalar min, SkScalar max)
Definition: BlurTest.cpp:215
sk_bzero(glyphs, sizeof(glyphs))
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool approximately_zero(double x)
Definition: SkCubics.cpp:153
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
SkCubicType SkClassifyCubic(const SkPoint P[4], double t[2], double s[2], double d[4])
Definition: SkGeometry.cpp:809
SkCubicType
Definition: SkGeometry.h:264
static double derivative_at_t(const double *src, double t)
static const double PI
static void formulate_F1DotF2(const double src[], double coeff[4])
static void interp_cubic_coords(const double *src, double *dst, double t)
int other_two(int one, int two)
bool AlmostBequalUlps(float a, float b)
bool AlmostDequalUlps(float a, float b)
bool approximately_equal(double x, double y)
bool precisely_zero(double x)
bool approximately_one_or_less(double x)
bool roughly_between(double a, double b, double c)
bool approximately_zero_or_more(double x)
const double FLT_EPSILON_ORDERABLE_ERR
bool between(double a, double b, double c)
bool precisely_between(double a, double b, double c)
bool approximately_equal_half(double x, double y)
double SkDInterp(double A, double B, double t)
bool approximately_zero_when_compared_to(double x, double y)
bool zero_or_one(double x)
static int sign(SkScalar x)
Definition: SkPath.cpp:2205
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
#define SkScalarAbs(x)
Definition: SkScalar.h:39
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
void SkTQSort(T *begin, T *end, const C &lessThan)
Definition: SkTSort.h:194
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
bool cubicEndPoints(const SkDCubic &pts)
double controlPtDistance(const SkDCubic &pts, int index) const
static void MathematicaIze(char *str, size_t bufferSize)
bool hullIntersects(const SkDQuad &quad, bool *isLinear) const override
int intersectRay(SkIntersections *i, const SkDLine &line) const override
SkDCubic fCubic
void setBounds(SkDRect *) const override
#define C(TEST_CATEGORY)
Definition: colrv1.cpp:248
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
#define R(r)
#define B
size_t length
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
Definition: ab.py:1
const CatchEntryMove ab[]
dst
Definition: cp.py:12
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:287
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
SeparatedVector2 offset
SkDCubic second() const
SkDCubic first() const
int findMaxCurvature(double tValues[]) const
int convexHull(char order[kPointCount]) const
bool hullIntersects(const SkDCubic &c2, bool *isLinear) const
bool toFloatPoints(SkPoint *) const
bool isLinear(int startIndex, int endIndex) const
SkDCubicPair chopAt(double t) const
static int RootsReal(double A, double B, double C, double D, double t[3])
double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const
int findInflections(double tValues[2]) const
bool monotonicInX() const
bool endsAreExtremaInXOrY() const
void align(int endIndex, int ctrlIndex, SkDPoint *dstPt) const
double calcPrecision() const
SkDPoint fPts[kPointCount]
bool monotonicInY() const
static const int gPrecisionUnit
static int FindExtrema(const double src[], double tValue[2])
double top(const SkDCubic &dCurve, double startT, double endT, SkDPoint *topPt) const
static void Coefficients(const double *cubic, double *A, double *B, double *C, double *D)
int searchRoots(double extremes[6], int extrema, double axisIntercept, SearchAxis xAxis, double *validRoots) const
static int RootsValidT(const double A, const double B, const double C, double D, double s[3])
SkDVector dxdyAtT(double t) const
SkDPoint ptAtT(double t) const
void otherPts(int index, const SkDPoint *o1Pts[kPointCount - 1]) const
static const int kPointCount
SkDCubic subDivide(double t1, double t2) const
static int ComplexBreak(const SkPoint pts[4], SkScalar *t)
void dump() const
static int RootsValidT(const double A, const double B, const double C, double s[2])
static int RootsReal(double A, double B, double C, double t[2])
static int AddValidTs(double s[], int realRoots, double *t)
bool hullIntersects(const SkDQuad &, bool *isLinear) const
SkDPoint fPts[kPointCount]
Definition: SkPathOpsQuad.h:39
static const int kPointCount
Definition: SkPathOpsQuad.h:35
double length() const
float fX
x-axis value
Definition: SkPoint_impl.h:164
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)