1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #include "PathOpsTestCommon.h" 8 #include "SkOpSegment.h" 9 #include "SkTArray.h" 10 #include "Test.h" 11 12 static const SkPoint cubics[][4] = { 13 /* 0 */ {{0, 1}, {2, 6}, {4, 2}, {5, 3}}, 14 /* 1 */ {{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}}, 15 /* 2 */ {{132, 11419}, {130.89543151855469f, 11419}, {130, 11418.1044921875f}, {130, 11417}}, 16 /* 3 */ {{130.04275512695312f, 11417.4130859375f}, {130.23307800292969f, 11418.3193359375f}, 17 {131.03709411621094f, 11419}, {132, 11419}}, 18 /* 4 */ {{0,1}, {0,5}, {4,1}, {6,4}}, 19 /* 5 */ {{1,5}, {4,6}, {1,0}, {4,0}}, 20 /* 6 */ {{0,1}, {0,4}, {5,1}, {6,4}}, 21 /* 7 */ {{0,1}, {1,2}, {1,0}, {6,1}}, 22 /* 8 */ {{0,3}, {0,1}, {2,0}, {1,0}}, 23 /* 9 */ {{189,7}, {189,5.3431458473205566f}, {190.3431396484375f,4}, {192,4}}, 24 /* 10 */ {{0,1}, {1,3}, {1,0}, {6,4}}, 25 /* 11 */ {{0,1}, {2,3}, {2,1}, {4,3}}, 26 /* 12 */ {{1,2}, {3,4}, {1,0}, {3,2}}, 27 /* 13 */ {{0,1}, {4,6}, {4,3}, {5,4}}, 28 /* 14 */ {{806,11419}, {806.962890625f,11419}, {807.76690673828125f,11418.3193359375f}, {807.957275390625f,11417.4130859375f}}, 29 /* 15 */ {{808,11417}, {808,11418.1044921875f}, {807.10455322265625f,11419}, {806,11419}}, 30 /* 16 */ {{132,11419}, {130.89543151855469f,11419}, {130,11418.1044921875f}, {130,11417}}, 31 /* 17 */ {{130.04275512695312f,11417.4130859375f}, {130.23312377929687f,11418.3193359375f}, {131.03707885742187f,11419}, {132,11419}}, 32 /* 18 */ {{1006.6951293945312f,291}, {1023.263671875f,291}, {1033.8402099609375f,304.43145751953125f}, {1030.318359375f,321}}, 33 }; 34 35 static const SkPoint quads[][3] = { 36 /* 0 */ {{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}}, 37 /* 1 */ {{304.24319458007812f,591.75677490234375f}, {306,593.51470947265625f}, {306,596}}, 38 /* 2 */ {{0,0}, {3,1}, {0,3}}, 39 /* 3 */ {{0,1}, {3,1}, {0,2}}, 40 }; 41 42 static const SkPoint lines[][2] = { 43 /* 0 */ {{6, 2}, {2, 4}}, 44 /* 1 */ {{306,617}, {306,590}}, 45 /* 2 */ {{306,596}, {306,617}}, 46 /* 3 */ {{6,4}, {0,1}}, 47 /* 4 */ {{6,1}, {0,1}}, 48 /* 5 */ {{1,0}, {0,3}}, 49 /* 6 */ {{246,4}, {189,4}}, 50 /* 7 */ {{192,4}, {243,4}}, 51 /* 8 */ {{4,3}, {0,1}}, 52 /* 9 */ {{3,2}, {1,2}}, 53 /* 10 */ {{6,4}, {3,4}}, 54 /* 11 */ {{979.30487060546875f,561}, {1036.695068359375f,291}}, 55 }; 56 57 struct SortSet { 58 const SkPoint* ptData; 59 int ptCount; 60 double tStart; 61 double tEnd; 62 SkPoint endPt; 63 }; 64 65 /*static const SortSet set1[] = { 66 {cubics[0], 4, 0.66666987081928919, 0.875, {0, 0}}, 67 {lines[0], 2, 0.574070336, 0.388888889, {0, 0}}, 68 {cubics[0], 4, 0.66666987081928919, 0.4050371120499307, {0, 0}}, 69 {lines[0], 2, 0.574070336, 0.9140625, {0, 0}}, 70 }; 71 72 static const SortSet set1a[] = { 73 {cubics[0], 4, 0.666666667, 0.405037112, {4.58007812f,2.83203125f}}, 74 {lines[0], 2, 0.574074074, 0.9140625, {4.44444466f,2.77777767f}}, 75 };*/ 76 77 static const SortSet set2[] = { 78 {cubics[0], 4, 0.666666667, 0.875, {0, 0}}, 79 {lines[0], 2, 0.574074074, 0.388888889, {0, 0}}, 80 {cubics[0], 4, 0.666666667, 0.405037112, {0, 0}}, 81 {lines[0], 2, 0.574074074, 0.9140625, {0, 0}}, 82 }; 83 84 static const SortSet set3[] = { 85 {cubics[1], 4, 0, 1, {0, 0}}, 86 {quads[0], 3, 1, 0, {0, 0}}, 87 }; 88 89 /*static const SortSet set4[] = { 90 {cubics[2], 4, 0.812114222, 1, {0, 0}}, 91 {cubics[3], 4, 0.0684734759, 0, {0, 0}}, 92 };*/ 93 94 static const SortSet set5[] = { 95 {lines[1], 2, 0.777777778, 1, {0, 0}}, 96 {quads[1], 3, 1, 4.34137342e-06, {0, 0}}, 97 {lines[2], 2, 0, 1, {0, 0}}, 98 }; 99 100 static const SortSet set5a[] = { 101 {lines[1], 2, 0.777777778, 1, {306,590}}, 102 {quads[1], 3, 1, 4.34137342e-06, {304.243195f,591.756775f}}, 103 {lines[2], 2, 0, 1, {306,617}}, 104 }; 105 106 static const SortSet set6[] = { 107 {lines[3], 2, 0.407407407, 0.554627832, {0, 0}}, 108 {cubics[4], 4, 0.666666667, 0.548022446, {0, 0}}, 109 {lines[3], 2, 0.407407407, 0, {0, 0}}, 110 {cubics[4], 4, 0.666666667, 1, {0, 0}}, 111 }; 112 113 static const SortSet set6a[] = { 114 {lines[3], 2, 0.407407407, 0.554627832, {2.6722331f,2.33611655f}}, 115 {cubics[4], 4, 0.666666667, 0.548022446, {2.61642241f,2.83718514f}}, 116 {lines[3], 2, 0.407407407, 0, {6,4}}, 117 {cubics[4], 4, 0.666666667, 1, {6,4}}, 118 }; 119 120 static const SortSet set7[] = { 121 {cubics[5], 4, 0.545233342, 0.545454545, {0, 0}}, 122 {cubics[6], 4, 0.484938134, 0.484805744, {0, 0}}, 123 {cubics[5], 4, 0.545233342, 0, {0, 0}}, 124 {cubics[6], 4, 0.484938134, 0.545454545, {0, 0}}, 125 }; 126 127 static const SortSet set8[] = { 128 {cubics[7], 4, 0.5, 0.522986744, {0, 0}}, 129 {lines[4], 2, 0.75, 1, {0, 0}}, 130 {cubics[7], 4, 0.5, 0, {0, 0}}, 131 {lines[4], 2, 0.75, 0.737654321, {0, 0}}, 132 }; 133 134 static const SortSet set8a[] = { 135 {cubics[7], 4, 0.5, 0.522986744, {1.60668361f,0.965592742f}}, 136 {lines[4], 2, 0.75, 1, {0,1}}, 137 {cubics[7], 4, 0.5, 0, {0,1}}, 138 {lines[4], 2, 0.75, 0.737654321, {1.57407403f,1}}, 139 }; 140 141 static const SortSet set9[] = { 142 {cubics[8], 4, 0.4, 1, {0, 0}}, 143 {lines[5], 2, 0.36, 0, {0, 0}}, 144 {cubics[8], 4, 0.4, 0.394675838, {0, 0}}, 145 {lines[5], 2, 0.36, 0.363999782, {0, 0}}, 146 }; 147 148 static const SortSet set10[] = { 149 {lines[6], 2, 0.947368421, 1, {0, 0}}, 150 {cubics[9], 4, 1, 0.500000357, {0, 0}}, 151 {lines[7], 2, 0, 1, {0, 0}}, 152 }; 153 154 static const SortSet set11[] = { 155 {lines[3], 2, 0.75, 1, {0, 0}}, 156 {cubics[10], 4, 0.5, 0.228744269, {0, 0}}, 157 {lines[3], 2, 0.75, 0.627112191, {0, 0}}, 158 {cubics[10], 4, 0.5, 0.6339746, {0, 0}}, 159 }; 160 161 static const SortSet set12[] = { 162 {cubics[12], 4, 0.5, 1, {0, 0}}, 163 {lines[8], 2, 0.5, 1, {0, 0}}, 164 {cubics[11], 4, 0.5, 0, {0, 0}}, 165 {lines[9], 2, 0.5, 1, {0, 0}}, 166 {cubics[12], 4, 0.5, 0, {0, 0}}, 167 {lines[8], 2, 0.5, 0, {0, 0}}, 168 {cubics[11], 4, 0.5, 1, {0, 0}}, 169 {lines[9], 2, 0.5, 0, {0, 0}}, 170 }; 171 172 /*static const SortSet set13[] = { 173 {cubics[13], 4, 0.5, 0.400631046, {0, 0}}, 174 {lines[10], 2, 0.791666667, 0.928, {0, 0}}, 175 {lines[10], 2, 0.791666667, 0.333333333, {0, 0}}, 176 {cubics[13], 4, 0.5, 0.866666667, {0, 0}}, 177 };*/ 178 179 static const SortSet set14[] = { 180 {quads[2], 3, 0.5, 0.310102051, {0, 0}}, 181 {quads[3], 3, 0.5, 0.2, {0, 0}}, 182 {quads[3], 3, 0.5, 0.770156212, {0, 0}}, 183 {quads[2], 3, 0.5, 0.7, {0, 0}}, 184 }; 185 186 /*static const SortSet set15[] = { 187 {cubics[14], 4, 0.93081374, 1, {0, 0}}, 188 {cubics[15], 4, 0.188518131, 0, {0, 0}}, 189 {cubics[14], 4, 0.93081374, 0, {0, 0}}, 190 };*/ 191 192 static const SortSet set16[] = { 193 {cubics[17], 4, 0.0682619216, 0, {130.042755f,11417.4131f}}, 194 {cubics[16], 4, 0.812302088, 1, {130,11417}}, 195 {cubics[17], 4, 0.0682619216, 1, {132,11419}}, 196 }; 197 198 static const SortSet set17[] = { 199 {lines[11], 2, 0.888889581, 1, {0, 0}}, 200 {cubics[18], 4, 0.999996241, 0, {0, 0}}, 201 {lines[11], 2, 0.888889581, 0, {0, 0}}, 202 {cubics[18], 4, 0.999996241, 1, {0, 0}}, 203 }; 204 205 struct SortSetTests { 206 const char* name; 207 const SortSet* set; 208 size_t count; 209 SkPoint startPt; 210 }; 211 212 #define TEST_ENTRY(name) #name, name, SK_ARRAY_COUNT(name) 213 214 static const SortSetTests tests[] = { 215 { TEST_ENTRY(set17), {0, 0}}, 216 { TEST_ENTRY(set16), {130.090179f,11417.5957f} }, 217 // { TEST_ENTRY(set15), {0, 0}}, 218 { TEST_ENTRY(set14), {0, 0}}, 219 // { TEST_ENTRY(set13), {0, 0}}, 220 { TEST_ENTRY(set12), {0, 0}}, 221 { TEST_ENTRY(set11), {0, 0}}, 222 { TEST_ENTRY(set10), {0, 0}}, 223 { TEST_ENTRY(set9), {0, 0}}, 224 { TEST_ENTRY(set6a), {3.55555558f,2.77777767f} }, 225 { TEST_ENTRY(set8a), {1.5f,1} }, 226 { TEST_ENTRY(set8), {0, 0}}, 227 { TEST_ENTRY(set7), {0, 0}}, 228 { TEST_ENTRY(set6a), {3.55555558f,2.77777767f} }, 229 { TEST_ENTRY(set6), {0, 0}}, 230 { TEST_ENTRY(set5a), {306,596} }, 231 { TEST_ENTRY(set5), {0, 0}}, 232 // { TEST_ENTRY(set4), {0, 0}}, 233 { TEST_ENTRY(set3), {0, 0}}, 234 { TEST_ENTRY(set2), {0, 0}}, 235 // { TEST_ENTRY(set1a), {3.70370364f,3.14814806f} }, 236 // { TEST_ENTRY(set1), {0, 0}}, 237 }; 238 239 #undef TEST_ENTRY 240 241 static void setup(const SortSet* set, const size_t idx, 242 SkOpSegment* seg, int* ts, const SkPoint& startPt) { 243 SkPoint start, end; 244 const SkPoint* data = set[idx].ptData; 245 bool useIntersectPt = startPt.fX != 0 || startPt.fY != 0; 246 if (useIntersectPt) { 247 start = startPt; 248 end = set[idx].endPt; 249 } 250 switch(set[idx].ptCount) { 251 case 2: { 252 SkASSERT(ValidPoints(data, 2)); 253 seg->addLine(data, false, false); 254 SkDLine dLine; 255 dLine.set(set[idx].ptData); 256 SkASSERT(ValidLine(dLine)); 257 if (useIntersectPt) { 258 break; 259 } 260 start = dLine.ptAtT(set[idx].tStart).asSkPoint(); 261 end = dLine.ptAtT(set[idx].tEnd).asSkPoint(); 262 } break; 263 case 3: { 264 SkASSERT(ValidPoints(data, 3)); 265 seg->addQuad(data, false, false); 266 SkDQuad dQuad; 267 dQuad.set(set[idx].ptData); 268 SkASSERT(ValidQuad(dQuad)); 269 if (useIntersectPt) { 270 break; 271 } 272 start = dQuad.ptAtT(set[idx].tStart).asSkPoint(); 273 end = dQuad.ptAtT(set[idx].tEnd).asSkPoint(); 274 } break; 275 case 4: { 276 SkASSERT(ValidPoints(data, 4)); 277 seg->addCubic(data, false, false); 278 SkDCubic dCubic; 279 dCubic.set(set[idx].ptData); 280 SkASSERT(ValidCubic(dCubic)); 281 if (useIntersectPt) { 282 break; 283 } 284 start = dCubic.ptAtT(set[idx].tStart).asSkPoint(); 285 end = dCubic.ptAtT(set[idx].tEnd).asSkPoint(); 286 } break; 287 } 288 double tStart = set[idx].tStart; 289 double tEnd = set[idx].tEnd; 290 seg->addT(NULL, start, tStart); 291 seg->addT(NULL, end, tEnd); 292 if (tStart != 0 && tEnd != 0) { 293 seg->addT(NULL, set[idx].ptData[0], 0); 294 } 295 if (tStart != 1 && tEnd != 1) { 296 seg->addT(NULL, set[idx].ptData[set[idx].ptCount - 1], 1); 297 } 298 int tIndex = 0; 299 ts[0] = 0; 300 ts[1] = 1; 301 do { 302 if (seg->t(tIndex) == set[idx].tStart) { 303 ts[0] = tIndex; 304 } 305 if (seg->t(tIndex) == set[idx].tEnd) { 306 ts[1] = tIndex; 307 } 308 if (seg->t(tIndex) >= 1) { 309 break; 310 } 311 } while (++tIndex); 312 } 313 314 static void testOne(skiatest::Reporter* reporter, const SortSetTests& test) { 315 SkTDArray<SkOpAngle> angles; 316 bool unsortable = false; 317 bool unorderable = false; 318 SkTArray<SkOpSegment> segs; 319 for (size_t idx = 0; idx < test.count; ++idx) { 320 int ts[2]; 321 const SortSet* set = test.set; 322 SkOpSegment& seg = segs.push_back(); 323 setup(set, idx, &seg, ts, test.startPt); 324 SkOpAngle* angle = angles.append(); 325 angle->set(&seg, ts[0], ts[1]); 326 #if DEBUG_ANGLE 327 angle->setID(idx); 328 #endif 329 if (angle->unsortable()) { 330 #if DEBUG_ANGLE 331 SkDebugf("%s test[%s]: angle[%d] unsortable\n", __FUNCTION__, test.name, idx); 332 #endif 333 unsortable = true; 334 } 335 if (angle->unorderable()) { 336 #if DEBUG_ANGLE 337 SkDebugf("%s test[%s]: angle[%d] unorderable\n", __FUNCTION__, test.name, idx); 338 #endif 339 unorderable = true; 340 } 341 reporter->bumpTestCount(); 342 } 343 if (unsortable || unorderable) { 344 return; 345 } 346 #if DEBUG_ANGLE 347 SkDebugf("%s test[%s]\n", __FUNCTION__, test.name); 348 #endif 349 for (size_t idxL = 0; idxL < test.count; ++idxL) { 350 const SkOpAngle& first = angles[idxL]; 351 for (size_t idxG = 0; idxG < test.count; ++idxG) { 352 if (idxL == idxG) { 353 continue; 354 } 355 const SkOpAngle& second = angles[idxG]; 356 bool compare = first < second; 357 if (idxL < idxG) { 358 if (!compare) { 359 SkDebugf("%s test[%s]: first[%d] > second[%d]\n", __FUNCTION__, 360 test.name, idxL, idxG); 361 compare = first < second; 362 } 363 REPORTER_ASSERT(reporter, compare); 364 } else { 365 SkASSERT(idxL > idxG); 366 if (compare) { 367 SkDebugf("%s test[%s]: first[%d] < second[%d]\n", __FUNCTION__, 368 test.name, idxL, idxG); 369 compare = first < second; 370 } 371 REPORTER_ASSERT(reporter, !compare); 372 } 373 compare = second < first; 374 if (idxL < idxG) { 375 if (compare) { 376 SkDebugf("%s test[%s]: second[%d] < first[%d]\n", __FUNCTION__, 377 test.name, idxL, idxG); 378 compare = second < first; 379 } 380 REPORTER_ASSERT(reporter, !compare); 381 } else { 382 SkASSERT(idxL > idxG); 383 if (!compare) { 384 SkDebugf("%s test[%s]: second[%d] > first[%d]\n", __FUNCTION__, 385 test.name, idxL, idxG); 386 compare = second < first; 387 } 388 REPORTER_ASSERT(reporter, compare); 389 } 390 } 391 } 392 } 393 394 static void PathOpsAngleTest(skiatest::Reporter* reporter) { 395 for (size_t index = 0; index < SK_ARRAY_COUNT(tests); ++index) { 396 const SortSetTests& test = tests[index]; 397 testOne(reporter, test); 398 reporter->bumpTestCount(); 399 } 400 } 401 402 static void PathOpsAngleTestOne(skiatest::Reporter* reporter) { 403 size_t index = 0; 404 const SortSetTests& test = tests[index]; 405 testOne(reporter, test); 406 } 407 408 #if 0 409 static int find_slop(double x, double y, double rx, double ry) { 410 int slopBits = 0; 411 bool less1, less2; 412 double absX = fabs(x); 413 double absY = fabs(y); 414 double length = absX < absY ? absX / 2 + absY : absX + absY / 2; 415 int exponent; 416 (void) frexp(length, &exponent); 417 double epsilon = ldexp(FLT_EPSILON, exponent); 418 do { 419 // get the length as the larger plus half the smaller (both same signs) 420 // find the ulps of the length 421 // compute the offsets from there 422 double xSlop = epsilon * slopBits; 423 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ? 424 double x1 = x - xSlop; 425 double y1 = y + ySlop; 426 double x_ry1 = x1 * ry; 427 double rx_y1 = rx * y1; 428 less1 = x_ry1 < rx_y1; 429 double x2 = x + xSlop; 430 double y2 = y - ySlop; 431 double x_ry2 = x2 * ry; 432 double rx_y2 = rx * y2; 433 less2 = x_ry2 < rx_y2; 434 } while (less1 == less2 && ++slopBits); 435 return slopBits; 436 } 437 438 // from http://stackoverflow.com/questions/1427422/cheap-algorithm-to-find-measure-of-angle-between-vectors 439 static double diamond_angle(double y, double x) 440 { 441 if (y >= 0) 442 return (x >= 0 ? y/(x+y) : 1-x/(-x+y)); 443 else 444 return (x < 0 ? 2-y/(-x-y) : 3+x/(x-y)); 445 } 446 447 static const double slopTests[][4] = { 448 // x y rx ry 449 {-0.058554756452593892, -0.18804585843827226, -0.018568569646021160, -0.059615294434479438}, 450 {-0.0013717412948608398, 0.0041152238845825195, -0.00045837944195925573, 0.0013753175735478074}, 451 {-2.1033774145221198, -1.4046019261273715e-008, -0.70062688352066704, -1.2706324683777995e-008}, 452 }; 453 454 static void PathOpsAngleFindSlop(skiatest::Reporter* reporter) { 455 for (size_t index = 0; index < SK_ARRAY_COUNT(slopTests); ++index) { 456 const double* slopTest = slopTests[index]; 457 double x = slopTest[0]; 458 double y = slopTest[1]; 459 double rx = slopTest[2]; 460 double ry = slopTest[3]; 461 SkDebugf("%s xy %d=%d\n", __FUNCTION__, (int) index, find_slop(x, y, rx, ry)); 462 SkDebugf("%s rxy %d=%d\n", __FUNCTION__, (int) index, find_slop(rx, ry, x, y)); 463 double angle = diamond_angle(y, x); 464 double rAngle = diamond_angle(ry, rx); 465 double diff = fabs(angle - rAngle); 466 SkDebugf("%s diamond xy=%1.9g rxy=%1.9g diff=%1.9g factor=%d\n", __FUNCTION__, 467 angle, rAngle, diff, (int) (diff / FLT_EPSILON)); 468 469 } 470 } 471 #endif 472 473 #include "TestClassDef.h" 474 DEFINE_TESTCLASS_SHORT(PathOpsAngleTest) 475 476 DEFINE_TESTCLASS_SHORT(PathOpsAngleTestOne) 477 478 // DEFINE_TESTCLASS_SHORT(PathOpsAngleFindSlop) 479