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