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