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