1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2014 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/utils/SkRandom.h"
8cb93a386Sopenharmony_ci#include "src/pathops/SkIntersections.h"
9cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsCubic.h"
10cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsLine.h"
11cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsQuad.h"
12cb93a386Sopenharmony_ci#include "src/pathops/SkReduceOrder.h"
13cb93a386Sopenharmony_ci#include "tests/PathOpsTestCommon.h"
14cb93a386Sopenharmony_ci#include "tests/Test.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_cistatic bool gPathOpsCubicLineIntersectionIdeasVerbose = false;
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_cistatic struct CubicLineFailures {
19cb93a386Sopenharmony_ci    CubicPts c;
20cb93a386Sopenharmony_ci    double t;
21cb93a386Sopenharmony_ci    SkDPoint p;
22cb93a386Sopenharmony_ci} cubicLineFailures[] = {
23cb93a386Sopenharmony_ci    {{{{-164.3726806640625, 36.826904296875}, {-189.045166015625, -953.2220458984375},
24cb93a386Sopenharmony_ci        {926.505859375, -897.36175537109375}, {-139.33489990234375, 204.40771484375}}},
25cb93a386Sopenharmony_ci        0.37329583, {107.54935269006289, -632.13736293162208}},
26cb93a386Sopenharmony_ci    {{{{784.056884765625, -554.8350830078125}, {67.5489501953125, 509.0224609375},
27cb93a386Sopenharmony_ci        {-447.713134765625, 751.375}, {415.7784423828125, 172.22265625}}},
28cb93a386Sopenharmony_ci        0.660005242, {-32.973148967736151, 478.01341797403569}},
29cb93a386Sopenharmony_ci    {{{{-580.6834716796875, -127.044921875}, {-872.8983154296875, -945.54302978515625},
30cb93a386Sopenharmony_ci        {260.8092041015625, -909.34991455078125}, {-976.2125244140625, -18.46551513671875}}},
31cb93a386Sopenharmony_ci        0.578826774, {-390.17910153915489, -687.21144412296007}},
32cb93a386Sopenharmony_ci};
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ciint cubicLineFailuresCount = (int) SK_ARRAY_COUNT(cubicLineFailures);
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_cidouble measuredSteps[] = {
37cb93a386Sopenharmony_ci    9.15910731e-007, 8.6600277e-007, 7.4122059e-007, 6.92087618e-007, 8.35290245e-007,
38cb93a386Sopenharmony_ci    3.29763199e-007, 5.07547773e-007, 4.41294224e-007, 0, 0,
39cb93a386Sopenharmony_ci    3.76879167e-006, 1.06126249e-006, 2.36873967e-006, 1.62421134e-005, 3.09103599e-005,
40cb93a386Sopenharmony_ci    4.38917976e-005, 0.000112348938, 0.000243149242, 0.000433174114, 0.00170880232,
41cb93a386Sopenharmony_ci    0.00272619724, 0.00518844604, 0.000352621078, 0.00175960064, 0.027875185,
42cb93a386Sopenharmony_ci    0.0351329803, 0.103964925,
43cb93a386Sopenharmony_ci};
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_ci/* last output : errors=3121
46cb93a386Sopenharmony_ci    9.1796875e-007 8.59375e-007 7.5e-007 6.875e-007 8.4375e-007
47cb93a386Sopenharmony_ci    3.125e-007 5e-007 4.375e-007 0 0
48cb93a386Sopenharmony_ci    3.75e-006 1.09375e-006 2.1875e-006 1.640625e-005 3.0859375e-005
49cb93a386Sopenharmony_ci    4.38964844e-005 0.000112304687 0.000243164063 0.000433181763 0.00170898437
50cb93a386Sopenharmony_ci    0.00272619247 0.00518844604 0.000352621078 0.00175960064 0.027875185
51cb93a386Sopenharmony_ci    0.0351329803 0.103964925
52cb93a386Sopenharmony_ci*/
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_cistatic double binary_search(const SkDCubic& cubic, double step, const SkDPoint& pt, double t,
55cb93a386Sopenharmony_ci        int* iters) {
56cb93a386Sopenharmony_ci    double firstStep = step;
57cb93a386Sopenharmony_ci    do {
58cb93a386Sopenharmony_ci        *iters += 1;
59cb93a386Sopenharmony_ci        SkDPoint cubicAtT = cubic.ptAtT(t);
60cb93a386Sopenharmony_ci        if (cubicAtT.approximatelyEqual(pt)) {
61cb93a386Sopenharmony_ci            break;
62cb93a386Sopenharmony_ci        }
63cb93a386Sopenharmony_ci        double calcX = cubicAtT.fX - pt.fX;
64cb93a386Sopenharmony_ci        double calcY = cubicAtT.fY - pt.fY;
65cb93a386Sopenharmony_ci        double calcDist = calcX * calcX + calcY * calcY;
66cb93a386Sopenharmony_ci        if (step == 0) {
67cb93a386Sopenharmony_ci            SkDebugf("binary search failed: step=%1.9g cubic=", firstStep);
68cb93a386Sopenharmony_ci            cubic.dump();
69cb93a386Sopenharmony_ci            SkDebugf(" t=%1.9g ", t);
70cb93a386Sopenharmony_ci            pt.dump();
71cb93a386Sopenharmony_ci            SkDebugf("\n");
72cb93a386Sopenharmony_ci            return -1;
73cb93a386Sopenharmony_ci        }
74cb93a386Sopenharmony_ci        double lastStep = step;
75cb93a386Sopenharmony_ci        step /= 2;
76cb93a386Sopenharmony_ci        SkDPoint lessPt = cubic.ptAtT(t - lastStep);
77cb93a386Sopenharmony_ci        double lessX = lessPt.fX - pt.fX;
78cb93a386Sopenharmony_ci        double lessY = lessPt.fY - pt.fY;
79cb93a386Sopenharmony_ci        double lessDist = lessX * lessX + lessY * lessY;
80cb93a386Sopenharmony_ci        // use larger x/y difference to choose step
81cb93a386Sopenharmony_ci        if (calcDist > lessDist) {
82cb93a386Sopenharmony_ci            t -= step;
83cb93a386Sopenharmony_ci            t = std::max(0., t);
84cb93a386Sopenharmony_ci        } else {
85cb93a386Sopenharmony_ci            SkDPoint morePt = cubic.ptAtT(t + lastStep);
86cb93a386Sopenharmony_ci            double moreX = morePt.fX - pt.fX;
87cb93a386Sopenharmony_ci            double moreY = morePt.fY - pt.fY;
88cb93a386Sopenharmony_ci            double moreDist = moreX * moreX + moreY * moreY;
89cb93a386Sopenharmony_ci            if (calcDist <= moreDist) {
90cb93a386Sopenharmony_ci                continue;
91cb93a386Sopenharmony_ci            }
92cb93a386Sopenharmony_ci            t += step;
93cb93a386Sopenharmony_ci            t = std::min(1., t);
94cb93a386Sopenharmony_ci        }
95cb93a386Sopenharmony_ci    } while (true);
96cb93a386Sopenharmony_ci    return t;
97cb93a386Sopenharmony_ci}
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci#if 0
100cb93a386Sopenharmony_cistatic bool r2check(double A, double B, double C, double D, double* R2MinusQ3Ptr) {
101cb93a386Sopenharmony_ci    if (approximately_zero(A)
102cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(A, B)
103cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(A, C)
104cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(A, D)) {  // we're just a quadratic
105cb93a386Sopenharmony_ci        return false;
106cb93a386Sopenharmony_ci    }
107cb93a386Sopenharmony_ci    if (approximately_zero_when_compared_to(D, A)
108cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(D, B)
109cb93a386Sopenharmony_ci            && approximately_zero_when_compared_to(D, C)) {  // 0 is one root
110cb93a386Sopenharmony_ci        return false;
111cb93a386Sopenharmony_ci    }
112cb93a386Sopenharmony_ci    if (approximately_zero(A + B + C + D)) {  // 1 is one root
113cb93a386Sopenharmony_ci        return false;
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci    double a, b, c;
116cb93a386Sopenharmony_ci    {
117cb93a386Sopenharmony_ci        double invA = 1 / A;
118cb93a386Sopenharmony_ci        a = B * invA;
119cb93a386Sopenharmony_ci        b = C * invA;
120cb93a386Sopenharmony_ci        c = D * invA;
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci    double a2 = a * a;
123cb93a386Sopenharmony_ci    double Q = (a2 - b * 3) / 9;
124cb93a386Sopenharmony_ci    double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
125cb93a386Sopenharmony_ci    double R2 = R * R;
126cb93a386Sopenharmony_ci    double Q3 = Q * Q * Q;
127cb93a386Sopenharmony_ci    double R2MinusQ3 = R2 - Q3;
128cb93a386Sopenharmony_ci    *R2MinusQ3Ptr = R2MinusQ3;
129cb93a386Sopenharmony_ci    return true;
130cb93a386Sopenharmony_ci}
131cb93a386Sopenharmony_ci#endif
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci/* What is the relationship between the accuracy of the root in range and the magnitude of all
134cb93a386Sopenharmony_ci   roots? To find out, create a bunch of cubics, and measure */
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ciDEF_TEST(PathOpsCubicLineRoots, reporter) {
137cb93a386Sopenharmony_ci    if (!gPathOpsCubicLineIntersectionIdeasVerbose) {  // slow; exclude it by default
138cb93a386Sopenharmony_ci        return;
139cb93a386Sopenharmony_ci    }
140cb93a386Sopenharmony_ci    SkRandom ran;
141cb93a386Sopenharmony_ci    double worstStep[256] = {0};
142cb93a386Sopenharmony_ci    int errors = 0;
143cb93a386Sopenharmony_ci    int iters = 0;
144cb93a386Sopenharmony_ci    double smallestR2 = 0;
145cb93a386Sopenharmony_ci    double largestR2 = 0;
146cb93a386Sopenharmony_ci    for (int index = 0; index < 1000000000; ++index) {
147cb93a386Sopenharmony_ci        SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
148cb93a386Sopenharmony_ci        CubicPts cuPts = {{origin,
149cb93a386Sopenharmony_ci                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
150cb93a386Sopenharmony_ci                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
151cb93a386Sopenharmony_ci                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
152cb93a386Sopenharmony_ci        }};
153cb93a386Sopenharmony_ci        // construct a line at a known intersection
154cb93a386Sopenharmony_ci        double t = ran.nextRangeF(0, 1);
155cb93a386Sopenharmony_ci        SkDCubic cubic;
156cb93a386Sopenharmony_ci        cubic.debugSet(cuPts.fPts);
157cb93a386Sopenharmony_ci        SkDPoint pt = cubic.ptAtT(t);
158cb93a386Sopenharmony_ci        // skip answers with no intersections (although note the bug!) or two, or more
159cb93a386Sopenharmony_ci        // see if the line / cubic has a fun range of roots
160cb93a386Sopenharmony_ci        double A, B, C, D;
161cb93a386Sopenharmony_ci        SkDCubic::Coefficients(&cubic[0].fY, &A, &B, &C, &D);
162cb93a386Sopenharmony_ci        D -= pt.fY;
163cb93a386Sopenharmony_ci        double allRoots[3] = {0}, validRoots[3] = {0};
164cb93a386Sopenharmony_ci        int realRoots = SkDCubic::RootsReal(A, B, C, D, allRoots);
165cb93a386Sopenharmony_ci        int valid = SkDQuad::AddValidTs(allRoots, realRoots, validRoots);
166cb93a386Sopenharmony_ci        if (valid != 1) {
167cb93a386Sopenharmony_ci            continue;
168cb93a386Sopenharmony_ci        }
169cb93a386Sopenharmony_ci        if (realRoots == 1) {
170cb93a386Sopenharmony_ci            continue;
171cb93a386Sopenharmony_ci        }
172cb93a386Sopenharmony_ci        t = validRoots[0];
173cb93a386Sopenharmony_ci        SkDPoint calcPt = cubic.ptAtT(t);
174cb93a386Sopenharmony_ci        if (calcPt.approximatelyEqual(pt)) {
175cb93a386Sopenharmony_ci            continue;
176cb93a386Sopenharmony_ci        }
177cb93a386Sopenharmony_ci#if 0
178cb93a386Sopenharmony_ci        double R2MinusQ3;
179cb93a386Sopenharmony_ci        if (r2check(A, B, C, D, &R2MinusQ3)) {
180cb93a386Sopenharmony_ci            smallestR2 = std::min(smallestR2, R2MinusQ3);
181cb93a386Sopenharmony_ci            largestR2 = std::max(largestR2, R2MinusQ3);
182cb93a386Sopenharmony_ci        }
183cb93a386Sopenharmony_ci#endif
184cb93a386Sopenharmony_ci        double largest = std::max(fabs(allRoots[0]), fabs(allRoots[1]));
185cb93a386Sopenharmony_ci        if (realRoots == 3) {
186cb93a386Sopenharmony_ci            largest = std::max(largest, fabs(allRoots[2]));
187cb93a386Sopenharmony_ci        }
188cb93a386Sopenharmony_ci        int largeBits;
189cb93a386Sopenharmony_ci        if (largest <= 1) {
190cb93a386Sopenharmony_ci#if 0
191cb93a386Sopenharmony_ci            SkDebugf("realRoots=%d (%1.9g, %1.9g, %1.9g) valid=%d (%1.9g, %1.9g, %1.9g)\n",
192cb93a386Sopenharmony_ci                realRoots, allRoots[0], allRoots[1], allRoots[2], valid, validRoots[0],
193cb93a386Sopenharmony_ci                validRoots[1], validRoots[2]);
194cb93a386Sopenharmony_ci#endif
195cb93a386Sopenharmony_ci            double smallest = std::min(allRoots[0], allRoots[1]);
196cb93a386Sopenharmony_ci            if (realRoots == 3) {
197cb93a386Sopenharmony_ci                smallest = std::min(smallest, allRoots[2]);
198cb93a386Sopenharmony_ci            }
199cb93a386Sopenharmony_ci            SkASSERT_RELEASE(smallest < 0);
200cb93a386Sopenharmony_ci            SkASSERT_RELEASE(smallest >= -1);
201cb93a386Sopenharmony_ci            largeBits = 0;
202cb93a386Sopenharmony_ci        } else {
203cb93a386Sopenharmony_ci            frexp(largest, &largeBits);
204cb93a386Sopenharmony_ci            SkASSERT_RELEASE(largeBits >= 0);
205cb93a386Sopenharmony_ci            SkASSERT_RELEASE(largeBits < 256);
206cb93a386Sopenharmony_ci        }
207cb93a386Sopenharmony_ci        double step = 1e-6;
208cb93a386Sopenharmony_ci        if (largeBits > 21) {
209cb93a386Sopenharmony_ci            step = 1e-1;
210cb93a386Sopenharmony_ci        } else if (largeBits > 18) {
211cb93a386Sopenharmony_ci            step = 1e-2;
212cb93a386Sopenharmony_ci        } else if (largeBits > 15) {
213cb93a386Sopenharmony_ci            step = 1e-3;
214cb93a386Sopenharmony_ci        } else if (largeBits > 12) {
215cb93a386Sopenharmony_ci            step = 1e-4;
216cb93a386Sopenharmony_ci        } else if (largeBits > 9) {
217cb93a386Sopenharmony_ci            step = 1e-5;
218cb93a386Sopenharmony_ci        }
219cb93a386Sopenharmony_ci        double diff;
220cb93a386Sopenharmony_ci        do {
221cb93a386Sopenharmony_ci            double newT = binary_search(cubic, step, pt, t, &iters);
222cb93a386Sopenharmony_ci            if (newT >= 0) {
223cb93a386Sopenharmony_ci                diff = fabs(t - newT);
224cb93a386Sopenharmony_ci                break;
225cb93a386Sopenharmony_ci            }
226cb93a386Sopenharmony_ci            step *= 1.5;
227cb93a386Sopenharmony_ci            SkASSERT_RELEASE(step < 1);
228cb93a386Sopenharmony_ci        } while (true);
229cb93a386Sopenharmony_ci        worstStep[largeBits] = std::max(worstStep[largeBits], diff);
230cb93a386Sopenharmony_ci#if 0
231cb93a386Sopenharmony_ci        {
232cb93a386Sopenharmony_ci            cubic.dump();
233cb93a386Sopenharmony_ci            SkDebugf("\n");
234cb93a386Sopenharmony_ci            SkDLine line = {{{pt.fX - 1, pt.fY}, {pt.fX + 1, pt.fY}}};
235cb93a386Sopenharmony_ci            line.dump();
236cb93a386Sopenharmony_ci            SkDebugf("\n");
237cb93a386Sopenharmony_ci        }
238cb93a386Sopenharmony_ci#endif
239cb93a386Sopenharmony_ci        ++errors;
240cb93a386Sopenharmony_ci    }
241cb93a386Sopenharmony_ci    SkDebugf("errors=%d avgIter=%1.9g", errors, (double) iters / errors);
242cb93a386Sopenharmony_ci    SkDebugf(" steps: ");
243cb93a386Sopenharmony_ci    int worstLimit = SK_ARRAY_COUNT(worstStep);
244cb93a386Sopenharmony_ci    while (worstStep[--worstLimit] == 0) ;
245cb93a386Sopenharmony_ci    for (int idx2 = 0; idx2 <= worstLimit; ++idx2) {
246cb93a386Sopenharmony_ci        SkDebugf("%1.9g ", worstStep[idx2]);
247cb93a386Sopenharmony_ci    }
248cb93a386Sopenharmony_ci    SkDebugf("\n");
249cb93a386Sopenharmony_ci    SkDebugf("smallestR2=%1.9g largestR2=%1.9g\n", smallestR2, largestR2);
250cb93a386Sopenharmony_ci}
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_cistatic double testOneFailure(const CubicLineFailures& failure) {
253cb93a386Sopenharmony_ci    const CubicPts& c = failure.c;
254cb93a386Sopenharmony_ci    SkDCubic cubic;
255cb93a386Sopenharmony_ci    cubic.debugSet(c.fPts);
256cb93a386Sopenharmony_ci    const SkDPoint& pt = failure.p;
257cb93a386Sopenharmony_ci    double A, B, C, D;
258cb93a386Sopenharmony_ci    SkDCubic::Coefficients(&cubic[0].fY, &A, &B, &C, &D);
259cb93a386Sopenharmony_ci    D -= pt.fY;
260cb93a386Sopenharmony_ci    double allRoots[3] = {0}, validRoots[3] = {0};
261cb93a386Sopenharmony_ci    int realRoots = SkDCubic::RootsReal(A, B, C, D, allRoots);
262cb93a386Sopenharmony_ci    int valid = SkDQuad::AddValidTs(allRoots, realRoots, validRoots);
263cb93a386Sopenharmony_ci    SkASSERT_RELEASE(valid == 1);
264cb93a386Sopenharmony_ci    SkASSERT_RELEASE(realRoots != 1);
265cb93a386Sopenharmony_ci    double t = validRoots[0];
266cb93a386Sopenharmony_ci    SkDPoint calcPt = cubic.ptAtT(t);
267cb93a386Sopenharmony_ci    SkASSERT_RELEASE(!calcPt.approximatelyEqual(pt));
268cb93a386Sopenharmony_ci    int iters = 0;
269cb93a386Sopenharmony_ci    double newT = binary_search(cubic, 0.1, pt, t, &iters);
270cb93a386Sopenharmony_ci    return newT;
271cb93a386Sopenharmony_ci}
272cb93a386Sopenharmony_ci
273cb93a386Sopenharmony_ciDEF_TEST(PathOpsCubicLineFailures, reporter) {
274cb93a386Sopenharmony_ci    return;  // disable for now
275cb93a386Sopenharmony_ci    for (int index = 0; index < cubicLineFailuresCount; ++index) {
276cb93a386Sopenharmony_ci        const CubicLineFailures& failure = cubicLineFailures[index];
277cb93a386Sopenharmony_ci        double newT = testOneFailure(failure);
278cb93a386Sopenharmony_ci        SkASSERT_RELEASE(newT >= 0);
279cb93a386Sopenharmony_ci    }
280cb93a386Sopenharmony_ci}
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_ciDEF_TEST(PathOpsCubicLineOneFailure, reporter) {
283cb93a386Sopenharmony_ci    return;  // disable for now
284cb93a386Sopenharmony_ci    const CubicLineFailures& failure = cubicLineFailures[1];
285cb93a386Sopenharmony_ci    double newT = testOneFailure(failure);
286cb93a386Sopenharmony_ci    SkASSERT_RELEASE(newT >= 0);
287cb93a386Sopenharmony_ci}
288