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