1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2013 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/SkTArray.h"
8cb93a386Sopenharmony_ci#include "include/utils/SkRandom.h"
9cb93a386Sopenharmony_ci#include "src/core/SkTSort.h"
10cb93a386Sopenharmony_ci#include "src/pathops/SkIntersections.h"
11cb93a386Sopenharmony_ci#include "src/pathops/SkOpContour.h"
12cb93a386Sopenharmony_ci#include "src/pathops/SkOpSegment.h"
13cb93a386Sopenharmony_ci#include "tests/PathOpsTestCommon.h"
14cb93a386Sopenharmony_ci#include "tests/Test.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_cistatic bool gPathOpsAngleIdeasVerbose = false;
17cb93a386Sopenharmony_cistatic bool gPathOpsAngleIdeasEnableBruteCheck = false;
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ciclass PathOpsAngleTester {
20cb93a386Sopenharmony_cipublic:
21cb93a386Sopenharmony_ci    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
22cb93a386Sopenharmony_ci        return lh.convexHullOverlaps(&rh);
23cb93a386Sopenharmony_ci    }
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
26cb93a386Sopenharmony_ci        return lh.endsIntersect(&rh);
27cb93a386Sopenharmony_ci    }
28cb93a386Sopenharmony_ci};
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_cistruct TRange {
31cb93a386Sopenharmony_ci    double tMin1;
32cb93a386Sopenharmony_ci    double tMin2;
33cb93a386Sopenharmony_ci    double t1;
34cb93a386Sopenharmony_ci    double t2;
35cb93a386Sopenharmony_ci    double tMin;
36cb93a386Sopenharmony_ci    double a1;
37cb93a386Sopenharmony_ci    double a2;
38cb93a386Sopenharmony_ci    bool ccw;
39cb93a386Sopenharmony_ci};
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_cistatic double testArc(skiatest::Reporter* reporter, const SkDQuad& quad, const SkDQuad& arcRef,
42cb93a386Sopenharmony_ci        int octant) {
43cb93a386Sopenharmony_ci    SkDQuad arc = arcRef;
44cb93a386Sopenharmony_ci    SkDVector offset = {quad[0].fX, quad[0].fY};
45cb93a386Sopenharmony_ci    arc[0] += offset;
46cb93a386Sopenharmony_ci    arc[1] += offset;
47cb93a386Sopenharmony_ci    arc[2] += offset;
48cb93a386Sopenharmony_ci    SkIntersections i;
49cb93a386Sopenharmony_ci    i.intersect(arc, quad);
50cb93a386Sopenharmony_ci    if (i.used() == 0) {
51cb93a386Sopenharmony_ci        return -1;
52cb93a386Sopenharmony_ci    }
53cb93a386Sopenharmony_ci    int smallest = -1;
54cb93a386Sopenharmony_ci    double t = 2;
55cb93a386Sopenharmony_ci    for (int idx = 0; idx < i.used(); ++idx) {
56cb93a386Sopenharmony_ci        if (i[0][idx] > 1 || i[0][idx] < 0) {
57cb93a386Sopenharmony_ci            i.reset();
58cb93a386Sopenharmony_ci            i.intersect(arc, quad);
59cb93a386Sopenharmony_ci        }
60cb93a386Sopenharmony_ci        if (i[1][idx] > 1 || i[1][idx] < 0) {
61cb93a386Sopenharmony_ci            i.reset();
62cb93a386Sopenharmony_ci            i.intersect(arc, quad);
63cb93a386Sopenharmony_ci        }
64cb93a386Sopenharmony_ci        if (t > i[1][idx]) {
65cb93a386Sopenharmony_ci            smallest = idx;
66cb93a386Sopenharmony_ci            t = i[1][idx];
67cb93a386Sopenharmony_ci        }
68cb93a386Sopenharmony_ci    }
69cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, smallest >= 0);
70cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, t >= 0 && t <= 1);
71cb93a386Sopenharmony_ci    return i[1][smallest];
72cb93a386Sopenharmony_ci}
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_cistatic void orderQuads(skiatest::Reporter* reporter, const SkDQuad& quad, double radius,
75cb93a386Sopenharmony_ci        SkTArray<double, false>* tArray) {
76cb93a386Sopenharmony_ci    double r = radius;
77cb93a386Sopenharmony_ci    double s = r * SK_ScalarTanPIOver8;
78cb93a386Sopenharmony_ci    double m = r * SK_ScalarRoot2Over2;
79cb93a386Sopenharmony_ci    // construct circle from quads
80cb93a386Sopenharmony_ci    const QuadPts circle[8] = {{{{ r,  0}, { r, -s}, { m, -m}}},
81cb93a386Sopenharmony_ci                                {{{ m, -m}, { s, -r}, { 0, -r}}},
82cb93a386Sopenharmony_ci                                {{{ 0, -r}, {-s, -r}, {-m, -m}}},
83cb93a386Sopenharmony_ci                                {{{-m, -m}, {-r, -s}, {-r,  0}}},
84cb93a386Sopenharmony_ci                                {{{-r,  0}, {-r,  s}, {-m,  m}}},
85cb93a386Sopenharmony_ci                                {{{-m,  m}, {-s,  r}, { 0,  r}}},
86cb93a386Sopenharmony_ci                                {{{ 0,  r}, { s,  r}, { m,  m}}},
87cb93a386Sopenharmony_ci                                {{{ m,  m}, { r,  s}, { r,  0}}}};
88cb93a386Sopenharmony_ci    for (int octant = 0; octant < 8; ++octant) {
89cb93a386Sopenharmony_ci        SkDQuad cQuad;
90cb93a386Sopenharmony_ci        cQuad.debugSet(circle[octant].fPts);
91cb93a386Sopenharmony_ci        double t = testArc(reporter, quad, cQuad, octant);
92cb93a386Sopenharmony_ci        if (t < 0) {
93cb93a386Sopenharmony_ci            continue;
94cb93a386Sopenharmony_ci        }
95cb93a386Sopenharmony_ci        for (int index = 0; index < tArray->count(); ++index) {
96cb93a386Sopenharmony_ci            double matchT = (*tArray)[index];
97cb93a386Sopenharmony_ci            if (approximately_equal(t, matchT)) {
98cb93a386Sopenharmony_ci                goto next;
99cb93a386Sopenharmony_ci            }
100cb93a386Sopenharmony_ci        }
101cb93a386Sopenharmony_ci        tArray->push_back(t);
102cb93a386Sopenharmony_cinext:   ;
103cb93a386Sopenharmony_ci    }
104cb93a386Sopenharmony_ci}
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_cistatic double quadAngle(skiatest::Reporter* reporter, const SkDQuad& quad, double t) {
107cb93a386Sopenharmony_ci    const SkDVector& pt = quad.ptAtT(t) - quad[0];
108cb93a386Sopenharmony_ci    double angle = (atan2(pt.fY, pt.fX) + SK_ScalarPI) * 8 / (SK_ScalarPI * 2);
109cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, angle >= 0 && angle <= 8);
110cb93a386Sopenharmony_ci    return angle;
111cb93a386Sopenharmony_ci}
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_cistatic bool angleDirection(double a1, double a2) {
114cb93a386Sopenharmony_ci    double delta = a1 - a2;
115cb93a386Sopenharmony_ci    return (delta < 4 && delta > 0) || delta < -4;
116cb93a386Sopenharmony_ci}
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_cistatic void setQuadHullSweep(const SkDQuad& quad, SkDVector sweep[2]) {
119cb93a386Sopenharmony_ci    sweep[0] = quad[1] - quad[0];
120cb93a386Sopenharmony_ci    sweep[1] = quad[2] - quad[0];
121cb93a386Sopenharmony_ci}
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_cistatic double distEndRatio(double dist, const SkDQuad& quad) {
124cb93a386Sopenharmony_ci    SkDVector v[] = {quad[2] - quad[0], quad[1] - quad[0], quad[2] - quad[1]};
125cb93a386Sopenharmony_ci    double longest = std::max(v[0].length(), std::max(v[1].length(), v[2].length()));
126cb93a386Sopenharmony_ci    return longest / dist;
127cb93a386Sopenharmony_ci}
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_cistatic bool checkParallel(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2) {
130cb93a386Sopenharmony_ci    SkDVector sweep[2], tweep[2];
131cb93a386Sopenharmony_ci    setQuadHullSweep(quad1, sweep);
132cb93a386Sopenharmony_ci    setQuadHullSweep(quad2, tweep);
133cb93a386Sopenharmony_ci    // if the ctrl tangents are not nearly parallel, use them
134cb93a386Sopenharmony_ci    // solve for opposite direction displacement scale factor == m
135cb93a386Sopenharmony_ci    // initial dir = v1.cross(v2) == v2.x * v1.y - v2.y * v1.x
136cb93a386Sopenharmony_ci    // displacement of q1[1] : dq1 = { -m * v1.y, m * v1.x } + q1[1]
137cb93a386Sopenharmony_ci    // straight angle when : v2.x * (dq1.y - q1[0].y) == v2.y * (dq1.x - q1[0].x)
138cb93a386Sopenharmony_ci    //                       v2.x * (m * v1.x + v1.y) == v2.y * (-m * v1.y + v1.x)
139cb93a386Sopenharmony_ci    // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x
140cb93a386Sopenharmony_ci    // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
141cb93a386Sopenharmony_ci    // m = v1.cross(v2) / v1.dot(v2)
142cb93a386Sopenharmony_ci    double s0dt0 = sweep[0].dot(tweep[0]);
143cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, s0dt0 != 0);
144cb93a386Sopenharmony_ci    double s0xt0 = sweep[0].crossCheck(tweep[0]);
145cb93a386Sopenharmony_ci    double m = s0xt0 / s0dt0;
146cb93a386Sopenharmony_ci    double sDist = sweep[0].length() * m;
147cb93a386Sopenharmony_ci    double tDist = tweep[0].length() * m;
148cb93a386Sopenharmony_ci    bool useS = fabs(sDist) < fabs(tDist);
149cb93a386Sopenharmony_ci    double mFactor = fabs(useS ? distEndRatio(sDist, quad1) : distEndRatio(tDist, quad2));
150cb93a386Sopenharmony_ci    if (mFactor < 5000) {  // empirically found limit
151cb93a386Sopenharmony_ci        return s0xt0 < 0;
152cb93a386Sopenharmony_ci    }
153cb93a386Sopenharmony_ci    SkDVector m0 = quad1.ptAtT(0.5) - quad1[0];
154cb93a386Sopenharmony_ci    SkDVector m1 = quad2.ptAtT(0.5) - quad2[0];
155cb93a386Sopenharmony_ci    return m0.crossCheck(m1) < 0;
156cb93a386Sopenharmony_ci}
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci/* returns
159cb93a386Sopenharmony_ci   -1 if overlaps
160cb93a386Sopenharmony_ci    0 if no overlap cw
161cb93a386Sopenharmony_ci    1 if no overlap ccw
162cb93a386Sopenharmony_ci*/
163cb93a386Sopenharmony_cistatic int quadHullsOverlap(skiatest::Reporter* reporter, const SkDQuad& quad1,
164cb93a386Sopenharmony_ci        const SkDQuad& quad2) {
165cb93a386Sopenharmony_ci    SkDVector sweep[2], tweep[2];
166cb93a386Sopenharmony_ci    setQuadHullSweep(quad1, sweep);
167cb93a386Sopenharmony_ci    setQuadHullSweep(quad2, tweep);
168cb93a386Sopenharmony_ci    double s0xs1 = sweep[0].crossCheck(sweep[1]);
169cb93a386Sopenharmony_ci    double s0xt0 = sweep[0].crossCheck(tweep[0]);
170cb93a386Sopenharmony_ci    double s1xt0 = sweep[1].crossCheck(tweep[0]);
171cb93a386Sopenharmony_ci    bool tBetweenS = s0xs1 > 0 ? s0xt0 > 0 && s1xt0 < 0 : s0xt0 < 0 && s1xt0 > 0;
172cb93a386Sopenharmony_ci    double s0xt1 = sweep[0].crossCheck(tweep[1]);
173cb93a386Sopenharmony_ci    double s1xt1 = sweep[1].crossCheck(tweep[1]);
174cb93a386Sopenharmony_ci    tBetweenS |= s0xs1 > 0 ? s0xt1 > 0 && s1xt1 < 0 : s0xt1 < 0 && s1xt1 > 0;
175cb93a386Sopenharmony_ci    double t0xt1 = tweep[0].crossCheck(tweep[1]);
176cb93a386Sopenharmony_ci    if (tBetweenS) {
177cb93a386Sopenharmony_ci        return -1;
178cb93a386Sopenharmony_ci    }
179cb93a386Sopenharmony_ci    if ((s0xt0 == 0 && s1xt1 == 0) || (s1xt0 == 0 && s0xt1 == 0)) {  // s0 to s1 equals t0 to t1
180cb93a386Sopenharmony_ci        return -1;
181cb93a386Sopenharmony_ci    }
182cb93a386Sopenharmony_ci    bool sBetweenT = t0xt1 > 0 ? s0xt0 < 0 && s0xt1 > 0 : s0xt0 > 0 && s0xt1 < 0;
183cb93a386Sopenharmony_ci    sBetweenT |= t0xt1 > 0 ? s1xt0 < 0 && s1xt1 > 0 : s1xt0 > 0 && s1xt1 < 0;
184cb93a386Sopenharmony_ci    if (sBetweenT) {
185cb93a386Sopenharmony_ci        return -1;
186cb93a386Sopenharmony_ci    }
187cb93a386Sopenharmony_ci    // if all of the sweeps are in the same half plane, then the order of any pair is enough
188cb93a386Sopenharmony_ci    if (s0xt0 >= 0 && s0xt1 >= 0 && s1xt0 >= 0 && s1xt1 >= 0) {
189cb93a386Sopenharmony_ci        return 0;
190cb93a386Sopenharmony_ci    }
191cb93a386Sopenharmony_ci    if (s0xt0 <= 0 && s0xt1 <= 0 && s1xt0 <= 0 && s1xt1 <= 0) {
192cb93a386Sopenharmony_ci        return 1;
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci    // if the outside sweeps are greater than 180 degress:
195cb93a386Sopenharmony_ci        // first assume the inital tangents are the ordering
196cb93a386Sopenharmony_ci        // if the midpoint direction matches the inital order, that is enough
197cb93a386Sopenharmony_ci    SkDVector m0 = quad1.ptAtT(0.5) - quad1[0];
198cb93a386Sopenharmony_ci    SkDVector m1 = quad2.ptAtT(0.5) - quad2[0];
199cb93a386Sopenharmony_ci    double m0xm1 = m0.crossCheck(m1);
200cb93a386Sopenharmony_ci    if (s0xt0 > 0 && m0xm1 > 0) {
201cb93a386Sopenharmony_ci        return 0;
202cb93a386Sopenharmony_ci    }
203cb93a386Sopenharmony_ci    if (s0xt0 < 0 && m0xm1 < 0) {
204cb93a386Sopenharmony_ci        return 1;
205cb93a386Sopenharmony_ci    }
206cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, s0xt0 != 0);
207cb93a386Sopenharmony_ci    return checkParallel(reporter, quad1, quad2);
208cb93a386Sopenharmony_ci}
209cb93a386Sopenharmony_ci
210cb93a386Sopenharmony_cistatic double radianSweep(double start, double end) {
211cb93a386Sopenharmony_ci    double sweep = end - start;
212cb93a386Sopenharmony_ci    if (sweep > SK_ScalarPI) {
213cb93a386Sopenharmony_ci        sweep -= 2 * SK_ScalarPI;
214cb93a386Sopenharmony_ci    } else if (sweep < -SK_ScalarPI) {
215cb93a386Sopenharmony_ci        sweep += 2 * SK_ScalarPI;
216cb93a386Sopenharmony_ci    }
217cb93a386Sopenharmony_ci    return sweep;
218cb93a386Sopenharmony_ci}
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_cistatic bool radianBetween(double start, double test, double end) {
221cb93a386Sopenharmony_ci    double startToEnd = radianSweep(start, end);
222cb93a386Sopenharmony_ci    double startToTest = radianSweep(start, test);
223cb93a386Sopenharmony_ci    double testToEnd = radianSweep(test, end);
224cb93a386Sopenharmony_ci    return (startToTest <= 0 && testToEnd <= 0 && startToTest >= startToEnd) ||
225cb93a386Sopenharmony_ci        (startToTest >= 0 && testToEnd >= 0 && startToTest <= startToEnd);
226cb93a386Sopenharmony_ci}
227cb93a386Sopenharmony_ci
228cb93a386Sopenharmony_cistatic bool orderTRange(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
229cb93a386Sopenharmony_ci        double r, TRange* result) {
230cb93a386Sopenharmony_ci    SkTArray<double, false> t1Array, t2Array;
231cb93a386Sopenharmony_ci    orderQuads(reporter, quad1, r, &t1Array);
232cb93a386Sopenharmony_ci    orderQuads(reporter,quad2, r, &t2Array);
233cb93a386Sopenharmony_ci    if (!t1Array.count() || !t2Array.count()) {
234cb93a386Sopenharmony_ci        return false;
235cb93a386Sopenharmony_ci    }
236cb93a386Sopenharmony_ci    SkTQSort<double>(t1Array.begin(), t1Array.end());
237cb93a386Sopenharmony_ci    SkTQSort<double>(t2Array.begin(), t2Array.end());
238cb93a386Sopenharmony_ci    double t1 = result->tMin1 = t1Array[0];
239cb93a386Sopenharmony_ci    double t2 = result->tMin2 = t2Array[0];
240cb93a386Sopenharmony_ci    double a1 = quadAngle(reporter,quad1, t1);
241cb93a386Sopenharmony_ci    double a2 = quadAngle(reporter,quad2, t2);
242cb93a386Sopenharmony_ci    if (approximately_equal(a1, a2)) {
243cb93a386Sopenharmony_ci        return false;
244cb93a386Sopenharmony_ci    }
245cb93a386Sopenharmony_ci    bool refCCW = angleDirection(a1, a2);
246cb93a386Sopenharmony_ci    result->t1 = t1;
247cb93a386Sopenharmony_ci    result->t2 = t2;
248cb93a386Sopenharmony_ci    result->tMin = std::min(t1, t2);
249cb93a386Sopenharmony_ci    result->a1 = a1;
250cb93a386Sopenharmony_ci    result->a2 = a2;
251cb93a386Sopenharmony_ci    result->ccw = refCCW;
252cb93a386Sopenharmony_ci    return true;
253cb93a386Sopenharmony_ci}
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_cistatic bool equalPoints(const SkDPoint& pt1, const SkDPoint& pt2, double max) {
256cb93a386Sopenharmony_ci    return approximately_zero_when_compared_to(pt1.fX - pt2.fX, max)
257cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(pt1.fY - pt2.fY, max);
258cb93a386Sopenharmony_ci}
259cb93a386Sopenharmony_ci
260cb93a386Sopenharmony_cistatic double maxDist(const SkDQuad& quad) {
261cb93a386Sopenharmony_ci    SkDRect bounds;
262cb93a386Sopenharmony_ci    bounds.setBounds(quad);
263cb93a386Sopenharmony_ci    SkDVector corner[4] = {
264cb93a386Sopenharmony_ci        { bounds.fLeft - quad[0].fX, bounds.fTop - quad[0].fY },
265cb93a386Sopenharmony_ci        { bounds.fRight - quad[0].fX, bounds.fTop - quad[0].fY },
266cb93a386Sopenharmony_ci        { bounds.fLeft - quad[0].fX, bounds.fBottom - quad[0].fY },
267cb93a386Sopenharmony_ci        { bounds.fRight - quad[0].fX, bounds.fBottom - quad[0].fY }
268cb93a386Sopenharmony_ci    };
269cb93a386Sopenharmony_ci    double max = 0;
270cb93a386Sopenharmony_ci    for (unsigned index = 0; index < SK_ARRAY_COUNT(corner); ++index) {
271cb93a386Sopenharmony_ci        max = std::max(max, corner[index].length());
272cb93a386Sopenharmony_ci    }
273cb93a386Sopenharmony_ci    return max;
274cb93a386Sopenharmony_ci}
275cb93a386Sopenharmony_ci
276cb93a386Sopenharmony_cistatic double maxQuad(const SkDQuad& quad) {
277cb93a386Sopenharmony_ci    double max = 0;
278cb93a386Sopenharmony_ci    for (int index = 0; index < 2; ++index) {
279cb93a386Sopenharmony_ci        max = std::max(max, fabs(quad[index].fX));
280cb93a386Sopenharmony_ci        max = std::max(max, fabs(quad[index].fY));
281cb93a386Sopenharmony_ci    }
282cb93a386Sopenharmony_ci    return max;
283cb93a386Sopenharmony_ci}
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_cistatic bool bruteMinT(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
286cb93a386Sopenharmony_ci        TRange* lowerRange, TRange* upperRange) {
287cb93a386Sopenharmony_ci    double maxRadius = std::min(maxDist(quad1), maxDist(quad2));
288cb93a386Sopenharmony_ci    double maxQuads = std::max(maxQuad(quad1), maxQuad(quad2));
289cb93a386Sopenharmony_ci    double r = maxRadius / 2;
290cb93a386Sopenharmony_ci    double rStep = r / 2;
291cb93a386Sopenharmony_ci    SkDPoint best1 = {SK_ScalarInfinity, SK_ScalarInfinity};
292cb93a386Sopenharmony_ci    SkDPoint best2 = {SK_ScalarInfinity, SK_ScalarInfinity};
293cb93a386Sopenharmony_ci    int bestCCW = -1;
294cb93a386Sopenharmony_ci    double bestR = maxRadius;
295cb93a386Sopenharmony_ci    upperRange->tMin = 0;
296cb93a386Sopenharmony_ci    lowerRange->tMin = 1;
297cb93a386Sopenharmony_ci    do {
298cb93a386Sopenharmony_ci        do {  // find upper bounds of single result
299cb93a386Sopenharmony_ci            TRange tRange;
300cb93a386Sopenharmony_ci            bool stepUp = orderTRange(reporter, quad1, quad2, r, &tRange);
301cb93a386Sopenharmony_ci            if (stepUp) {
302cb93a386Sopenharmony_ci                SkDPoint pt1 = quad1.ptAtT(tRange.t1);
303cb93a386Sopenharmony_ci                if (equalPoints(pt1, best1, maxQuads)) {
304cb93a386Sopenharmony_ci                    break;
305cb93a386Sopenharmony_ci                }
306cb93a386Sopenharmony_ci                best1 = pt1;
307cb93a386Sopenharmony_ci                SkDPoint pt2 = quad2.ptAtT(tRange.t2);
308cb93a386Sopenharmony_ci                if (equalPoints(pt2, best2, maxQuads)) {
309cb93a386Sopenharmony_ci                    break;
310cb93a386Sopenharmony_ci                }
311cb93a386Sopenharmony_ci                best2 = pt2;
312cb93a386Sopenharmony_ci                if (gPathOpsAngleIdeasVerbose) {
313cb93a386Sopenharmony_ci                    SkDebugf("u bestCCW=%d ccw=%d bestMin=%1.9g:%1.9g r=%1.9g tMin=%1.9g\n",
314cb93a386Sopenharmony_ci                            bestCCW, tRange.ccw, lowerRange->tMin, upperRange->tMin, r,
315cb93a386Sopenharmony_ci                            tRange.tMin);
316cb93a386Sopenharmony_ci                }
317cb93a386Sopenharmony_ci                if (bestCCW >= 0 && bestCCW != (int) tRange.ccw) {
318cb93a386Sopenharmony_ci                    if (tRange.tMin < upperRange->tMin) {
319cb93a386Sopenharmony_ci                        upperRange->tMin = 0;
320cb93a386Sopenharmony_ci                    } else {
321cb93a386Sopenharmony_ci                        stepUp = false;
322cb93a386Sopenharmony_ci                    }
323cb93a386Sopenharmony_ci                }
324cb93a386Sopenharmony_ci                if (upperRange->tMin < tRange.tMin) {
325cb93a386Sopenharmony_ci                    bestCCW = tRange.ccw;
326cb93a386Sopenharmony_ci                    bestR = r;
327cb93a386Sopenharmony_ci                    *upperRange = tRange;
328cb93a386Sopenharmony_ci                }
329cb93a386Sopenharmony_ci                if (lowerRange->tMin > tRange.tMin) {
330cb93a386Sopenharmony_ci                    *lowerRange = tRange;
331cb93a386Sopenharmony_ci                }
332cb93a386Sopenharmony_ci            }
333cb93a386Sopenharmony_ci            r += stepUp ? rStep : -rStep;
334cb93a386Sopenharmony_ci            rStep /= 2;
335cb93a386Sopenharmony_ci        } while (rStep > FLT_EPSILON);
336cb93a386Sopenharmony_ci        if (bestCCW < 0) {
337cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, bestR < maxRadius);
338cb93a386Sopenharmony_ci            return false;
339cb93a386Sopenharmony_ci        }
340cb93a386Sopenharmony_ci        double lastHighR = bestR;
341cb93a386Sopenharmony_ci        r = bestR / 2;
342cb93a386Sopenharmony_ci        rStep = r / 2;
343cb93a386Sopenharmony_ci        do {  // find lower bounds of single result
344cb93a386Sopenharmony_ci            TRange tRange;
345cb93a386Sopenharmony_ci            bool success = orderTRange(reporter, quad1, quad2, r, &tRange);
346cb93a386Sopenharmony_ci            if (success) {
347cb93a386Sopenharmony_ci                if (gPathOpsAngleIdeasVerbose) {
348cb93a386Sopenharmony_ci                    SkDebugf("l bestCCW=%d ccw=%d bestMin=%1.9g:%1.9g r=%1.9g tMin=%1.9g\n",
349cb93a386Sopenharmony_ci                            bestCCW, tRange.ccw, lowerRange->tMin, upperRange->tMin, r,
350cb93a386Sopenharmony_ci                            tRange.tMin);
351cb93a386Sopenharmony_ci                }
352cb93a386Sopenharmony_ci                if (bestCCW != (int) tRange.ccw || upperRange->tMin < tRange.tMin) {
353cb93a386Sopenharmony_ci                    bestCCW = tRange.ccw;
354cb93a386Sopenharmony_ci                    *upperRange = tRange;
355cb93a386Sopenharmony_ci                    bestR = lastHighR;
356cb93a386Sopenharmony_ci                    break;  // need to establish a new upper bounds
357cb93a386Sopenharmony_ci                }
358cb93a386Sopenharmony_ci                SkDPoint pt1 = quad1.ptAtT(tRange.t1);
359cb93a386Sopenharmony_ci                SkDPoint pt2 = quad2.ptAtT(tRange.t2);
360cb93a386Sopenharmony_ci                if (equalPoints(pt1, best1, maxQuads)) {
361cb93a386Sopenharmony_ci                    goto breakOut;
362cb93a386Sopenharmony_ci                }
363cb93a386Sopenharmony_ci                best1 = pt1;
364cb93a386Sopenharmony_ci                if (equalPoints(pt2, best2, maxQuads)) {
365cb93a386Sopenharmony_ci                    goto breakOut;
366cb93a386Sopenharmony_ci                }
367cb93a386Sopenharmony_ci                best2 = pt2;
368cb93a386Sopenharmony_ci                if (equalPoints(pt1, pt2, maxQuads)) {
369cb93a386Sopenharmony_ci                    success = false;
370cb93a386Sopenharmony_ci                } else {
371cb93a386Sopenharmony_ci                    if (upperRange->tMin < tRange.tMin) {
372cb93a386Sopenharmony_ci                        *upperRange = tRange;
373cb93a386Sopenharmony_ci                    }
374cb93a386Sopenharmony_ci                    if (lowerRange->tMin > tRange.tMin) {
375cb93a386Sopenharmony_ci                        *lowerRange = tRange;
376cb93a386Sopenharmony_ci                    }
377cb93a386Sopenharmony_ci                }
378cb93a386Sopenharmony_ci                lastHighR = std::min(r, lastHighR);
379cb93a386Sopenharmony_ci            }
380cb93a386Sopenharmony_ci            r += success ? -rStep : rStep;
381cb93a386Sopenharmony_ci            rStep /= 2;
382cb93a386Sopenharmony_ci        } while (rStep > FLT_EPSILON);
383cb93a386Sopenharmony_ci    } while (rStep > FLT_EPSILON);
384cb93a386Sopenharmony_cibreakOut:
385cb93a386Sopenharmony_ci    if (gPathOpsAngleIdeasVerbose) {
386cb93a386Sopenharmony_ci        SkDebugf("l a2-a1==%1.9g\n", lowerRange->a2 - lowerRange->a1);
387cb93a386Sopenharmony_ci    }
388cb93a386Sopenharmony_ci    return true;
389cb93a386Sopenharmony_ci}
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_cistatic void bruteForce(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
392cb93a386Sopenharmony_ci        bool ccw) {
393cb93a386Sopenharmony_ci    if (!gPathOpsAngleIdeasEnableBruteCheck) {
394cb93a386Sopenharmony_ci        return;
395cb93a386Sopenharmony_ci    }
396cb93a386Sopenharmony_ci    TRange lowerRange, upperRange;
397cb93a386Sopenharmony_ci    bool result = bruteMinT(reporter, quad1, quad2, &lowerRange, &upperRange);
398cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, result);
399cb93a386Sopenharmony_ci    double angle = fabs(lowerRange.a2 - lowerRange.a1);
400cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, angle > 3.998 || ccw == upperRange.ccw);
401cb93a386Sopenharmony_ci}
402cb93a386Sopenharmony_ci
403cb93a386Sopenharmony_cistatic bool bruteForceCheck(skiatest::Reporter* reporter, const SkDQuad& quad1,
404cb93a386Sopenharmony_ci        const SkDQuad& quad2, bool ccw) {
405cb93a386Sopenharmony_ci    TRange lowerRange, upperRange;
406cb93a386Sopenharmony_ci    bool result = bruteMinT(reporter, quad1, quad2, &lowerRange, &upperRange);
407cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, result);
408cb93a386Sopenharmony_ci    return ccw == upperRange.ccw;
409cb93a386Sopenharmony_ci}
410cb93a386Sopenharmony_ci
411cb93a386Sopenharmony_cistatic void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3]) {
412cb93a386Sopenharmony_ci    shortQuad[0] = quad[0].asSkPoint();
413cb93a386Sopenharmony_ci    shortQuad[1] = quad[1].asSkPoint();
414cb93a386Sopenharmony_ci    shortQuad[2] = quad[2].asSkPoint();
415cb93a386Sopenharmony_ci    contour->addQuad(shortQuad);
416cb93a386Sopenharmony_ci}
417cb93a386Sopenharmony_ci
418cb93a386Sopenharmony_cistatic void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
419cb93a386Sopenharmony_ci        int testNo, SkArenaAlloc* allocator) {
420cb93a386Sopenharmony_ci    SkPoint shortQuads[2][3];
421cb93a386Sopenharmony_ci
422cb93a386Sopenharmony_ci    SkOpContourHead contour;
423cb93a386Sopenharmony_ci    SkOpGlobalState state(&contour, allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
424cb93a386Sopenharmony_ci    contour.init(&state, false, false);
425cb93a386Sopenharmony_ci    makeSegment(&contour, quad1, shortQuads[0]);
426cb93a386Sopenharmony_ci    makeSegment(&contour, quad1, shortQuads[1]);
427cb93a386Sopenharmony_ci    SkOpSegment* seg1 = contour.first();
428cb93a386Sopenharmony_ci    seg1->debugAddAngle(0, 1);
429cb93a386Sopenharmony_ci    SkOpSegment* seg2 = seg1->next();
430cb93a386Sopenharmony_ci    seg2->debugAddAngle(0, 1);
431cb93a386Sopenharmony_ci    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
432cb93a386Sopenharmony_ci            *seg2->debugLastAngle());
433cb93a386Sopenharmony_ci    const SkDPoint& origin = quad1[0];
434cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, origin == quad2[0]);
435cb93a386Sopenharmony_ci    double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX);
436cb93a386Sopenharmony_ci    double a1e = atan2(origin.fY - quad1[2].fY, quad1[2].fX - origin.fX);
437cb93a386Sopenharmony_ci    double a2s = atan2(origin.fY - quad2[1].fY, quad2[1].fX - origin.fX);
438cb93a386Sopenharmony_ci    double a2e = atan2(origin.fY - quad2[2].fY, quad2[2].fX - origin.fX);
439cb93a386Sopenharmony_ci    bool oldSchoolOverlap = radianBetween(a1s, a2s, a1e)
440cb93a386Sopenharmony_ci        || radianBetween(a1s, a2e, a1e) || radianBetween(a2s, a1s, a2e)
441cb93a386Sopenharmony_ci        || radianBetween(a2s, a1e, a2e);
442cb93a386Sopenharmony_ci    int overlap = quadHullsOverlap(reporter, quad1, quad2);
443cb93a386Sopenharmony_ci    bool realMatchesOverlap = realOverlap == overlap || SK_ScalarPI - fabs(a2s - a1s) < 0.002;
444cb93a386Sopenharmony_ci    if (realOverlap != overlap) {
445cb93a386Sopenharmony_ci        SkDebugf("\nSK_ScalarPI - fabs(a2s - a1s) = %1.9g\n", SK_ScalarPI - fabs(a2s - a1s));
446cb93a386Sopenharmony_ci    }
447cb93a386Sopenharmony_ci    if (!realMatchesOverlap) {
448cb93a386Sopenharmony_ci        DumpQ(quad1, quad2, testNo);
449cb93a386Sopenharmony_ci    }
450cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, realMatchesOverlap);
451cb93a386Sopenharmony_ci    if (oldSchoolOverlap != (overlap < 0)) {
452cb93a386Sopenharmony_ci        overlap = quadHullsOverlap(reporter, quad1, quad2);  // set a breakpoint and debug if assert fires
453cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, oldSchoolOverlap == (overlap < 0));
454cb93a386Sopenharmony_ci    }
455cb93a386Sopenharmony_ci    SkDVector v1s = quad1[1] - quad1[0];
456cb93a386Sopenharmony_ci    SkDVector v1e = quad1[2] - quad1[0];
457cb93a386Sopenharmony_ci    SkDVector v2s = quad2[1] - quad2[0];
458cb93a386Sopenharmony_ci    SkDVector v2e = quad2[2] - quad2[0];
459cb93a386Sopenharmony_ci    double vDir[2] = { v1s.cross(v1e), v2s.cross(v2e) };
460cb93a386Sopenharmony_ci    bool ray1In2 = v1s.cross(v2s) * vDir[1] <= 0 && v1s.cross(v2e) * vDir[1] >= 0;
461cb93a386Sopenharmony_ci    bool ray2In1 = v2s.cross(v1s) * vDir[0] <= 0 && v2s.cross(v1e) * vDir[0] >= 0;
462cb93a386Sopenharmony_ci    if (overlap >= 0) {
463cb93a386Sopenharmony_ci        // verify that hulls really don't overlap
464cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, !ray1In2);
465cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, !ray2In1);
466cb93a386Sopenharmony_ci        bool ctrl1In2 = v1e.cross(v2s) * vDir[1] <= 0 && v1e.cross(v2e) * vDir[1] >= 0;
467cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, !ctrl1In2);
468cb93a386Sopenharmony_ci        bool ctrl2In1 = v2e.cross(v1s) * vDir[0] <= 0 && v2e.cross(v1e) * vDir[0] >= 0;
469cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, !ctrl2In1);
470cb93a386Sopenharmony_ci        // check answer against reference
471cb93a386Sopenharmony_ci        bruteForce(reporter, quad1, quad2, overlap > 0);
472cb93a386Sopenharmony_ci    }
473cb93a386Sopenharmony_ci    // continue end point rays and see if they intersect the opposite curve
474cb93a386Sopenharmony_ci    SkDLine rays[] = {{{origin, quad2[2]}}, {{origin, quad1[2]}}};
475cb93a386Sopenharmony_ci    const SkDQuad* quads[] = {&quad1, &quad2};
476cb93a386Sopenharmony_ci    SkDVector midSpokes[2];
477cb93a386Sopenharmony_ci    SkIntersections intersect[2];
478cb93a386Sopenharmony_ci    double minX, minY, maxX, maxY;
479cb93a386Sopenharmony_ci    minX = minY = SK_ScalarInfinity;
480cb93a386Sopenharmony_ci    maxX = maxY = -SK_ScalarInfinity;
481cb93a386Sopenharmony_ci    double maxWidth = 0;
482cb93a386Sopenharmony_ci    bool useIntersect = false;
483cb93a386Sopenharmony_ci    double smallestTs[] = {1, 1};
484cb93a386Sopenharmony_ci    for (unsigned index = 0; index < SK_ARRAY_COUNT(quads); ++index) {
485cb93a386Sopenharmony_ci        const SkDQuad& q = *quads[index];
486cb93a386Sopenharmony_ci        midSpokes[index] = q.ptAtT(0.5) - origin;
487cb93a386Sopenharmony_ci        minX = std::min(std::min(std::min(minX, origin.fX), q[1].fX), q[2].fX);
488cb93a386Sopenharmony_ci        minY = std::min(std::min(std::min(minY, origin.fY), q[1].fY), q[2].fY);
489cb93a386Sopenharmony_ci        maxX = std::max(std::max(std::max(maxX, origin.fX), q[1].fX), q[2].fX);
490cb93a386Sopenharmony_ci        maxY = std::max(std::max(std::max(maxY, origin.fY), q[1].fY), q[2].fY);
491cb93a386Sopenharmony_ci        maxWidth = std::max(maxWidth, std::max(maxX - minX, maxY - minY));
492cb93a386Sopenharmony_ci        intersect[index].intersectRay(q, rays[index]);
493cb93a386Sopenharmony_ci        const SkIntersections& i = intersect[index];
494cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, i.used() >= 1);
495cb93a386Sopenharmony_ci        bool foundZero = false;
496cb93a386Sopenharmony_ci        double smallT = 1;
497cb93a386Sopenharmony_ci        for (int idx2 = 0; idx2 < i.used(); ++idx2) {
498cb93a386Sopenharmony_ci            double t = i[0][idx2];
499cb93a386Sopenharmony_ci            if (t == 0) {
500cb93a386Sopenharmony_ci                foundZero = true;
501cb93a386Sopenharmony_ci                continue;
502cb93a386Sopenharmony_ci            }
503cb93a386Sopenharmony_ci            if (smallT > t) {
504cb93a386Sopenharmony_ci                smallT = t;
505cb93a386Sopenharmony_ci            }
506cb93a386Sopenharmony_ci        }
507cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, foundZero == true);
508cb93a386Sopenharmony_ci        if (smallT == 1) {
509cb93a386Sopenharmony_ci            continue;
510cb93a386Sopenharmony_ci        }
511cb93a386Sopenharmony_ci        SkDVector ray = q.ptAtT(smallT) - origin;
512cb93a386Sopenharmony_ci        SkDVector end = rays[index][1] - origin;
513cb93a386Sopenharmony_ci        if (ray.fX * end.fX < 0 || ray.fY * end.fY < 0) {
514cb93a386Sopenharmony_ci            continue;
515cb93a386Sopenharmony_ci        }
516cb93a386Sopenharmony_ci        double rayDist = ray.length();
517cb93a386Sopenharmony_ci        double endDist = end.length();
518cb93a386Sopenharmony_ci        double delta = fabs(rayDist - endDist) / maxWidth;
519cb93a386Sopenharmony_ci        if (delta > 1e-4) {
520cb93a386Sopenharmony_ci            useIntersect ^= true;
521cb93a386Sopenharmony_ci        }
522cb93a386Sopenharmony_ci        smallestTs[index] = smallT;
523cb93a386Sopenharmony_ci    }
524cb93a386Sopenharmony_ci    bool firstInside;
525cb93a386Sopenharmony_ci    if (useIntersect) {
526cb93a386Sopenharmony_ci        int sIndex = (int) (smallestTs[1] < 1);
527cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, smallestTs[sIndex ^ 1] == 1);
528cb93a386Sopenharmony_ci        double t = smallestTs[sIndex];
529cb93a386Sopenharmony_ci        const SkDQuad& q = *quads[sIndex];
530cb93a386Sopenharmony_ci        SkDVector ray = q.ptAtT(t) - origin;
531cb93a386Sopenharmony_ci        SkDVector end = rays[sIndex][1] - origin;
532cb93a386Sopenharmony_ci        double rayDist = ray.length();
533cb93a386Sopenharmony_ci        double endDist = end.length();
534cb93a386Sopenharmony_ci        SkDVector mid = q.ptAtT(t / 2) - origin;
535cb93a386Sopenharmony_ci        double midXray = mid.crossCheck(ray);
536cb93a386Sopenharmony_ci        if (gPathOpsAngleIdeasVerbose) {
537cb93a386Sopenharmony_ci            SkDebugf("rayDist>endDist:%d sIndex==0:%d vDir[sIndex]<0:%d midXray<0:%d\n",
538cb93a386Sopenharmony_ci                    rayDist > endDist, sIndex == 0, vDir[sIndex] < 0, midXray < 0);
539cb93a386Sopenharmony_ci        }
540cb93a386Sopenharmony_ci        SkASSERT(SkScalarSignAsInt(SkDoubleToScalar(midXray))
541cb93a386Sopenharmony_ci            == SkScalarSignAsInt(SkDoubleToScalar(vDir[sIndex])));
542cb93a386Sopenharmony_ci        firstInside = (rayDist > endDist) ^ (sIndex == 0) ^ (vDir[sIndex] < 0);
543cb93a386Sopenharmony_ci    } else if (overlap >= 0) {
544cb93a386Sopenharmony_ci        return;  // answer has already been determined
545cb93a386Sopenharmony_ci    } else {
546cb93a386Sopenharmony_ci        firstInside = checkParallel(reporter, quad1, quad2);
547cb93a386Sopenharmony_ci    }
548cb93a386Sopenharmony_ci    if (overlap < 0) {
549cb93a386Sopenharmony_ci        SkDEBUGCODE(int realEnds =)
550cb93a386Sopenharmony_ci                PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(),
551cb93a386Sopenharmony_ci                *seg2->debugLastAngle());
552cb93a386Sopenharmony_ci        SkASSERT(realEnds == (firstInside ? 1 : 0));
553cb93a386Sopenharmony_ci    }
554cb93a386Sopenharmony_ci    bruteForce(reporter, quad1, quad2, firstInside);
555cb93a386Sopenharmony_ci}
556cb93a386Sopenharmony_ci
557cb93a386Sopenharmony_ciDEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
558cb93a386Sopenharmony_ci    SkSTArenaAlloc<4096> allocator;
559cb93a386Sopenharmony_ci//    gPathOpsAngleIdeasVerbose = true;
560cb93a386Sopenharmony_ci    const QuadPts quads[] = {
561cb93a386Sopenharmony_ci{{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
562cb93a386Sopenharmony_ci{{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
563cb93a386Sopenharmony_ci    };
564cb93a386Sopenharmony_ci    for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) {
565cb93a386Sopenharmony_ci        SkDQuad quad0, quad1;
566cb93a386Sopenharmony_ci        quad0.debugSet(quads[index].fPts);
567cb93a386Sopenharmony_ci        quad1.debugSet(quads[index + 1].fPts);
568cb93a386Sopenharmony_ci        testQuadAngles(reporter, quad0, quad1, 0, &allocator);
569cb93a386Sopenharmony_ci    }
570cb93a386Sopenharmony_ci}
571cb93a386Sopenharmony_ci
572cb93a386Sopenharmony_ciDEF_TEST(PathOpsAngleOverlapHulls, reporter) {
573cb93a386Sopenharmony_ci    SkSTArenaAlloc<4096> allocator;
574cb93a386Sopenharmony_ci    if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
575cb93a386Sopenharmony_ci        return;
576cb93a386Sopenharmony_ci    }
577cb93a386Sopenharmony_ci    SkRandom ran;
578cb93a386Sopenharmony_ci    for (int index = 0; index < 100000; ++index) {
579cb93a386Sopenharmony_ci        if (index % 1000 == 999) SkDebugf(".");
580cb93a386Sopenharmony_ci        SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
581cb93a386Sopenharmony_ci        QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
582cb93a386Sopenharmony_ci            {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
583cb93a386Sopenharmony_ci        if (quad1.fPts[0] == quad1.fPts[2]) {
584cb93a386Sopenharmony_ci            continue;
585cb93a386Sopenharmony_ci        }
586cb93a386Sopenharmony_ci        QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
587cb93a386Sopenharmony_ci            {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
588cb93a386Sopenharmony_ci        if (quad2.fPts[0] == quad2.fPts[2]) {
589cb93a386Sopenharmony_ci            continue;
590cb93a386Sopenharmony_ci        }
591cb93a386Sopenharmony_ci        SkIntersections i;
592cb93a386Sopenharmony_ci        SkDQuad q1, q2;
593cb93a386Sopenharmony_ci        q1.debugSet(quad1.fPts);
594cb93a386Sopenharmony_ci        q2.debugSet(quad2.fPts);
595cb93a386Sopenharmony_ci        i.intersect(q1, q2);
596cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, i.used() >= 1);
597cb93a386Sopenharmony_ci        if (i.used() > 1) {
598cb93a386Sopenharmony_ci            continue;
599cb93a386Sopenharmony_ci        }
600cb93a386Sopenharmony_ci        testQuadAngles(reporter, q1, q2, index, &allocator);
601cb93a386Sopenharmony_ci    }
602cb93a386Sopenharmony_ci}
603cb93a386Sopenharmony_ci
604cb93a386Sopenharmony_ciDEF_TEST(PathOpsAngleBruteT, reporter) {
605cb93a386Sopenharmony_ci    if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
606cb93a386Sopenharmony_ci        return;
607cb93a386Sopenharmony_ci    }
608cb93a386Sopenharmony_ci    SkRandom ran;
609cb93a386Sopenharmony_ci    double smaller = SK_Scalar1;
610cb93a386Sopenharmony_ci    SkDQuad small[2];
611cb93a386Sopenharmony_ci    SkDEBUGCODE(int smallIndex = 0);
612cb93a386Sopenharmony_ci    for (int index = 0; index < 100000; ++index) {
613cb93a386Sopenharmony_ci        SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
614cb93a386Sopenharmony_ci        QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
615cb93a386Sopenharmony_ci            {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
616cb93a386Sopenharmony_ci        if (quad1.fPts[0] == quad1.fPts[2]) {
617cb93a386Sopenharmony_ci            continue;
618cb93a386Sopenharmony_ci        }
619cb93a386Sopenharmony_ci        QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
620cb93a386Sopenharmony_ci            {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
621cb93a386Sopenharmony_ci        if (quad2.fPts[0] == quad2.fPts[2]) {
622cb93a386Sopenharmony_ci            continue;
623cb93a386Sopenharmony_ci        }
624cb93a386Sopenharmony_ci        SkDQuad q1, q2;
625cb93a386Sopenharmony_ci        q1.debugSet(quad1.fPts);
626cb93a386Sopenharmony_ci        q2.debugSet(quad2.fPts);
627cb93a386Sopenharmony_ci        SkIntersections i;
628cb93a386Sopenharmony_ci        i.intersect(q1, q2);
629cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, i.used() >= 1);
630cb93a386Sopenharmony_ci        if (i.used() > 1) {
631cb93a386Sopenharmony_ci            continue;
632cb93a386Sopenharmony_ci        }
633cb93a386Sopenharmony_ci        TRange lowerRange, upperRange;
634cb93a386Sopenharmony_ci        bool result = bruteMinT(reporter, q1, q2, &lowerRange, &upperRange);
635cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, result);
636cb93a386Sopenharmony_ci        double min = std::min(upperRange.t1, upperRange.t2);
637cb93a386Sopenharmony_ci        if (smaller > min) {
638cb93a386Sopenharmony_ci            small[0] = q1;
639cb93a386Sopenharmony_ci            small[1] = q2;
640cb93a386Sopenharmony_ci            SkDEBUGCODE(smallIndex = index);
641cb93a386Sopenharmony_ci            smaller = min;
642cb93a386Sopenharmony_ci        }
643cb93a386Sopenharmony_ci    }
644cb93a386Sopenharmony_ci#ifdef SK_DEBUG
645cb93a386Sopenharmony_ci    DumpQ(small[0], small[1], smallIndex);
646cb93a386Sopenharmony_ci#endif
647cb93a386Sopenharmony_ci}
648cb93a386Sopenharmony_ci
649cb93a386Sopenharmony_ciDEF_TEST(PathOpsAngleBruteTOne, reporter) {
650cb93a386Sopenharmony_ci//    gPathOpsAngleIdeasVerbose = true;
651cb93a386Sopenharmony_ci    const QuadPts qPts[] = {
652cb93a386Sopenharmony_ci{{{-770.8492431640625, 948.2369384765625}, {-853.37066650390625, 972.0301513671875}, {-200.62042236328125, -26.7174072265625}}},
653cb93a386Sopenharmony_ci{{{-770.8492431640625, 948.2369384765625}, {513.602783203125, 578.8681640625}, {960.641357421875, -813.69757080078125}}},
654cb93a386Sopenharmony_ci{{{563.8267822265625, -107.4566650390625}, {-44.67724609375, -136.57452392578125}, {492.3856201171875, -268.79644775390625}}},
655cb93a386Sopenharmony_ci{{{563.8267822265625, -107.4566650390625}, {708.049072265625, -100.77789306640625}, {-48.88226318359375, 967.9022216796875}}},
656cb93a386Sopenharmony_ci{{{598.857421875, 846.345458984375}, {-644.095703125, -316.12921142578125}, {-97.64599609375, 20.6158447265625}}},
657cb93a386Sopenharmony_ci{{{598.857421875, 846.345458984375}, {715.7142333984375, 955.3599853515625}, {-919.9478759765625, 691.611328125}}},
658cb93a386Sopenharmony_ci    };
659cb93a386Sopenharmony_ci    TRange lowerRange, upperRange;
660cb93a386Sopenharmony_ci    SkDQuad quads[SK_ARRAY_COUNT(qPts)];
661cb93a386Sopenharmony_ci    for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts); ++index) {
662cb93a386Sopenharmony_ci        quads[index].debugSet(qPts[index].fPts);
663cb93a386Sopenharmony_ci    }
664cb93a386Sopenharmony_ci    bruteMinT(reporter, quads[0], quads[1], &lowerRange, &upperRange);
665cb93a386Sopenharmony_ci    bruteMinT(reporter, quads[2], quads[3], &lowerRange, &upperRange);
666cb93a386Sopenharmony_ci    bruteMinT(reporter, quads[4], quads[5], &lowerRange, &upperRange);
667cb93a386Sopenharmony_ci}
668cb93a386Sopenharmony_ci
669cb93a386Sopenharmony_ci/*
670cb93a386Sopenharmony_ciThe sorting problem happens when the inital tangents are not a true indicator of the curve direction
671cb93a386Sopenharmony_ciNearly always, the initial tangents do give the right answer,
672cb93a386Sopenharmony_ciso the trick is to figure out when the initial tangent cannot be trusted.
673cb93a386Sopenharmony_ciIf the convex hulls of both curves are in the same half plane, and not overlapping, sorting the
674cb93a386Sopenharmony_cihulls is enough.
675cb93a386Sopenharmony_ciIf the hulls overlap, and have the same general direction, then intersect the shorter end point ray
676cb93a386Sopenharmony_ciwith the opposing curve, and see on which side of the shorter curve the opposing intersection lies.
677cb93a386Sopenharmony_ciOtherwise, if the control vector is extremely short, likely the point on curve must be computed
678cb93a386Sopenharmony_ciIf moving the control point slightly can change the sign of the cross product, either answer could
679cb93a386Sopenharmony_cibe "right".
680cb93a386Sopenharmony_ciWe need to determine how short is extremely short. Move the control point a set percentage of
681cb93a386Sopenharmony_cithe largest length to determine how stable the curve is vis-a-vis the initial tangent.
682cb93a386Sopenharmony_ci*/
683cb93a386Sopenharmony_ci
684cb93a386Sopenharmony_cistatic const QuadPts extremeTests[][2] = {
685cb93a386Sopenharmony_ci    {
686cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
687cb93a386Sopenharmony_ci            {-707.9234268635319,-154.30459999551294},
688cb93a386Sopenharmony_ci            {505.58447265625,-504.9130859375}}},
689cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
690cb93a386Sopenharmony_ci            {-711.127526325141,-163.9446090624656},
691cb93a386Sopenharmony_ci            {-32.39227294921875,-906.3277587890625}}},
692cb93a386Sopenharmony_ci    }, {
693cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
694cb93a386Sopenharmony_ci            {-708.2875337527566,-154.36676458635623},
695cb93a386Sopenharmony_ci            {505.58447265625,-504.9130859375}}},
696cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
697cb93a386Sopenharmony_ci            {-708.4111557216864,-154.5366642875255},
698cb93a386Sopenharmony_ci            {-32.39227294921875,-906.3277587890625}}},
699cb93a386Sopenharmony_ci    }, {
700cb93a386Sopenharmony_ci        {{{-609.0230951752058,-267.5435593490574},
701cb93a386Sopenharmony_ci            {-594.1120809906336,-136.08492475411555},
702cb93a386Sopenharmony_ci            {505.58447265625,-504.9130859375}}},
703cb93a386Sopenharmony_ci        {{{-609.0230951752058,-267.5435593490574},
704cb93a386Sopenharmony_ci            {-693.7467719138988,-341.3259237831895},
705cb93a386Sopenharmony_ci            {-32.39227294921875,-906.3277587890625}}}
706cb93a386Sopenharmony_ci    }, {
707cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
708cb93a386Sopenharmony_ci            {-707.9994640658723,-154.58588461064852},
709cb93a386Sopenharmony_ci            {505.58447265625,-504.9130859375}}},
710cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
711cb93a386Sopenharmony_ci            {-708.0239418990758,-154.6403553507124},
712cb93a386Sopenharmony_ci            {-32.39227294921875,-906.3277587890625}}}
713cb93a386Sopenharmony_ci    }, {
714cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
715cb93a386Sopenharmony_ci            {-707.9993222215099,-154.55999389855003},
716cb93a386Sopenharmony_ci            {68.88981098017803,296.9273945411635}}},
717cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
718cb93a386Sopenharmony_ci            {-708.0509091919608,-154.64675214697067},
719cb93a386Sopenharmony_ci            {-777.4871194247767,-995.1470120113145}}}
720cb93a386Sopenharmony_ci    }, {
721cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
722cb93a386Sopenharmony_ci            {-708.0060491116379,-154.60889321524968},
723cb93a386Sopenharmony_ci            {229.97088707895057,-430.0569357467175}}},
724cb93a386Sopenharmony_ci        {{{-708.0077926931004,-154.61669472244046},
725cb93a386Sopenharmony_ci            {-708.013911296257,-154.6219143988058},
726cb93a386Sopenharmony_ci            {138.13162892614037,-573.3689311737394}}}
727cb93a386Sopenharmony_ci    }, {
728cb93a386Sopenharmony_ci        {{{-543.2570545751013,-237.29243831075053},
729cb93a386Sopenharmony_ci            {-452.4119186056987,-143.47223056267802},
730cb93a386Sopenharmony_ci            {229.97088707895057,-430.0569357467175}}},
731cb93a386Sopenharmony_ci        {{{-543.2570545751013,-237.29243831075053},
732cb93a386Sopenharmony_ci            {-660.5330371214436,-362.0016148388},
733cb93a386Sopenharmony_ci            {138.13162892614037,-573.3689311737394}}},
734cb93a386Sopenharmony_ci    },
735cb93a386Sopenharmony_ci};
736cb93a386Sopenharmony_ci
737cb93a386Sopenharmony_cistatic double endCtrlRatio(const SkDQuad quad) {
738cb93a386Sopenharmony_ci    SkDVector longEdge = quad[2] - quad[0];
739cb93a386Sopenharmony_ci    double longLen = longEdge.length();
740cb93a386Sopenharmony_ci    SkDVector shortEdge = quad[1] - quad[0];
741cb93a386Sopenharmony_ci    double shortLen = shortEdge.length();
742cb93a386Sopenharmony_ci    return longLen / shortLen;
743cb93a386Sopenharmony_ci}
744cb93a386Sopenharmony_ci
745cb93a386Sopenharmony_cistatic void computeMV(const SkDQuad& quad, const SkDVector& v, double m, SkDVector mV[2]) {
746cb93a386Sopenharmony_ci        SkDPoint mPta = {quad[1].fX - m * v.fY, quad[1].fY + m * v.fX};
747cb93a386Sopenharmony_ci        SkDPoint mPtb = {quad[1].fX + m * v.fY, quad[1].fY - m * v.fX};
748cb93a386Sopenharmony_ci        mV[0] = mPta - quad[0];
749cb93a386Sopenharmony_ci        mV[1] = mPtb - quad[0];
750cb93a386Sopenharmony_ci}
751cb93a386Sopenharmony_ci
752cb93a386Sopenharmony_cistatic double mDistance(skiatest::Reporter* reporter, bool agrees, const SkDQuad& q1,
753cb93a386Sopenharmony_ci        const SkDQuad& q2) {
754cb93a386Sopenharmony_ci    if (1 && agrees) {
755cb93a386Sopenharmony_ci        return SK_ScalarMax;
756cb93a386Sopenharmony_ci    }
757cb93a386Sopenharmony_ci    // how close is the angle from inflecting in the opposite direction?
758cb93a386Sopenharmony_ci    SkDVector v1 = q1[1] - q1[0];
759cb93a386Sopenharmony_ci    SkDVector v2 = q2[1] - q2[0];
760cb93a386Sopenharmony_ci    double dir = v1.crossCheck(v2);
761cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, dir != 0);
762cb93a386Sopenharmony_ci    // solve for opposite direction displacement scale factor == m
763cb93a386Sopenharmony_ci    // initial dir = v1.cross(v2) == v2.x * v1.y - v2.y * v1.x
764cb93a386Sopenharmony_ci    // displacement of q1[1] : dq1 = { -m * v1.y, m * v1.x } + q1[1]
765cb93a386Sopenharmony_ci    // straight angle when : v2.x * (dq1.y - q1[0].y) == v2.y * (dq1.x - q1[0].x)
766cb93a386Sopenharmony_ci    //                       v2.x * (m * v1.x + v1.y) == v2.y * (-m * v1.y + v1.x)
767cb93a386Sopenharmony_ci    // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x
768cb93a386Sopenharmony_ci    // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
769cb93a386Sopenharmony_ci    // m = v1.cross(v2) / v1.dot(v2)
770cb93a386Sopenharmony_ci    double div = v1.dot(v2);
771cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, div != 0);
772cb93a386Sopenharmony_ci    double m = dir / div;
773cb93a386Sopenharmony_ci    SkDVector mV1[2], mV2[2];
774cb93a386Sopenharmony_ci    computeMV(q1, v1, m, mV1);
775cb93a386Sopenharmony_ci    computeMV(q2, v2, m, mV2);
776cb93a386Sopenharmony_ci    double dist1 = v1.length() * m;
777cb93a386Sopenharmony_ci    double dist2 = v2.length() * m;
778cb93a386Sopenharmony_ci    if (gPathOpsAngleIdeasVerbose) {
779cb93a386Sopenharmony_ci        SkDebugf("%c r1=%1.9g r2=%1.9g m=%1.9g dist1=%1.9g dist2=%1.9g "
780cb93a386Sopenharmony_ci                " dir%c 1a=%1.9g 1b=%1.9g 2a=%1.9g 2b=%1.9g\n", agrees ? 'T' : 'F',
781cb93a386Sopenharmony_ci                endCtrlRatio(q1), endCtrlRatio(q2), m, dist1, dist2, dir > 0 ? '+' : '-',
782cb93a386Sopenharmony_ci                mV1[0].crossCheck(v2), mV1[1].crossCheck(v2),
783cb93a386Sopenharmony_ci                mV2[0].crossCheck(v1), mV2[1].crossCheck(v1));
784cb93a386Sopenharmony_ci    }
785cb93a386Sopenharmony_ci    if (1) {
786cb93a386Sopenharmony_ci        bool use1 = fabs(dist1) < fabs(dist2);
787cb93a386Sopenharmony_ci        if (gPathOpsAngleIdeasVerbose) {
788cb93a386Sopenharmony_ci            SkDebugf("%c dist=%1.9g r=%1.9g\n", agrees ? 'T' : 'F', use1 ? dist1 : dist2,
789cb93a386Sopenharmony_ci                use1 ? distEndRatio(dist1, q1) : distEndRatio(dist2, q2));
790cb93a386Sopenharmony_ci        }
791cb93a386Sopenharmony_ci        return fabs(use1 ? distEndRatio(dist1, q1) : distEndRatio(dist2, q2));
792cb93a386Sopenharmony_ci    }
793cb93a386Sopenharmony_ci    return SK_ScalarMax;
794cb93a386Sopenharmony_ci}
795cb93a386Sopenharmony_ci
796cb93a386Sopenharmony_cistatic void midPointAgrees(skiatest::Reporter* reporter, const SkDQuad& q1, const SkDQuad& q2,
797cb93a386Sopenharmony_ci        bool ccw) {
798cb93a386Sopenharmony_ci    SkDPoint mid1 = q1.ptAtT(0.5);
799cb93a386Sopenharmony_ci    SkDVector m1 = mid1 - q1[0];
800cb93a386Sopenharmony_ci    SkDPoint mid2 = q2.ptAtT(0.5);
801cb93a386Sopenharmony_ci    SkDVector m2 = mid2 - q2[0];
802cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, ccw ? m1.crossCheck(m2) < 0 : m1.crossCheck(m2) > 0);
803cb93a386Sopenharmony_ci}
804cb93a386Sopenharmony_ci
805cb93a386Sopenharmony_ciDEF_TEST(PathOpsAngleExtreme, reporter) {
806cb93a386Sopenharmony_ci    if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
807cb93a386Sopenharmony_ci        return;
808cb93a386Sopenharmony_ci    }
809cb93a386Sopenharmony_ci    double maxR = SK_ScalarMax;
810cb93a386Sopenharmony_ci    for (int index = 0; index < (int) SK_ARRAY_COUNT(extremeTests); ++index) {
811cb93a386Sopenharmony_ci        const QuadPts& qu1 = extremeTests[index][0];
812cb93a386Sopenharmony_ci        const QuadPts& qu2 = extremeTests[index][1];
813cb93a386Sopenharmony_ci        SkDQuad quad1, quad2;
814cb93a386Sopenharmony_ci        quad1.debugSet(qu1.fPts);
815cb93a386Sopenharmony_ci        quad2.debugSet(qu2.fPts);
816cb93a386Sopenharmony_ci        if (gPathOpsAngleIdeasVerbose) {
817cb93a386Sopenharmony_ci            SkDebugf("%s %d\n", __FUNCTION__, index);
818cb93a386Sopenharmony_ci        }
819cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, quad1[0] == quad2[0]);
820cb93a386Sopenharmony_ci        SkIntersections i;
821cb93a386Sopenharmony_ci        i.intersect(quad1, quad2);
822cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, i.used() == 1);
823cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, i.pt(0) == quad1[0]);
824cb93a386Sopenharmony_ci        int overlap = quadHullsOverlap(reporter, quad1, quad2);
825cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, overlap >= 0);
826cb93a386Sopenharmony_ci        SkDVector sweep[2], tweep[2];
827cb93a386Sopenharmony_ci        setQuadHullSweep(quad1, sweep);
828cb93a386Sopenharmony_ci        setQuadHullSweep(quad2, tweep);
829cb93a386Sopenharmony_ci        double s0xt0 = sweep[0].crossCheck(tweep[0]);
830cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, s0xt0 != 0);
831cb93a386Sopenharmony_ci        bool ccw = s0xt0 < 0;
832cb93a386Sopenharmony_ci        bool agrees = bruteForceCheck(reporter, quad1, quad2, ccw);
833cb93a386Sopenharmony_ci        maxR = std::min(maxR, mDistance(reporter, agrees, quad1, quad2));
834cb93a386Sopenharmony_ci        if (agrees) {
835cb93a386Sopenharmony_ci            continue;
836cb93a386Sopenharmony_ci        }
837cb93a386Sopenharmony_ci        midPointAgrees(reporter, quad1, quad2, !ccw);
838cb93a386Sopenharmony_ci        SkDQuad q1 = quad1;
839cb93a386Sopenharmony_ci        SkDQuad q2 = quad2;
840cb93a386Sopenharmony_ci        double loFail = 1;
841cb93a386Sopenharmony_ci        double hiPass = 2;
842cb93a386Sopenharmony_ci        // double vectors until t passes
843cb93a386Sopenharmony_ci        do {
844cb93a386Sopenharmony_ci            q1[1].fX = quad1[0].fX * (1 - hiPass) + quad1[1].fX * hiPass;
845cb93a386Sopenharmony_ci            q1[1].fY = quad1[0].fY * (1 - hiPass) + quad1[1].fY * hiPass;
846cb93a386Sopenharmony_ci            q2[1].fX = quad2[0].fX * (1 - hiPass) + quad2[1].fX * hiPass;
847cb93a386Sopenharmony_ci            q2[1].fY = quad2[0].fY * (1 - hiPass) + quad2[1].fY * hiPass;
848cb93a386Sopenharmony_ci            agrees = bruteForceCheck(reporter, q1, q2, ccw);
849cb93a386Sopenharmony_ci            maxR = std::min(maxR, mDistance(reporter, agrees, q1, q2));
850cb93a386Sopenharmony_ci            if (agrees) {
851cb93a386Sopenharmony_ci                break;
852cb93a386Sopenharmony_ci            }
853cb93a386Sopenharmony_ci            midPointAgrees(reporter, quad1, quad2, !ccw);
854cb93a386Sopenharmony_ci            loFail = hiPass;
855cb93a386Sopenharmony_ci            hiPass *= 2;
856cb93a386Sopenharmony_ci        } while (true);
857cb93a386Sopenharmony_ci        // binary search to find minimum pass
858cb93a386Sopenharmony_ci        double midTest = (loFail + hiPass) / 2;
859cb93a386Sopenharmony_ci        double step = (hiPass - loFail) / 4;
860cb93a386Sopenharmony_ci        while (step > FLT_EPSILON) {
861cb93a386Sopenharmony_ci            q1[1].fX = quad1[0].fX * (1 - midTest) + quad1[1].fX * midTest;
862cb93a386Sopenharmony_ci            q1[1].fY = quad1[0].fY * (1 - midTest) + quad1[1].fY * midTest;
863cb93a386Sopenharmony_ci            q2[1].fX = quad2[0].fX * (1 - midTest) + quad2[1].fX * midTest;
864cb93a386Sopenharmony_ci            q2[1].fY = quad2[0].fY * (1 - midTest) + quad2[1].fY * midTest;
865cb93a386Sopenharmony_ci            agrees = bruteForceCheck(reporter, q1, q2, ccw);
866cb93a386Sopenharmony_ci            maxR = std::min(maxR, mDistance(reporter, agrees, q1, q2));
867cb93a386Sopenharmony_ci            if (!agrees) {
868cb93a386Sopenharmony_ci                midPointAgrees(reporter, quad1, quad2, !ccw);
869cb93a386Sopenharmony_ci            }
870cb93a386Sopenharmony_ci            midTest += agrees ? -step : step;
871cb93a386Sopenharmony_ci            step /= 2;
872cb93a386Sopenharmony_ci        }
873cb93a386Sopenharmony_ci#ifdef SK_DEBUG
874cb93a386Sopenharmony_ci//        DumpQ(q1, q2, 999);
875cb93a386Sopenharmony_ci#endif
876cb93a386Sopenharmony_ci    }
877cb93a386Sopenharmony_ci    if (gPathOpsAngleIdeasVerbose) {
878cb93a386Sopenharmony_ci        SkDebugf("maxR=%1.9g\n", maxR);
879cb93a386Sopenharmony_ci    }
880cb93a386Sopenharmony_ci}
881