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 19 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 30 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 SkOPASSERT(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 // get the rough scale of the cubic; used to determine if curvature is extreme 79 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 */ 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 103 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 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 135 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 */ 149 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 198 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const { 199 return hullIntersects(c2.fPts, c2.kPointCount, isLinear); 200 } 201 202 bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const { 203 return hullIntersects(quad.fPts, quad.kPointCount, isLinear); 204 } 205 206 bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const { 207 208 return hullIntersects(conic.fPts, isLinear); 209 } 210 211 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 = 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 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 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 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 SkScalar d[3]; 252 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); 253 switch (cubicType) { 254 case kLoop_SkCubicType: { 255 // crib code from gpu path utils that finds t values where loop self-intersects 256 // use it to find mid of t values which should be a friendly place to chop 257 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); 258 SkScalar ls = d[1] - tempSqrt; 259 SkScalar lt = 2.f * d[0]; 260 SkScalar ms = d[1] + tempSqrt; 261 SkScalar mt = 2.f * d[0]; 262 if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) { 263 ls = ls / lt; 264 ms = ms / mt; 265 SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1)); 266 t[0] = (ls + ms) / 2; 267 SkASSERT(roughly_between(0, *t, 1)); 268 return (int) (t[0] > 0 && t[0] < 1); 269 } 270 } 271 // fall through if no t value found 272 case kSerpentine_SkCubicType: 273 case kCusp_SkCubicType: { 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 } 327 default: 328 ; 329 } 330 return 0; 331 } 332 333 bool SkDCubic::monotonicInX() const { 334 return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX) 335 && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX); 336 } 337 338 bool SkDCubic::monotonicInY() const { 339 return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY) 340 && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY); 341 } 342 343 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const { 344 int offset = (int) !SkToBool(index); 345 o1Pts[0] = &fPts[offset]; 346 o1Pts[1] = &fPts[++offset]; 347 o1Pts[2] = &fPts[++offset]; 348 } 349 350 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept, 351 SearchAxis xAxis, double* validRoots) const { 352 extrema += findInflections(&extremeTs[extrema]); 353 extremeTs[extrema++] = 0; 354 extremeTs[extrema] = 1; 355 SkASSERT(extrema < 6); 356 SkTQSort(extremeTs, extremeTs + extrema); 357 int validCount = 0; 358 for (int index = 0; index < extrema; ) { 359 double min = extremeTs[index]; 360 double max = extremeTs[++index]; 361 if (min == max) { 362 continue; 363 } 364 double newT = binarySearch(min, max, axisIntercept, xAxis); 365 if (newT >= 0) { 366 if (validCount >= 3) { 367 return 0; 368 } 369 validRoots[validCount++] = newT; 370 } 371 } 372 return validCount; 373 } 374 375 // cubic roots 376 377 static const double PI = 3.141592653589793; 378 379 // from SkGeometry.cpp (and Numeric Solutions, 5.6) 380 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 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { 410 #ifdef SK_DEBUG 411 // create a string mathematica understands 412 // GDB set print repe 15 # if repeated digits is a bother 413 // set print elements 400 # if line doesn't fit 414 char str[1024]; 415 sk_bzero(str, sizeof(str)); 416 SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", 417 A, B, C, D); 418 SkPathOpsDebug::MathematicaIze(str, sizeof(str)); 419 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA 420 SkDebugf("%s\n", str); 421 #endif 422 #endif 423 if (approximately_zero(A) 424 && approximately_zero_when_compared_to(A, B) 425 && approximately_zero_when_compared_to(A, C) 426 && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic 427 return SkDQuad::RootsReal(B, C, D, s); 428 } 429 if (approximately_zero_when_compared_to(D, A) 430 && approximately_zero_when_compared_to(D, B) 431 && approximately_zero_when_compared_to(D, C)) { // 0 is one root 432 int num = SkDQuad::RootsReal(A, B, C, s); 433 for (int i = 0; i < num; ++i) { 434 if (approximately_zero(s[i])) { 435 return num; 436 } 437 } 438 s[num++] = 0; 439 return num; 440 } 441 if (approximately_zero(A + B + C + D)) { // 1 is one root 442 int num = SkDQuad::RootsReal(A, A + B, -D, s); 443 for (int i = 0; i < num; ++i) { 444 if (AlmostDequalUlps(s[i], 1)) { 445 return num; 446 } 447 } 448 s[num++] = 1; 449 return num; 450 } 451 double a, b, c; 452 { 453 double invA = 1 / A; 454 a = B * invA; 455 b = C * invA; 456 c = D * invA; 457 } 458 double a2 = a * a; 459 double Q = (a2 - b * 3) / 9; 460 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; 461 double R2 = R * R; 462 double Q3 = Q * Q * Q; 463 double R2MinusQ3 = R2 - Q3; 464 double adiv3 = a / 3; 465 double r; 466 double* roots = s; 467 if (R2MinusQ3 < 0) { // we have 3 real roots 468 // the divide/root can, due to finite precisions, be slightly outside of -1...1 469 double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.)); 470 double neg2RootQ = -2 * sqrt(Q); 471 472 r = neg2RootQ * cos(theta / 3) - adiv3; 473 *roots++ = r; 474 475 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; 476 if (!AlmostDequalUlps(s[0], r)) { 477 *roots++ = r; 478 } 479 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; 480 if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) { 481 *roots++ = r; 482 } 483 } else { // we have 1 real root 484 double sqrtR2MinusQ3 = sqrt(R2MinusQ3); 485 double A = fabs(R) + sqrtR2MinusQ3; 486 A = SkDCubeRoot(A); 487 if (R > 0) { 488 A = -A; 489 } 490 if (A != 0) { 491 A += Q / A; 492 } 493 r = A - adiv3; 494 *roots++ = r; 495 if (AlmostDequalUlps((double) R2, (double) Q3)) { 496 r = -A / 2 - adiv3; 497 if (!AlmostDequalUlps(s[0], r)) { 498 *roots++ = r; 499 } 500 } 501 } 502 return static_cast<int>(roots - s); 503 } 504 505 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t? 506 SkDVector SkDCubic::dxdyAtT(double t) const { 507 SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) }; 508 if (result.fX == 0 && result.fY == 0) { 509 if (t == 0) { 510 result = fPts[2] - fPts[0]; 511 } else if (t == 1) { 512 result = fPts[3] - fPts[1]; 513 } else { 514 // incomplete 515 SkDebugf("!c"); 516 } 517 if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) { 518 result = fPts[3] - fPts[0]; 519 } 520 } 521 return result; 522 } 523 524 // OPTIMIZE? share code with formulate_F1DotF2 525 int SkDCubic::findInflections(double tValues[]) const { 526 double Ax = fPts[1].fX - fPts[0].fX; 527 double Ay = fPts[1].fY - fPts[0].fY; 528 double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX; 529 double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY; 530 double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX; 531 double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY; 532 return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues); 533 } 534 535 static void formulate_F1DotF2(const double src[], double coeff[4]) { 536 double a = src[2] - src[0]; 537 double b = src[4] - 2 * src[2] + src[0]; 538 double c = src[6] + 3 * (src[2] - src[4]) - src[0]; 539 coeff[0] = c * c; 540 coeff[1] = 3 * b * c; 541 coeff[2] = 2 * b * b + c * a; 542 coeff[3] = a * b; 543 } 544 545 /** SkDCubic'(t) = At^2 + Bt + C, where 546 A = 3(-a + 3(b - c) + d) 547 B = 6(a - 2b + c) 548 C = 3(b - a) 549 Solve for t, keeping only those that fit between 0 < t < 1 550 */ 551 int SkDCubic::FindExtrema(const double src[], double tValues[2]) { 552 // we divide A,B,C by 3 to simplify 553 double a = src[0]; 554 double b = src[2]; 555 double c = src[4]; 556 double d = src[6]; 557 double A = d - a + 3 * (b - c); 558 double B = 2 * (a - b - b + c); 559 double C = b - a; 560 561 return SkDQuad::RootsValidT(A, B, C, tValues); 562 } 563 564 /* from SkGeometry.cpp 565 Looking for F' dot F'' == 0 566 567 A = b - a 568 B = c - 2b + a 569 C = d - 3c + 3b - a 570 571 F' = 3Ct^2 + 6Bt + 3A 572 F'' = 6Ct + 6B 573 574 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB 575 */ 576 int SkDCubic::findMaxCurvature(double tValues[]) const { 577 double coeffX[4], coeffY[4]; 578 int i; 579 formulate_F1DotF2(&fPts[0].fX, coeffX); 580 formulate_F1DotF2(&fPts[0].fY, coeffY); 581 for (i = 0; i < 4; i++) { 582 coeffX[i] = coeffX[i] + coeffY[i]; 583 } 584 return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); 585 } 586 587 SkDPoint SkDCubic::ptAtT(double t) const { 588 if (0 == t) { 589 return fPts[0]; 590 } 591 if (1 == t) { 592 return fPts[3]; 593 } 594 double one_t = 1 - t; 595 double one_t2 = one_t * one_t; 596 double a = one_t2 * one_t; 597 double b = 3 * one_t2 * t; 598 double t2 = t * t; 599 double c = 3 * one_t * t2; 600 double d = t2 * t; 601 SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX, 602 a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY}; 603 return result; 604 } 605 606 /* 607 Given a cubic c, t1, and t2, find a small cubic segment. 608 609 The new cubic is defined as points A, B, C, and D, where 610 s1 = 1 - t1 611 s2 = 1 - t2 612 A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1 613 D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2 614 615 We don't have B or C. So We define two equations to isolate them. 616 First, compute two reference T values 1/3 and 2/3 from t1 to t2: 617 618 c(at (2*t1 + t2)/3) == E 619 c(at (t1 + 2*t2)/3) == F 620 621 Next, compute where those values must be if we know the values of B and C: 622 623 _12 = A*2/3 + B*1/3 624 12_ = A*1/3 + B*2/3 625 _23 = B*2/3 + C*1/3 626 23_ = B*1/3 + C*2/3 627 _34 = C*2/3 + D*1/3 628 34_ = C*1/3 + D*2/3 629 _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 630 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 631 _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 632 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 633 _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3 634 = A*8/27 + B*12/27 + C*6/27 + D*1/27 635 = E 636 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3 637 = A*1/27 + B*6/27 + C*12/27 + D*8/27 638 = F 639 E*27 = A*8 + B*12 + C*6 + D 640 F*27 = A + B*6 + C*12 + D*8 641 642 Group the known values on one side: 643 644 M = E*27 - A*8 - D = B*12 + C* 6 645 N = F*27 - A - D*8 = B* 6 + C*12 646 M*2 - N = B*18 647 N*2 - M = C*18 648 B = (M*2 - N)/18 649 C = (N*2 - M)/18 650 */ 651 652 static double interp_cubic_coords(const double* src, double t) { 653 double ab = SkDInterp(src[0], src[2], t); 654 double bc = SkDInterp(src[2], src[4], t); 655 double cd = SkDInterp(src[4], src[6], t); 656 double abc = SkDInterp(ab, bc, t); 657 double bcd = SkDInterp(bc, cd, t); 658 double abcd = SkDInterp(abc, bcd, t); 659 return abcd; 660 } 661 662 SkDCubic SkDCubic::subDivide(double t1, double t2) const { 663 if (t1 == 0 || t2 == 1) { 664 if (t1 == 0 && t2 == 1) { 665 return *this; 666 } 667 SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1); 668 SkDCubic dst = t1 == 0 ? pair.first() : pair.second(); 669 return dst; 670 } 671 SkDCubic dst; 672 double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1); 673 double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1); 674 double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3); 675 double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3); 676 double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3); 677 double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3); 678 double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2); 679 double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2); 680 double mx = ex * 27 - ax * 8 - dx; 681 double my = ey * 27 - ay * 8 - dy; 682 double nx = fx * 27 - ax - dx * 8; 683 double ny = fy * 27 - ay - dy * 8; 684 /* bx = */ dst[1].fX = (mx * 2 - nx) / 18; 685 /* by = */ dst[1].fY = (my * 2 - ny) / 18; 686 /* cx = */ dst[2].fX = (nx * 2 - mx) / 18; 687 /* cy = */ dst[2].fY = (ny * 2 - my) / 18; 688 // FIXME: call align() ? 689 return dst; 690 } 691 692 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, 693 double t1, double t2, SkDPoint dst[2]) const { 694 SkASSERT(t1 != t2); 695 // this approach assumes that the control points computed directly are accurate enough 696 SkDCubic sub = subDivide(t1, t2); 697 dst[0] = sub[1] + (a - sub[0]); 698 dst[1] = sub[2] + (d - sub[3]); 699 if (t1 == 0 || t2 == 0) { 700 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); 701 } 702 if (t1 == 1 || t2 == 1) { 703 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); 704 } 705 if (AlmostBequalUlps(dst[0].fX, a.fX)) { 706 dst[0].fX = a.fX; 707 } 708 if (AlmostBequalUlps(dst[0].fY, a.fY)) { 709 dst[0].fY = a.fY; 710 } 711 if (AlmostBequalUlps(dst[1].fX, d.fX)) { 712 dst[1].fX = d.fX; 713 } 714 if (AlmostBequalUlps(dst[1].fY, d.fY)) { 715 dst[1].fY = d.fY; 716 } 717 } 718 719 bool SkDCubic::toFloatPoints(SkPoint* pts) const { 720 const double* dCubic = &fPts[0].fX; 721 SkScalar* cubic = &pts[0].fX; 722 for (int index = 0; index < kPointCount * 2; ++index) { 723 *cubic++ = SkDoubleToScalar(*dCubic++); 724 } 725 return SkScalarsAreFinite(&pts->fX, kPointCount * 2); 726 } 727 728 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const { 729 double extremeTs[2]; 730 double topT = -1; 731 int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs); 732 for (int index = 0; index < roots; ++index) { 733 double t = startT + (endT - startT) * extremeTs[index]; 734 SkDPoint mid = dCurve.ptAtT(t); 735 if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) { 736 topT = t; 737 *topPt = mid; 738 } 739 } 740 return topT; 741 } 742