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