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