1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 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 "src/pathops/SkIntersections.h" 8cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsRect.h" 9cb93a386Sopenharmony_ci#include "src/pathops/SkReduceOrder.h" 10cb93a386Sopenharmony_ci#include "tests/PathOpsCubicIntersectionTestData.h" 11cb93a386Sopenharmony_ci#include "tests/PathOpsQuadIntersectionTestData.h" 12cb93a386Sopenharmony_ci#include "tests/PathOpsTestCommon.h" 13cb93a386Sopenharmony_ci#include "tests/Test.h" 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciusing namespace PathOpsCubicIntersectionTestData; 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci#if 0 // disable test until stroke reduction is supported 18cb93a386Sopenharmony_cistatic bool controls_inside(const SkDCubic& cubic) { 19cb93a386Sopenharmony_ci return between(cubic[0].fX, cubic[1].fX, cubic[3].fX) 20cb93a386Sopenharmony_ci && between(cubic[0].fX, cubic[2].fX, cubic[3].fX) 21cb93a386Sopenharmony_ci && between(cubic[0].fY, cubic[1].fY, cubic[3].fY) 22cb93a386Sopenharmony_ci && between(cubic[0].fY, cubic[2].fY, cubic[3].fY); 23cb93a386Sopenharmony_ci} 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_cistatic bool tiny(const SkDCubic& cubic) { 26cb93a386Sopenharmony_ci int index, minX, maxX, minY, maxY; 27cb93a386Sopenharmony_ci minX = maxX = minY = maxY = 0; 28cb93a386Sopenharmony_ci for (index = 1; index < 4; ++index) { 29cb93a386Sopenharmony_ci if (cubic[minX].fX > cubic[index].fX) { 30cb93a386Sopenharmony_ci minX = index; 31cb93a386Sopenharmony_ci } 32cb93a386Sopenharmony_ci if (cubic[minY].fY > cubic[index].fY) { 33cb93a386Sopenharmony_ci minY = index; 34cb93a386Sopenharmony_ci } 35cb93a386Sopenharmony_ci if (cubic[maxX].fX < cubic[index].fX) { 36cb93a386Sopenharmony_ci maxX = index; 37cb93a386Sopenharmony_ci } 38cb93a386Sopenharmony_ci if (cubic[maxY].fY < cubic[index].fY) { 39cb93a386Sopenharmony_ci maxY = index; 40cb93a386Sopenharmony_ci } 41cb93a386Sopenharmony_ci } 42cb93a386Sopenharmony_ci return approximately_equal(cubic[maxX].fX, cubic[minX].fX) 43cb93a386Sopenharmony_ci && approximately_equal(cubic[maxY].fY, cubic[minY].fY); 44cb93a386Sopenharmony_ci} 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_cistatic void find_tight_bounds(const SkDCubic& cubic, SkDRect& bounds) { 47cb93a386Sopenharmony_ci SkDCubicPair cubicPair = cubic.chopAt(0.5); 48cb93a386Sopenharmony_ci if (!tiny(cubicPair.first()) && !controls_inside(cubicPair.first())) { 49cb93a386Sopenharmony_ci find_tight_bounds(cubicPair.first(), bounds); 50cb93a386Sopenharmony_ci } else { 51cb93a386Sopenharmony_ci bounds.add(cubicPair.first()[0]); 52cb93a386Sopenharmony_ci bounds.add(cubicPair.first()[3]); 53cb93a386Sopenharmony_ci } 54cb93a386Sopenharmony_ci if (!tiny(cubicPair.second()) && !controls_inside(cubicPair.second())) { 55cb93a386Sopenharmony_ci find_tight_bounds(cubicPair.second(), bounds); 56cb93a386Sopenharmony_ci } else { 57cb93a386Sopenharmony_ci bounds.add(cubicPair.second()[0]); 58cb93a386Sopenharmony_ci bounds.add(cubicPair.second()[3]); 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci#endif 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ciDEF_TEST(PathOpsReduceOrderCubic, reporter) { 64cb93a386Sopenharmony_ci size_t index; 65cb93a386Sopenharmony_ci SkReduceOrder reducer; 66cb93a386Sopenharmony_ci int order; 67cb93a386Sopenharmony_ci enum { 68cb93a386Sopenharmony_ci RunAll, 69cb93a386Sopenharmony_ci RunPointDegenerates, 70cb93a386Sopenharmony_ci RunNotPointDegenerates, 71cb93a386Sopenharmony_ci RunLines, 72cb93a386Sopenharmony_ci RunNotLines, 73cb93a386Sopenharmony_ci RunModEpsilonLines, 74cb93a386Sopenharmony_ci RunLessEpsilonLines, 75cb93a386Sopenharmony_ci RunNegEpsilonLines, 76cb93a386Sopenharmony_ci RunQuadraticLines, 77cb93a386Sopenharmony_ci RunQuadraticPoints, 78cb93a386Sopenharmony_ci RunQuadraticModLines, 79cb93a386Sopenharmony_ci RunComputedLines, 80cb93a386Sopenharmony_ci RunNone 81cb93a386Sopenharmony_ci } run = RunAll; 82cb93a386Sopenharmony_ci int firstTestIndex = 0; 83cb93a386Sopenharmony_ci#if 0 84cb93a386Sopenharmony_ci run = RunComputedLines; 85cb93a386Sopenharmony_ci firstTestIndex = 18; 86cb93a386Sopenharmony_ci#endif 87cb93a386Sopenharmony_ci int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates 88cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 89cb93a386Sopenharmony_ci int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates 90cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 91cb93a386Sopenharmony_ci int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32; 92cb93a386Sopenharmony_ci int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32; 93cb93a386Sopenharmony_ci int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines 94cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 95cb93a386Sopenharmony_ci int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines 96cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 97cb93a386Sopenharmony_ci int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines 98cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 99cb93a386Sopenharmony_ci int firstQuadraticPointTest = run == RunAll ? 0 : run == RunQuadraticPoints 100cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 101cb93a386Sopenharmony_ci int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines 102cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 103cb93a386Sopenharmony_ci int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines 104cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 105cb93a386Sopenharmony_ci#if 0 106cb93a386Sopenharmony_ci int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines 107cb93a386Sopenharmony_ci ? firstTestIndex : SK_MaxS32; 108cb93a386Sopenharmony_ci#endif 109cb93a386Sopenharmony_ci for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) { 110cb93a386Sopenharmony_ci const CubicPts& c = pointDegenerates[index]; 111cb93a386Sopenharmony_ci SkDCubic cubic; 112cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 113cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 114cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 115cb93a386Sopenharmony_ci if (order != 1) { 116cb93a386Sopenharmony_ci SkDebugf("[%d] pointDegenerates order=%d\n", static_cast<int>(index), order); 117cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) { 121cb93a386Sopenharmony_ci const CubicPts& c = notPointDegenerates[index]; 122cb93a386Sopenharmony_ci SkDCubic cubic; 123cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 124cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 125cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 126cb93a386Sopenharmony_ci if (order == 1) { 127cb93a386Sopenharmony_ci SkDebugf("[%d] notPointDegenerates order=%d\n", static_cast<int>(index), order); 128cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 129cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci for (index = firstLinesTest; index < lines_count; ++index) { 133cb93a386Sopenharmony_ci const CubicPts& c = lines[index]; 134cb93a386Sopenharmony_ci SkDCubic cubic; 135cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 136cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 137cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 138cb93a386Sopenharmony_ci if (order != 2) { 139cb93a386Sopenharmony_ci SkDebugf("[%d] lines order=%d\n", static_cast<int>(index), order); 140cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci for (index = firstNotLinesTest; index < notLines_count; ++index) { 144cb93a386Sopenharmony_ci const CubicPts& c = notLines[index]; 145cb93a386Sopenharmony_ci SkDCubic cubic; 146cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 147cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 148cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 149cb93a386Sopenharmony_ci if (order == 2) { 150cb93a386Sopenharmony_ci SkDebugf("[%d] notLines order=%d\n", static_cast<int>(index), order); 151cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 152cb93a386Sopenharmony_ci } 153cb93a386Sopenharmony_ci } 154cb93a386Sopenharmony_ci for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) { 155cb93a386Sopenharmony_ci const CubicPts& c = modEpsilonLines[index]; 156cb93a386Sopenharmony_ci SkDCubic cubic; 157cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 158cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 159cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 160cb93a386Sopenharmony_ci if (order == 2) { 161cb93a386Sopenharmony_ci SkDebugf("[%d] line mod by epsilon order=%d\n", static_cast<int>(index), order); 162cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) { 166cb93a386Sopenharmony_ci const CubicPts& c = lessEpsilonLines[index]; 167cb93a386Sopenharmony_ci SkDCubic cubic; 168cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 169cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 170cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 171cb93a386Sopenharmony_ci if (order != 2) { 172cb93a386Sopenharmony_ci SkDebugf("[%d] line less by epsilon/2 order=%d\n", static_cast<int>(index), order); 173cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 174cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) { 178cb93a386Sopenharmony_ci const CubicPts& c = negEpsilonLines[index]; 179cb93a386Sopenharmony_ci SkDCubic cubic; 180cb93a386Sopenharmony_ci cubic.debugSet(c.fPts); 181cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 182cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 183cb93a386Sopenharmony_ci if (order != 2) { 184cb93a386Sopenharmony_ci SkDebugf("[%d] line neg by epsilon/2 order=%d\n", static_cast<int>(index), order); 185cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci } 188cb93a386Sopenharmony_ci for (index = firstQuadraticPointTest; index < quadraticPoints_count; ++index) { 189cb93a386Sopenharmony_ci const QuadPts& q = quadraticPoints[index]; 190cb93a386Sopenharmony_ci SkDQuad quad; 191cb93a386Sopenharmony_ci quad.debugSet(q.fPts); 192cb93a386Sopenharmony_ci SkASSERT(ValidQuad(quad)); 193cb93a386Sopenharmony_ci SkDCubic cubic = quad.debugToCubic(); 194cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 195cb93a386Sopenharmony_ci if (order != 1) { 196cb93a386Sopenharmony_ci SkDebugf("[%d] point quad order=%d\n", static_cast<int>(index), order); 197cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) { 201cb93a386Sopenharmony_ci const QuadPts& q = quadraticLines[index]; 202cb93a386Sopenharmony_ci SkDQuad quad; 203cb93a386Sopenharmony_ci quad.debugSet(q.fPts); 204cb93a386Sopenharmony_ci SkASSERT(ValidQuad(quad)); 205cb93a386Sopenharmony_ci SkDCubic cubic = quad.debugToCubic(); 206cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 207cb93a386Sopenharmony_ci if (order != 2) { 208cb93a386Sopenharmony_ci SkDebugf("[%d] line quad order=%d\n", static_cast<int>(index), order); 209cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 210cb93a386Sopenharmony_ci } 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) { 213cb93a386Sopenharmony_ci const QuadPts& q = quadraticModEpsilonLines[index]; 214cb93a386Sopenharmony_ci SkDQuad quad; 215cb93a386Sopenharmony_ci quad.debugSet(q.fPts); 216cb93a386Sopenharmony_ci SkASSERT(ValidQuad(quad)); 217cb93a386Sopenharmony_ci SkDCubic cubic = quad.debugToCubic(); 218cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); 219cb93a386Sopenharmony_ci if (order != 3) { 220cb93a386Sopenharmony_ci SkDebugf("[%d] line mod quad order=%d\n", static_cast<int>(index), order); 221cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 222cb93a386Sopenharmony_ci } 223cb93a386Sopenharmony_ci } 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci#if 0 // disable test until stroke reduction is supported 226cb93a386Sopenharmony_ci// test if computed line end points are valid 227cb93a386Sopenharmony_ci for (index = firstComputedLinesTest; index < lines_count; ++index) { 228cb93a386Sopenharmony_ci const SkDCubic& cubic = lines[index]; 229cb93a386Sopenharmony_ci SkASSERT(ValidCubic(cubic)); 230cb93a386Sopenharmony_ci bool controlsInside = controls_inside(cubic); 231cb93a386Sopenharmony_ci order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics, 232cb93a386Sopenharmony_ci SkReduceOrder::kStroke_Style); 233cb93a386Sopenharmony_ci if (order == 2 && reducer.fLine[0] == reducer.fLine[1]) { 234cb93a386Sopenharmony_ci SkDebugf("[%d] line computed ends match order=%d\n", static_cast<int>(index), order); 235cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 236cb93a386Sopenharmony_ci } 237cb93a386Sopenharmony_ci if (controlsInside) { 238cb93a386Sopenharmony_ci if ( (reducer.fLine[0].fX != cubic[0].fX && reducer.fLine[0].fX != cubic[3].fX) 239cb93a386Sopenharmony_ci || (reducer.fLine[0].fY != cubic[0].fY && reducer.fLine[0].fY != cubic[3].fY) 240cb93a386Sopenharmony_ci || (reducer.fLine[1].fX != cubic[0].fX && reducer.fLine[1].fX != cubic[3].fX) 241cb93a386Sopenharmony_ci || (reducer.fLine[1].fY != cubic[0].fY && reducer.fLine[1].fY != cubic[3].fY)) { 242cb93a386Sopenharmony_ci SkDebugf("[%d] line computed ends order=%d\n", static_cast<int>(index), order); 243cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 244cb93a386Sopenharmony_ci } 245cb93a386Sopenharmony_ci } else { 246cb93a386Sopenharmony_ci // binary search for extrema, compare against actual results 247cb93a386Sopenharmony_ci // while a control point is outside of bounding box formed by end points, split 248cb93a386Sopenharmony_ci SkDRect bounds = {DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX}; 249cb93a386Sopenharmony_ci find_tight_bounds(cubic, bounds); 250cb93a386Sopenharmony_ci if ( (!AlmostEqualUlps(reducer.fLine[0].fX, bounds.fLeft) 251cb93a386Sopenharmony_ci && !AlmostEqualUlps(reducer.fLine[0].fX, bounds.fRight)) 252cb93a386Sopenharmony_ci || (!AlmostEqualUlps(reducer.fLine[0].fY, bounds.fTop) 253cb93a386Sopenharmony_ci && !AlmostEqualUlps(reducer.fLine[0].fY, bounds.fBottom)) 254cb93a386Sopenharmony_ci || (!AlmostEqualUlps(reducer.fLine[1].fX, bounds.fLeft) 255cb93a386Sopenharmony_ci && !AlmostEqualUlps(reducer.fLine[1].fX, bounds.fRight)) 256cb93a386Sopenharmony_ci || (!AlmostEqualUlps(reducer.fLine[1].fY, bounds.fTop) 257cb93a386Sopenharmony_ci && !AlmostEqualUlps(reducer.fLine[1].fY, bounds.fBottom))) { 258cb93a386Sopenharmony_ci SkDebugf("[%d] line computed tight bounds order=%d\n", static_cast<int>(index), order); 259cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, 0); 260cb93a386Sopenharmony_ci } 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci } 263cb93a386Sopenharmony_ci#endif 264cb93a386Sopenharmony_ci} 265