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