Exemplo n.º 1
0
static double testArc(skiatest::Reporter* reporter, const SkDQuad& quad, const SkDQuad& arcRef,
        int octant) {
    SkDQuad arc = arcRef;
    SkDVector offset = {quad[0].fX, quad[0].fY};
    arc[0] += offset;
    arc[1] += offset;
    arc[2] += offset;
    SkIntersections i;
    i.intersect(arc, quad);
    if (i.used() == 0) {
        return -1;
    }
    int smallest = -1;
    double t = 2;
    for (int idx = 0; idx < i.used(); ++idx) {
        if (i[0][idx] > 1 || i[0][idx] < 0) {
            i.reset();
            i.intersect(arc, quad);
        }
        if (i[1][idx] > 1 || i[1][idx] < 0) {
            i.reset();
            i.intersect(arc, quad);
        }
        if (t > i[1][idx]) {
            smallest = idx;
            t = i[1][idx];
        }
    }
    REPORTER_ASSERT(reporter, smallest >= 0);
    REPORTER_ASSERT(reporter, t >= 0 && t <= 1);
    return i[1][smallest];
}
Exemplo n.º 2
0
static void TestLineIntersection(skiatest::Reporter* reporter) {
    size_t index;
    for (index = 0; index < tests_count; ++index) {
        const SkDLine& line1 = tests[index][0];
        const SkDLine& line2 = tests[index][1];
        SkIntersections ts;
        int pts = ts.intersect(line1, line2);
        REPORTER_ASSERT(reporter, pts);
        for (int i = 0; i < pts; ++i) {
            SkDPoint result1 = line1.xyAtT(ts[0][i]);
            SkDPoint result2 = line2.xyAtT(ts[1][i]);
            if (!result1.approximatelyEqual(result2)) {
                REPORTER_ASSERT(reporter, pts != 1);
                result2 = line2.xyAtT(ts[1][i ^ 1]);
                REPORTER_ASSERT(reporter, result1.approximatelyEqual(result2));
            }
        }
    }
    for (index = 0; index < noIntersect_count; ++index) {
        const SkDLine& line1 = noIntersect[index][0];
        const SkDLine& line2 = noIntersect[index][1];
        SkIntersections ts;
        int pts = ts.intersect(line1, line2);
        REPORTER_ASSERT(reporter, !pts);
    }
}
Exemplo n.º 3
0
static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
        int order2, SkIntersections& i) {
    if (order1 == 3 && order2 == 3) {
        i.intersect(simple1, simple2);
    } else if (order1 <= 2 && order2 <= 2) {
        i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
    } else if (order1 == 3 && order2 <= 2) {
        i.intersect(simple1, (const SkDLine&) simple2);
    } else {
        SkASSERT(order1 <= 2 && order2 == 3);
        i.intersect(simple2, (const SkDLine&) simple1);
        i.swapPts();
    }
}
static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
    int iIndex = static_cast<int>(index);
    const SkDCubic& cubic = quadCubicTests[index].cubic;
    SkASSERT(ValidCubic(cubic));
    const SkDQuad& quad = quadCubicTests[index].quad;
    SkASSERT(ValidQuad(quad));
    SkReduceOrder reduce1;
    SkReduceOrder reduce2;
    int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics);
    int order2 = reduce2.reduce(quad);
    if (order1 != 4) {
        SkDebugf("[%d] cubic order=%d\n", iIndex, order1);
        REPORTER_ASSERT(reporter, 0);
    }
    if (order2 != 3) {
        SkDebugf("[%d] quad order=%d\n", iIndex, order2);
        REPORTER_ASSERT(reporter, 0);
    }
    SkIntersections i;
    int roots = i.intersect(cubic, quad);
    for (int pt = 0; pt < roots; ++pt) {
        double tt1 = i[0][pt];
        SkDPoint xy1 = cubic.ptAtT(tt1);
        double tt2 = i[1][pt];
        SkDPoint xy2 = quad.ptAtT(tt2);
        if (!xy1.approximatelyEqual(xy2)) {
            SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
        }
        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
    }
    reporter->bumpTestCount();
}
static void oneOff(skiatest::Reporter* reporter, const SkDConic& c1, const SkDConic& c2,
        bool coin) {
#if DEBUG_VISUALIZE_CONICS
    writeFrames();
#endif
    chopBothWays(c1, 0.5, "c1");
    chopBothWays(c2, 0.5, "c2");
#if DEBUG_VISUALIZE_CONICS
    writeDPng(c1, "d1");
    writeDPng(c2, "d2");
#endif
    SkASSERT(ValidConic(c1));
    SkASSERT(ValidConic(c2));
    SkIntersections intersections;
    intersections.intersect(c1, c2);
    if (coin && intersections.used() != 2) {
        SkDebugf("");
    }
    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
    double tt1, tt2;
    SkDPoint xy1, xy2;
    for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
        tt1 = intersections[0][pt3];
        xy1 = c1.ptAtT(tt1);
        tt2 = intersections[1][pt3];
        xy2 = c2.ptAtT(tt2);
        const SkDPoint& iPt = intersections.pt(pt3);
        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
        REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
    }
    reporter->bumpTestCount();
}
static int doIntersect(SkIntersections& intersections, const SkDConic& conic, const SkDLine& line,
                       bool& flipped) {
    int result;
    flipped = false;
    if (line[0].fX == line[1].fX) {
        double top = line[0].fY;
        double bottom = line[1].fY;
        flipped = top > bottom;
        if (flipped) {
            SkTSwap<double>(top, bottom);
        }
        result = intersections.vertical(conic, top, bottom, line[0].fX, flipped);
    } else if (line[0].fY == line[1].fY) {
        double left = line[0].fX;
        double right = line[1].fX;
        flipped = left > right;
        if (flipped) {
            SkTSwap<double>(left, right);
        }
        result = intersections.horizontal(conic, left, right, line[0].fY, flipped);
    } else {
        intersections.intersect(conic, line);
        result = intersections.used();
    }
    return result;
}
Exemplo n.º 7
0
DEF_TEST(PathOpsAngleFindQuadEpsilon, reporter) {
    if (gDisableAngleTests) {
        return;
    }
    SkRandom ran;
    int maxEpsilon = 0;
    double maxAngle = 0;
    for (int index = 0; index < 100000; ++index) {
        SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
        float t = ran.nextRangeF(0.0001f, 1);
        SkDPoint dPt = line.ptAtT(t);
        float t2 = ran.nextRangeF(0.0001f, 1);
        SkDPoint qPt = line.ptAtT(t2);
        float t3 = ran.nextRangeF(0.0001f, 1);
        SkDPoint qPt2 = line.ptAtT(t3);
        qPt.fX += qPt2.fY;
        qPt.fY -= qPt2.fX;
        QuadPts q = {{line[0], dPt, qPt}};
        SkDQuad quad;
        quad.debugSet(q.fPts);
        // binary search for maximum movement of quad[1] towards test that still has 1 intersection
        double moveT = 0.5f;
        double deltaT = moveT / 2;
        SkDPoint last;
        do {
            last = quad[1];
            quad[1].fX = dPt.fX - line[1].fY * moveT;
            quad[1].fY = dPt.fY + line[1].fX * moveT;
            SkIntersections i;
            i.intersect(quad, line);
            REPORTER_ASSERT(reporter, i.used() > 0);
            if (i.used() == 1) {
                moveT += deltaT;
            } else {
                moveT -= deltaT;
            }
            deltaT /= 2;
        } while (last.asSkPoint() != quad[1].asSkPoint());
        float p1 = SkDoubleToScalar(line[1].fX * last.fY);
        float p2 = SkDoubleToScalar(line[1].fY * last.fX);
        int p1Bits = SkFloatAs2sCompliment(p1);
        int p2Bits = SkFloatAs2sCompliment(p2);
        int epsilon = SkTAbs(p1Bits - p2Bits);
        if (maxEpsilon < epsilon) {
            SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
                    " pt={%1.7g, %1.7g} epsilon=%d\n",
                    line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, epsilon);
            maxEpsilon = epsilon;
        }
        double a1 = atan2(line[1].fY, line[1].fX);
        double a2 = atan2(last.fY, last.fX);
        double angle = fabs(a1 - a2);
        if (maxAngle < angle) {
            SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
                    " pt={%1.7g, %1.7g} angle=%1.7g\n",
                    line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, angle);
            maxAngle = angle;
        }
    }
}
// returns false if there's more than one intercept or the intercept doesn't match the point
// returns true if the intercept was successfully added or if the
// original quads need to be subdivided
static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
                          SkIntersections* i, bool* subDivide) {
    double tMid = (tMin + tMax) / 2;
    SkDPoint mid = q2.ptAtT(tMid);
    SkDLine line;
    line[0] = line[1] = mid;
    SkDVector dxdy = q2.dxdyAtT(tMid);
    line[0] -= dxdy;
    line[1] += dxdy;
    SkIntersections rootTs;
    rootTs.allowNear(false);
    int roots = rootTs.intersect(q1, line);
    if (roots == 0) {
        if (subDivide) {
            *subDivide = true;
        }
        return true;
    }
    if (roots == 2) {
        return false;
    }
    SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
    if (!pt2.approximatelyEqualHalf(mid)) {
        return false;
    }
    i->insertSwap(rootTs[0][0], tMid, pt2);
    return true;
}
Exemplo n.º 9
0
static void selfOneOff(skiatest::Reporter* reporter, int index) {
    const SkDCubic& cubic = selfSet[index];
#if ONE_OFF_DEBUG
    int idx2;
    double max[3];
    int ts = cubic.findMaxCurvature(max);
    for (idx2 = 0; idx2 < ts; ++idx2) {
        SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
                max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY);
    }
    SkTArray<double, true> ts1;
    SkTArray<SkDQuad, true> quads1;
    cubic.toQuadraticTs(cubic.calcPrecision(), &ts1);
    for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
        SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
    }
    CubicToQuads(cubic, cubic.calcPrecision(), quads1);
    for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
        const SkDQuad& q = quads1[idx2];
        SkDebugf("  {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n",
                q[0].fX, q[0].fY,  q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
    }
    SkDebugf("\n");
#endif
    SkIntersections i;
    int result = i.intersect(cubic);
    REPORTER_ASSERT(reporter, result == 1);
    REPORTER_ASSERT(reporter, i.used() == 1);
    REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0]));
    SkDPoint pt1 = cubic.ptAtT(i[0][0]);
    SkDPoint pt2 = cubic.ptAtT(i[1][0]);
    REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
    reporter->bumpTestCount();
}
Exemplo n.º 10
0
static int doIntersect(SkIntersections& intersections, const SkDQuad& quad, const SkDLine& line,
                       bool& flipped) {
    int result;
    flipped = false;
    if (line[0].fX == line[1].fX) {
        double top = line[0].fY;
        double bottom = line[1].fY;
        flipped = top > bottom;
        if (flipped) {
            using std::swap;
            swap(top, bottom);
        }
        result = intersections.vertical(quad, top, bottom, line[0].fX, flipped);
    } else if (line[0].fY == line[1].fY) {
        double left = line[0].fX;
        double right = line[1].fX;
        flipped = left > right;
        if (flipped) {
            using std::swap;
            swap(left, right);
        }
        result = intersections.horizontal(quad, left, right, line[0].fY, flipped);
    } else {
        intersections.intersect(quad, line);
        result = intersections.used();
    }
    return result;
}
static void PathOpsCubicLineIntersectionTest(skiatest::Reporter* reporter) {
    for (size_t index = 0; index < lineCubicTests_count; ++index) {
        int iIndex = static_cast<int>(index);
        const SkDCubic& cubic = lineCubicTests[index].cubic;
        const SkDLine& line = lineCubicTests[index].line;
        SkReduceOrder reduce1;
        SkReduceOrder reduce2;
        int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics,
                SkReduceOrder::kFill_Style);
        int order2 = reduce2.reduce(line);
        if (order1 < 4) {
            SkDebugf("[%d] cubic order=%d\n", iIndex, order1);
            REPORTER_ASSERT(reporter, 0);
        }
        if (order2 < 2) {
            SkDebugf("[%d] line order=%d\n", iIndex, order2);
            REPORTER_ASSERT(reporter, 0);
        }
        if (order1 == 4 && order2 == 2) {
            SkIntersections i;
            int roots = i.intersect(cubic, line);
            for (int pt = 0; pt < roots; ++pt) {
                double tt1 = i[0][pt];
                SkDPoint xy1 = cubic.xyAtT(tt1);
                double tt2 = i[1][pt];
                SkDPoint xy2 = line.xyAtT(tt2);
                if (!xy1.approximatelyEqual(xy2)) {
                    SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                        __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
                }
                REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
            }
        }
    }
}
Exemplo n.º 12
0
static void selfOneOff(skiatest::Reporter* reporter, int index) {
    const CubicPts& cubic = selfSet[index];
    SkPoint c[4];
    for (int i = 0; i < 4; ++i) {
        c[i] = cubic.fPts[i].asSkPoint();
    }
    SkScalar loopT;
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(c, d);
    if (SkDCubic::ComplexBreak(c, &loopT) && cubicType == SkCubicType::kLoop_SkCubicType) {
        SkIntersections i;
        SkPoint twoCubics[7];
        SkChopCubicAt(c, twoCubics, loopT);
        SkDCubic chopped[2];
        chopped[0].set(&twoCubics[0]);
        chopped[1].set(&twoCubics[3]);
        int result = i.intersect(chopped[0], chopped[1]);
        REPORTER_ASSERT(reporter, result == 2);
        REPORTER_ASSERT(reporter, i.used() == 2);
        for (int index = 0; index < result; ++index) {
            SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
            SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
            REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
            reporter->bumpTestCount();
        }
    }
}
Exemplo n.º 13
0
static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
        bool coin) {
    SkASSERT(ValidCubic(cubic1));
    SkASSERT(ValidCubic(cubic2));
#if ONE_OFF_DEBUG
    SkDebugf("computed quadratics given\n");
    SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
        cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY,
        cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY);
    SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
        cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
        cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
#endif
    SkTArray<SkDQuad, true> quads1;
    CubicToQuads(cubic1, cubic1.calcPrecision(), quads1);
#if ONE_OFF_DEBUG
    SkDebugf("computed quadratics set 1\n");
    for (int index = 0; index < quads1.count(); ++index) {
        const SkDQuad& q = quads1[index];
        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
    }
#endif
    SkTArray<SkDQuad, true> quads2;
    CubicToQuads(cubic2, cubic2.calcPrecision(), quads2);
#if ONE_OFF_DEBUG
    SkDebugf("computed quadratics set 2\n");
    for (int index = 0; index < quads2.count(); ++index) {
        const SkDQuad& q = quads2[index];
        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
    }
#endif
    SkIntersections intersections;
    intersections.intersect(cubic1, cubic2);
    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
    double tt1, tt2;
    SkDPoint xy1, xy2;
    for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
        tt1 = intersections[0][pt3];
        xy1 = cubic1.ptAtT(tt1);
        tt2 = intersections[1][pt3];
        xy2 = cubic2.ptAtT(tt2);
        const SkDPoint& iPt = intersections.pt(pt3);
#if ONE_OFF_DEBUG
        SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
                __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX,
                iPt.fY, xy2.fX, xy2.fY, tt2);
#endif
       REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
       REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
       REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
    }
    reporter->bumpTestCount();
}
Exemplo n.º 14
0
static void standardTestCases(skiatest::Reporter* reporter) {
    for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) {
        int iIndex = static_cast<int>(index);
        const CubicPts& cubic1 = tests[index][0];
        const CubicPts& cubic2 = tests[index][1];
        SkDCubic c1, c2;
        c1.debugSet(cubic1.fPts);
        c2.debugSet(cubic2.fPts);
        SkReduceOrder reduce1, reduce2;
        int order1 = reduce1.reduce(c1, SkReduceOrder::kNo_Quadratics);
        int order2 = reduce2.reduce(c2, SkReduceOrder::kNo_Quadratics);
        const bool showSkipped = false;
        if (order1 < 4) {
            if (showSkipped) {
                SkDebugf("%s [%d] cubic1 order=%d\n", __FUNCTION__, iIndex, order1);
            }
            continue;
        }
        if (order2 < 4) {
            if (showSkipped) {
                SkDebugf("%s [%d] cubic2 order=%d\n", __FUNCTION__, iIndex, order2);
            }
            continue;
        }
        SkIntersections tIntersections;
        tIntersections.intersect(c1, c2);
        if (!tIntersections.used()) {
            if (showSkipped) {
                SkDebugf("%s [%d] no intersection\n", __FUNCTION__, iIndex);
            }
            continue;
        }
        if (tIntersections.isCoincident(0)) {
            if (showSkipped) {
                SkDebugf("%s [%d] coincident\n", __FUNCTION__, iIndex);
            }
            continue;
        }
        for (int pt = 0; pt < tIntersections.used(); ++pt) {
            double tt1 = tIntersections[0][pt];
            SkDPoint xy1 = c1.ptAtT(tt1);
            double tt2 = tIntersections[1][pt];
            SkDPoint xy2 = c2.ptAtT(tt2);
            if (!xy1.approximatelyEqual(xy2)) {
                SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                    __FUNCTION__, (int)index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
            }
            REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
        }
        reporter->bumpTestCount();
    }
}
static void hullIntersect(const SkDQuad& q1, const SkDQuad& q2) {
    SkDebugf("%s", __FUNCTION__);
    SkIntersections ts;
    for (int i1 = 0; i1 < 3; ++i1) {
        SkDLine l1 = {{q1[i1], q1[(i1 + 1) % 3]}};
        for (int i2 = 0; i2 < 3; ++i2) {
            SkDLine l2 = {{q2[i2], q2[(i2 + 1) % 3]}};
            if (ts.intersect(l1, l2)) {
                SkDebugf(" [%d,%d]", i1, i2);
            }
        }
    }
    SkDebugf("\n");
}
Exemplo n.º 16
0
static void oneOff(skiatest::Reporter* reporter, const CubicPts& cubic1, const CubicPts& cubic2,
        bool coin) {
    SkDCubic c1, c2;
    c1.debugSet(cubic1.fPts);
    c2.debugSet(cubic2.fPts);
    SkASSERT(ValidCubic(c1));
    SkASSERT(ValidCubic(c2));
#if ONE_OFF_DEBUG
    SkDebugf("computed quadratics given\n");
    SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
        cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY,
        cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY);
    SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
        cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
        cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
#endif
    SkIntersections intersections;
    intersections.intersect(c1, c2);
#if DEBUG_T_SECT_DUMP == 3
    SkDebugf("</div>\n\n");
    SkDebugf("<script type=\"text/javascript\">\n\n");
    SkDebugf("var testDivs = [\n");
    for (int index = 1; index <= gDumpTSectNum; ++index) {
        SkDebugf("sect%d,\n", index);
    }
#endif
    if (coin && intersections.used() < 2) {
        SkDebugf("");
    }
    REPORTER_ASSERT(reporter, !coin || intersections.used() >= 2);
    double tt1, tt2;
    SkDPoint xy1, xy2;
    for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
        tt1 = intersections[0][pt3];
        xy1 = c1.ptAtT(tt1);
        tt2 = intersections[1][pt3];
        xy2 = c2.ptAtT(tt2);
        const SkDPoint& iPt = intersections.pt(pt3);
#if ONE_OFF_DEBUG
        SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
                __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX,
                iPt.fY, xy2.fX, xy2.fY, tt2);
#endif
       REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
       REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
       REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
    }
    reporter->bumpTestCount();
}
static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1,
                              const SkDLine& line2) {
    SkASSERT(ValidLine(line1));
    SkASSERT(ValidLine(line2));
    SkIntersections ts2;
    int pts2 = ts2.intersect(line1, line2);
    REPORTER_ASSERT(reporter, pts2 == 2);
    REPORTER_ASSERT(reporter, pts2 == ts2.used());
    check_results(reporter, line1, line2, ts2);
#if 0
    SkIntersections ts;
    int pts = ts.intersect(line1, line2);
    REPORTER_ASSERT(reporter, pts == pts2);
    REPORTER_ASSERT(reporter, pts == 2);
    REPORTER_ASSERT(reporter, pts == ts.used());
    check_results(reporter, line1, line2, ts);
#endif
}
static void PathOpsCubicQuadIntersectionTest(skiatest::Reporter* reporter) {
    for (size_t index = 0; index < quadCubicTests_count; ++index) {
        int iIndex = static_cast<int>(index);
        const SkDCubic& cubic = quadCubicTests[index].cubic;
        const SkDQuad& quad = quadCubicTests[index].quad;
        SkReduceOrder reduce1;
        SkReduceOrder reduce2;
        int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics,
                SkReduceOrder::kFill_Style);
        int order2 = reduce2.reduce(quad, SkReduceOrder::kFill_Style);
        if (order1 != 4) {
            SkDebugf("[%d] cubic order=%d\n", iIndex, order1);
            REPORTER_ASSERT(reporter, 0);
        }
        if (order2 != 3) {
            SkDebugf("[%d] quad order=%d\n", iIndex, order2);
            REPORTER_ASSERT(reporter, 0);
        }
        SkIntersections i;
        int roots = i.intersect(cubic, quad);
        SkASSERT(roots == quadCubicTests[index].answerCount);
        for (int pt = 0; pt < roots; ++pt) {
            double tt1 = i[0][pt];
            SkDPoint xy1 = cubic.xyAtT(tt1);
            double tt2 = i[1][pt];
            SkDPoint xy2 = quad.xyAtT(tt2);
            if (!xy1.approximatelyEqual(xy2)) {
                SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                    __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
            }
            REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
            bool found = false;
            for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
                found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
            }
            REPORTER_ASSERT(reporter, found);
        }
        reporter->bumpTestCount();
    }
}
static void standardTestCases(skiatest::Reporter* reporter) {
    bool showSkipped = false;
    for (size_t index = 0; index < quadraticTests_count; ++index) {
        const SkDQuad& quad1 = quadraticTests[index][0];
        SkASSERT(ValidQuad(quad1));
        const SkDQuad& quad2 = quadraticTests[index][1];
        SkASSERT(ValidQuad(quad2));
        SkReduceOrder reduce1, reduce2;
        int order1 = reduce1.reduce(quad1);
        int order2 = reduce2.reduce(quad2);
        if (order1 < 3) {
            if (showSkipped) {
                SkDebugf("[%d] quad1 order=%d\n", static_cast<int>(index), order1);
            }
        }
        if (order2 < 3) {
            if (showSkipped) {
                SkDebugf("[%d] quad2 order=%d\n", static_cast<int>(index), order2);
            }
        }
        if (order1 == 3 && order2 == 3) {
            SkIntersections intersections;
            intersections.intersect(quad1, quad2);
            if (intersections.used() > 0) {
                for (int pt = 0; pt < intersections.used(); ++pt) {
                    double tt1 = intersections[0][pt];
                    SkDPoint xy1 = quad1.ptAtT(tt1);
                    double tt2 = intersections[1][pt];
                    SkDPoint xy2 = quad2.ptAtT(tt2);
                    if (!xy1.approximatelyEqual(xy2)) {
                        SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                                __FUNCTION__, static_cast<int>(index), pt, tt1, xy1.fX, xy1.fY,
                                tt2, xy2.fX, xy2.fY);
                        REPORTER_ASSERT(reporter, 0);
                    }
                }
            }
        }
    }
}
static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) {
    SkASSERT(ValidLine(line1));
    SkASSERT(ValidLine(line2));
    SkIntersections i;
    int pts = i.intersect(line1, line2);
    REPORTER_ASSERT(reporter, pts);
    REPORTER_ASSERT(reporter, pts == i.used());
    check_results(reporter, line1, line2, i);
    if (line1[0] == line1[1] || line2[0] == line2[1]) {
        return;
    }
    if (line1[0].fY == line1[1].fY) {
        double left = SkTMin(line1[0].fX, line1[1].fX);
        double right = SkTMax(line1[0].fX, line1[1].fX);
        SkIntersections ts;
        ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
        check_results(reporter, line2, line1, ts);
    }
    if (line2[0].fY == line2[1].fY) {
        double left = SkTMin(line2[0].fX, line2[1].fX);
        double right = SkTMax(line2[0].fX, line2[1].fX);
        SkIntersections ts;
        ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
        check_results(reporter, line1, line2, ts);
    }
    if (line1[0].fX == line1[1].fX) {
        double top = SkTMin(line1[0].fY, line1[1].fY);
        double bottom = SkTMax(line1[0].fY, line1[1].fY);
        SkIntersections ts;
        ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
        check_results(reporter, line2, line1, ts);
    }
    if (line2[0].fX == line2[1].fX) {
        double top = SkTMin(line2[0].fY, line2[1].fY);
        double bottom = SkTMax(line2[0].fY, line2[1].fY);
        SkIntersections ts;
        ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
        check_results(reporter, line1, line2, ts);
    }
}
static void PathOpsLineIntersectionTest(skiatest::Reporter* reporter) {
    size_t index;
    for (index = 0; index < coincidentTests_count; ++index) {
        const SkDLine& line1 = coincidentTests[index][0];
        const SkDLine& line2 = coincidentTests[index][1];
        testOneCoincident(reporter, line1, line2);
        reporter->bumpTestCount();
    }
    for (index = 0; index < tests_count; ++index) {
        const SkDLine& line1 = tests[index][0];
        const SkDLine& line2 = tests[index][1];
        testOne(reporter, line1, line2);
        reporter->bumpTestCount();
    }
    for (index = 0; index < noIntersect_count; ++index) {
        const SkDLine& line1 = noIntersect[index][0];
        const SkDLine& line2 = noIntersect[index][1];
        SkIntersections ts;
        int pts = ts.intersect(line1, line2);
        REPORTER_ASSERT(reporter, !pts);
        REPORTER_ASSERT(reporter, pts == ts.used());
        reporter->bumpTestCount();
    }
}
Exemplo n.º 22
0
// intersect the end of the cubic with the other. Try lines from the end to control and opposite
// end to determine range of t on opposite cubic.
static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
                         const SkDRect& bounds2, SkIntersections& i) {
    SkDLine line;
    int t1Index = start ? 0 : 3;
    line[0] = cubic1[t1Index];
    // don't bother if the two cubics are connnected
    SkTDArray<double> tVals;  // OPTIMIZE: replace with hard-sized array
    for (int index = 0; index < 4; ++index) {
        if (index == t1Index) {
            continue;
        }
        SkDVector dxy1 = cubic1[index] - line[0];
        dxy1 /= SkDCubic::gPrecisionUnit;
        line[1] = line[0] + dxy1;
        SkDRect lineBounds;
        lineBounds.setBounds(line);
        if (!bounds2.intersects(&lineBounds)) {
            continue;
        }
        SkIntersections local;
        if (!local.intersect(cubic2, line)) {
            continue;
        }
        for (int idx2 = 0; idx2 < local.used(); ++idx2) {
            double foundT = local[0][idx2];
            if (approximately_less_than_zero(foundT)
                    || approximately_greater_than_one(foundT)) {
                continue;
            }
            if (local.pt(idx2).approximatelyEqual(line[0])) {
                if (i.swapped()) {  // FIXME: insert should respect swap
                    i.insert(foundT, start ? 0 : 1, line[0]);
                } else {
                    i.insert(start ? 0 : 1, foundT, line[0]);
                }
            } else {
                *tVals.append() = local[0][idx2];
            }
        }
    }
    if (tVals.count() == 0) {
        return;
    }
    QSort<double>(tVals.begin(), tVals.end() - 1);
    double tMin1 = start ? 0 : 1 - LINE_FRACTION;
    double tMax1 = start ? LINE_FRACTION : 1;
    int tIdx = 0;
    do {
        int tLast = tIdx;
        while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
            ++tLast;
        }
        double tMin2 = SkTMax<double>(tVals[tIdx] - LINE_FRACTION, 0.0);
        double tMax2 = SkTMin<double>(tVals[tLast] + LINE_FRACTION, 1.0);
        int lastUsed = i.used();
        intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
        if (lastUsed == i.used()) {
            tMin2 = SkTMax<double>(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
            tMax2 = SkTMin<double>(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
            intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
        }
        tIdx = tLast + 1;
    } while (tIdx < tVals.count());
    return;
}
// 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");
}
Exemplo n.º 24
0
void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
                         const SkDRect& bounds2) {
    SkDLine line;
    int t1Index = start ? 0 : 3;
    double testT = (double) !start;
   // don't bother if the two cubics are connnected
    static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
    static const int kMaxLineCubicIntersections = 3;
    SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
    line[0] = cubic1[t1Index];
    // this variant looks for intersections with the end point and lines parallel to other points
    for (int index = 0; index < kPointsInCubic; ++index) {
        if (index == t1Index) {
            continue;
        }
        SkDVector dxy1 = cubic1[index] - line[0];
        dxy1 /= SkDCubic::gPrecisionUnit;
        line[1] = line[0] + dxy1;
        SkDRect lineBounds;
        lineBounds.setBounds(line);
        if (!bounds2.intersects(&lineBounds)) {
            continue;
        }
        SkIntersections local;
        if (!local.intersect(cubic2, line)) {
            continue;
        }
        for (int idx2 = 0; idx2 < local.used(); ++idx2) {
            double foundT = local[0][idx2];
            if (approximately_less_than_zero(foundT)
                    || approximately_greater_than_one(foundT)) {
                continue;
            }
            if (local.pt(idx2).approximatelyEqual(line[0])) {
                if (swapped()) {  // FIXME: insert should respect swap
                    insert(foundT, testT, line[0]);
                } else {
                    insert(testT, foundT, line[0]);
                }
            } else {
                tVals.push_back(foundT);
            }
        }
    }
    if (tVals.count() == 0) {
        return;
    }
    SkTQSort<double>(tVals.begin(), tVals.end() - 1);
    double tMin1 = start ? 0 : 1 - LINE_FRACTION;
    double tMax1 = start ? LINE_FRACTION : 1;
    int tIdx = 0;
    do {
        int tLast = tIdx;
        while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
            ++tLast;
        }
        double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
        double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
        int lastUsed = used();
        if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
            ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
        }
        if (lastUsed == used()) {
            tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
            tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
            if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
                ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
            }
        }
        tIdx = tLast + 1;
    } while (tIdx < tVals.count());
    return;
}
static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
                            double t2s, double t2e, SkIntersections* i, bool* subDivide) {
    SkDQuad hull = q1.subDivide(t1s, t1e);
    SkDLine line = {{hull[2], hull[0]}};
    const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
    const size_t kTestCount = SK_ARRAY_COUNT(testLines);
    SkSTArray<kTestCount * 2, double, true> tsFound;
    for (size_t index = 0; index < kTestCount; ++index) {
        SkIntersections rootTs;
        rootTs.allowNear(false);
        int roots = rootTs.intersect(q2, *testLines[index]);
        for (int idx2 = 0; idx2 < roots; ++idx2) {
            double t = rootTs[0][idx2];
#ifdef SK_DEBUG
            SkDPoint qPt = q2.ptAtT(t);
            SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
            SkASSERT(qPt.approximatelyEqual(lPt));
#endif
            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
                continue;
            }
            tsFound.push_back(rootTs[0][idx2]);
        }
    }
    int tCount = tsFound.count();
    if (tCount <= 0) {
        return true;
    }
    double tMin, tMax;
    if (tCount == 1) {
        tMin = tMax = tsFound[0];
    } else {
        SkASSERT(tCount > 1);
        SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
        tMin = tsFound[0];
        tMax = tsFound[tsFound.count() - 1];
    }
    SkDPoint end = q2.ptAtT(t2s);
    bool startInTriangle = hull.pointInHull(end);
    if (startInTriangle) {
        tMin = t2s;
    }
    end = q2.ptAtT(t2e);
    bool endInTriangle = hull.pointInHull(end);
    if (endInTriangle) {
        tMax = t2e;
    }
    int split = 0;
    SkDVector dxy1, dxy2;
    if (tMin != tMax || tCount > 2) {
        dxy2 = q2.dxdyAtT(tMin);
        for (int index = 1; index < tCount; ++index) {
            dxy1 = dxy2;
            dxy2 = q2.dxdyAtT(tsFound[index]);
            double dot = dxy1.dot(dxy2);
            if (dot < 0) {
                split = index - 1;
                break;
            }
        }
    }
    if (split == 0) {  // there's one point
        if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
            return true;
        }
        i->swap();
        return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
    }
    // At this point, we have two ranges of t values -- treat each separately at the split
    bool result;
    if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
        result = true;
    } else {
        i->swap();
        result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
    }
    if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
        result = true;
    } else {
        i->swap();
        result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
    }
    return result;
}