static void standardTestCases(skiatest::Reporter* reporter) {
    size_t index;
    SkReduceOrder reducer;
    int order;
    enum {
        RunAll,
        RunQuadraticLines,
        RunQuadraticModLines,
        RunNone
    } run = RunAll;
    int firstTestIndex = 0;
#if 0
    run = RunQuadraticLines;
    firstTestIndex = 1;
#endif
    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex
            : SK_MaxS32;
    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex
            : SK_MaxS32;

    for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
        const SkDQuad& quad = quadraticLines[index];
        order = reducer.reduce(quad);
        if (order != 2) {
            SkDebugf("[%d] line quad order=%d\n", (int) index, order);
        }
    }
    for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
        const SkDQuad& quad = quadraticModEpsilonLines[index];
        order = reducer.reduce(quad);
        if (order != 2 && order != 3) {  // FIXME: data probably is not good
            SkDebugf("[%d] line mod quad order=%d\n", (int) index, order);
        }
    }
}
static void oneOffTest(skiatest::Reporter* reporter) {
    for (size_t index = 0; index < testSetCount; ++index) {
        const SkDQuad& quad = testSet[index];
        SkReduceOrder reducer;
        SkDEBUGCODE(int result = ) reducer.reduce(quad);
        SkASSERT(result == 3);
    }
}
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
    SkDCubic cubic;
    cubic.set(a);
    SkReduceOrder reducer;
    int order = reducer.reduce(cubic, kAllow_Quadratics);
    if (order == 2 || order == 3) {  // cubic became line or quad
        for (int index = 0; index < order; ++index) {
            *reducePts++ = reducer.fQuad[index].asSkPoint();
        }
    }
    return SkPathOpsPointsToVerb(order - 1);
}
SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
    SkDQuad quad;
    quad.set(a);
    SkReduceOrder reducer;
    int order = reducer.reduce(quad);
    if (order == 2) {  // quad became line
        for (int index = 0; index < order; ++index) {
            *reducePts++ = reducer.fLine[index].asSkPoint();
        }
    }
    return SkPathOpsPointsToVerb(order - 1);
}
DEF_TEST(PathOpsConicLineIntersection, reporter) {
    for (size_t index = 0; index < lineConicTests_count; ++index) {
        int iIndex = static_cast<int>(index);
        const SkDConic& conic = lineConicTests[index].conic;
        SkASSERT(ValidConic(conic));
        const SkDLine& line = lineConicTests[index].line;
        SkASSERT(ValidLine(line));
        SkReduceOrder reducer;
        SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(),
            conic.fPts.fPts[2].asSkPoint() };
        SkPoint reduced[3];
        SkPath::Verb order1 = SkReduceOrder::Conic(pts, conic.fWeight, reduced);
        if (order1 != SkPath::kConic_Verb) {
            SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1);
            REPORTER_ASSERT(reporter, 0);
        }
        int order2 = reducer.reduce(line);
        if (order2 < 2) {
            SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2);
            REPORTER_ASSERT(reporter, 0);
        }
        SkIntersections intersections;
        bool flipped = false;
        int result = doIntersect(intersections, conic, line, flipped);
        REPORTER_ASSERT(reporter, result == lineConicTests[index].result);
        if (intersections.used() <= 0) {
            continue;
        }
        for (int pt = 0; pt < result; ++pt) {
            double tt1 = intersections[0][pt];
            REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1);
            SkDPoint t1 = conic.ptAtT(tt1);
            double tt2 = intersections[1][pt];
            REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1);
            SkDPoint t2 = line.ptAtT(tt2);
            if (!t1.approximatelyEqual(t2)) {
                SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
                    __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY);
                REPORTER_ASSERT(reporter, 0);
            }
            if (!t1.approximatelyEqual(lineConicTests[index].expected[0])
                    && (lineConicTests[index].result == 1
                    || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) {
                SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY);
                REPORTER_ASSERT(reporter, 0);
            }
        }
    }
}
Exemple #6
0
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
    if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
            && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
        reducePts[0] = a[0];
        return SkPath::kMove_Verb;
    }
    SkDCubic cubic;
    cubic.set(a);
    SkReduceOrder reducer;
    int order = reducer.reduce(cubic, kAllow_Quadratics);
    if (order == 2 || order == 3) {  // cubic became line or quad
        for (int index = 0; index < order; ++index) {
            *reducePts++ = reducer.fQuad[index].asSkPoint();
        }
    }
    return SkPathOpsPointsToVerb(order - 1);
}
// find a point on a quad by choosing a t from 0 to 1
// create a vertical span above and below the point
// verify that intersecting the vertical span and the quad returns t
// verify that a vertical span starting at quad[0] intersects at t=0
// verify that a vertical span starting at quad[2] intersects at t=1
static void testQuadLineIntersectMain(PathOpsThreadState* data)
{
    PathOpsThreadState& state = *data;
    REPORTER_ASSERT(state.fReporter, data);
    int ax = state.fA & 0x03;
    int ay = state.fA >> 2;
    int bx = state.fB & 0x03;
    int by = state.fB >> 2;
    int cx = state.fC & 0x03;
    int cy = state.fC >> 2;
    SkDQuad quad = {{{(double) ax, (double) ay}, {(double) bx, (double) by},
            {(double) cx, (double) cy}
        }
    };
    SkReduceOrder reducer;
    int order = reducer.reduce(quad);
    if (order < 3) {
        return;
    }
    for (int tIndex = 0; tIndex <= 4; ++tIndex) {
        SkDPoint xy = quad.ptAtT(tIndex / 4.0);
        for (int h = -2; h <= 2; ++h) {
            for (int v = -2; v <= 2; ++v) {
                if (h == v && abs(h) != 1) {
                    continue;
                }
                double x = xy.fX;
                double y = xy.fY;
                SkDLine line = {{{x - h, y - v}, {x, y}}};
                testLineIntersect(state.fReporter, quad, line, x, y);
                state.fReporter->bumpTestCount();
                SkDLine line2 = {{{x, y}, {x + h, y + v}}};
                testLineIntersect(state.fReporter, quad, line2, x, y);
                state.fReporter->bumpTestCount();
                SkDLine line3 = {{{x - h, y - v}, {x + h, y + v}}};
                testLineIntersect(state.fReporter, quad, line3, x, y);
                state.fReporter->bumpTestCount();
            }
        }
    }
}
// determine that slop required after quad/quad finds a candidate intersection
// use the cross of the tangents plus the distance from 1 or 0 as knobs
DEF_TEST(PathOpsCubicQuadSlop, reporter) {
    // create a random non-selfintersecting cubic
    // break it into quadratics
    // offset the quadratic, measuring the slop required to find the intersection
    if (!gPathOpCubicQuadSlopVerbose) {  // takes a while to run -- so exclude it by default
        return;
    }
    int results[101];
    sk_bzero(results, sizeof(results));
    double minCross[101];
    sk_bzero(minCross, sizeof(minCross));
    double maxCross[101];
    sk_bzero(maxCross, sizeof(maxCross));
    double sumCross[101];
    sk_bzero(sumCross, sizeof(sumCross));
    int foundOne = 0;
    int slopCount = 1;
    SkRandom ran;
    for (int index = 0; index < 10000000; ++index) {
        if (index % 1000 == 999) SkDebugf(".");
        SkDCubic cubic = {{
                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
        }};
        SkIntersections i;
        if (i.intersect(cubic)) {
            continue;
        }
        SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
        cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
        double tStart = 0;
        int tsCount = ts.count();
        for (int i1 = 0; i1 <= tsCount; ++i1) {
            const double tEnd = i1 < tsCount ? ts[i1] : 1;
            SkDCubic part = cubic.subDivide(tStart, tEnd);
            SkDQuad quad = part.toQuad();
            SkReduceOrder reducer;
            int order = reducer.reduce(quad);
            if (order != 3) {
                continue;
            }
            for (int i2 = 0; i2 < 100; ++i2) {
                SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
                SkDQuad nearby = {{
                        {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
                        {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
                        {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
                }};
                order = reducer.reduce(nearby);
                if (order != 3) {
                    continue;
                }
                SkIntersections locals;
                locals.allowNear(false);
                locals.intersect(quad, nearby);
                if (locals.used() != 1) {
                    continue;
                }
                // brute force find actual intersection
                SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
                SkIntersections liner;
                int i3;
                int found = -1;
                int foundErr = true;
                for (i3 = 1; i3 <= 1000; ++i3) {
                    cubicLine[0] = cubicLine[1];
                    cubicLine[1] = cubic.ptAtT(i3 / 1000.);
                    liner.reset();
                    liner.allowNear(false);
                    liner.intersect(nearby, cubicLine);
                    if (liner.used() == 0) {
                        continue;
                    }
                    if (liner.used() > 1) {
                        foundErr = true;
                        break;
                    }
                    if (found > 0) {
                        foundErr = true;
                        break;
                    }
                    foundErr = false;
                    found = i3;
                }
                if (foundErr) {
                    continue;
                }
                SkDVector dist = liner.pt(0) - locals.pt(0);
                SkDVector qV = nearby.dxdyAtT(locals[0][0]);
                double cubicT = (found - 1 + liner[1][0]) / 1000.;
                SkDVector cV = cubic.dxdyAtT(cubicT);
                double qxc = qV.crossCheck(cV);
                double qvLen = qV.length();
                double cvLen = cV.length();
                double maxLen = SkTMax(qvLen, cvLen);
                qxc /= maxLen;
                double quadT = tStart + (tEnd - tStart) * locals[0][0];
                double diffT = fabs(cubicT - quadT);
                int diffIdx = (int) (diffT * 100);
                results[diffIdx]++;
                double absQxc = fabs(qxc);
                if (sumCross[diffIdx] == 0) {
                    minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
                } else {
                    minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
                    maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
                    sumCross[diffIdx] +=  absQxc;
                }
                if (diffIdx >= 20) {
#if 01
                    SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
                        " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
                        " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
                        " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
                        nearby[2].fX, nearby[2].fY,
                        liner.pt(0).fX, liner.pt(0).fY,
                        locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
#else
                    SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
                        quadT, cubicT, dist.length(), qxc);
                    SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
                    SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
                        nearby[2].fX, nearby[2].fY,
                        liner.pt(0).fX, liner.pt(0).fY,
                        locals.pt(0).fX, locals.pt(0).fY);
                    SkDebugf("</div>\n\n");
#endif
                }
                ++foundOne;
            }
            tStart = tEnd;
        }
        if (++foundOne >= 100000) {
            break;
        }
    }
#if 01
    SkDebugf("slopCount=%d\n", slopCount);
    int max = 100;
    while (results[max] == 0) {
        --max;
    }
    for (int i = 0; i <= max; ++i) {
        if (i > 0 && i % 10 == 0) {
            SkDebugf("\n");
        }
        SkDebugf("%d ", results[i]);
    }
    SkDebugf("min\n");
    for (int i = 0; i <= max; ++i) {
        if (i > 0 && i % 10 == 0) {
            SkDebugf("\n");
        }
        SkDebugf("%1.9g ", minCross[i]);
    }
    SkDebugf("max\n");
    for (int i = 0; i <= max; ++i) {
        if (i > 0 && i % 10 == 0) {
            SkDebugf("\n");
        }
        SkDebugf("%1.9g ", maxCross[i]);
    }
    SkDebugf("avg\n");
    for (int i = 0; i <= max; ++i) {
        if (i > 0 && i % 10 == 0) {
            SkDebugf("\n");
        }
        SkDebugf("%1.9g ", sumCross[i] / results[i]);
    }
#else
    for (int i = 1; i < slopCount; ++i) {
        SkDebugf("        slop%d,\n", i);
    }
#endif
    SkDebugf("\n");
}
Exemple #9
0
// flavor that returns T values only, deferring computing the quads until they are needed
// FIXME: when called from recursive intersect 2, this could take the original cubic
// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
// it would still take the prechopped cubic for reduce order and find cubic inflections
void SkDCubic::toQuadraticTs(double precision, SkTDArray<double>* ts) const {
    SkReduceOrder reducer;
    int order = reducer.reduce(*this, SkReduceOrder::kAllow_Quadratics, SkReduceOrder::kFill_Style);
    if (order < 3) {
        return;
    }
    double inflectT[5];
    int inflections = findInflections(inflectT);
    SkASSERT(inflections <= 2);
    if (!endsAreExtremaInXOrY()) {
        inflections += findMaxCurvature(&inflectT[inflections]);
        SkASSERT(inflections <= 5);
    }
    QSort<double>(inflectT, &inflectT[inflections - 1]);
    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
    // own subroutine?
    while (inflections && approximately_less_than_zero(inflectT[0])) {
        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
    }
    int start = 0;
    do {
        int next = start + 1;
        if (next >= inflections) {
            break;
        }
        if (!approximately_equal(inflectT[start], inflectT[next])) {
            ++start;
            continue;
        }
        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
    } while (true);
    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
        --inflections;
    }
    SkDCubicPair pair;
    if (inflections == 1) {
        pair = chopAt(inflectT[0]);
        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics,
                SkReduceOrder::kFill_Style);
        if (orderP1 < 2) {
            --inflections;
        } else {
            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics,
                    SkReduceOrder::kFill_Style);
            if (orderP2 < 2) {
                --inflections;
            }
        }
    }
    if (inflections == 0 && add_simple_ts(*this, precision, ts)) {
        return;
    }
    if (inflections == 1) {
        pair = chopAt(inflectT[0]);
        addTs(pair.first(), precision, 0, inflectT[0], ts);
        addTs(pair.second(), precision, inflectT[0], 1, ts);
        return;
    }
    if (inflections > 1) {
        SkDCubic part = subDivide(0, inflectT[0]);
        addTs(part, precision, 0, inflectT[0], ts);
        int last = inflections - 1;
        for (int idx = 0; idx < last; ++idx) {
            part = subDivide(inflectT[idx], inflectT[idx + 1]);
            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
        }
        part = subDivide(inflectT[last], 1);
        addTs(part, precision, inflectT[last], 1, ts);
        return;
    }
    addTs(*this, precision, 0, 1, ts);
}