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