Example #1
0
int SkIntersections::computePoints(const SkDLine& line, int used) {
    fPt[0] = line.xyAtT(fT[0][0]);
    if ((fUsed = used) == 2) {
        fPt[1] = line.xyAtT(fT[0][1]);
    }
    return fUsed;
}
Example #2
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;
        }
    }
}
Example #3
0
DEF_TEST(PathOpsAngleFindCrossEpsilon, reporter) {
    if (gDisableAngleTests) {
        return;
    }
    SkRandom ran;
    int maxEpsilon = 0;
    for (int index = 0; index < 10000000; ++index) {
        SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}};
        for (int inner = 0; inner < 10; ++inner) {
            float t = ran.nextRangeF(0.0001f, 1);
            SkDPoint dPt = line.ptAtT(t);
            SkPoint pt = dPt.asSkPoint();
            float xs[3] = { prev(pt.fX), pt.fX, next(pt.fX) };
            float ys[3] = { prev(pt.fY), pt.fY, next(pt.fY) };
            for (int xIdx = 0; xIdx < 3; ++xIdx) {
                for (int yIdx = 0; yIdx < 3; ++yIdx) {
                    SkPoint test = { xs[xIdx], ys[yIdx] };
                    float p1 = SkDoubleToScalar(line[1].fX * test.fY);
                    float p2 = SkDoubleToScalar(line[1].fY * test.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 pt={%1.7g, %1.7g}"
                            " epsilon=%d\n",
                            line[1].fX, line[1].fY, t, test.fX, test.fY, epsilon);
                        maxEpsilon = epsilon;
                    }
                }
            }
        }
    }
}
Example #4
0
bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
    if (cubicType == kLoop_SkCubicType) {
        // crib code from gpu path utils that finds t values where loop self-intersects
        // use it to find mid of t values which should be a friendly place to chop
        SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
        SkScalar ls = d[1] - tempSqrt;
        SkScalar lt = 2.f * d[0];
        SkScalar ms = d[1] + tempSqrt;
        SkScalar mt = 2.f * d[0];
        if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
            ls = ls / lt;
            ms = ms / mt;
            SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
            *t = (ls + ms) / 2;
            SkASSERT(roughly_between(0, *t, 1));
            return *t > 0 && *t < 1;
        }
    } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) {
        SkDCubic cubic;
        cubic.set(pointsPtr);
        double inflectionTs[2];
        int infTCount = cubic.findInflections(inflectionTs);
        if (infTCount == 2) {
            double maxCurvature[3];
            int roots = cubic.findMaxCurvature(maxCurvature);
#if DEBUG_CUBIC_SPLIT
            SkDebugf("%s\n", __FUNCTION__);
            cubic.dump();
            for (int index = 0; index < infTCount; ++index) {
                SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
                SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
                SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
            for (int index = 0; index < roots; ++index) {
                SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
                SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
                SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
#endif
            for (int index = 0; index < roots; ++index) {
                if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
                    *t = maxCurvature[index];
                    return *t > 0 && *t < 1;
                }
            }
        } else if (infTCount == 1) {
            *t = inflectionTs[0];
            return *t > 0 && *t < 1;
        }
    }
    return false;
}
int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
                              double x, bool flipped) {
    fMax = 3;  // cleanup parallel lines will bring this back line
    // see if end points intersect the opposite line
    double t;
    SkDPoint topPt = { x, top };
    if ((t = line.exactPoint(topPt)) >= 0) {
        insert(t, (double) flipped, topPt);
    }
    if (top != bottom) {
        SkDPoint bottomPt = { x, bottom };
        if ((t = line.exactPoint(bottomPt)) >= 0) {
            insert(t, (double) !flipped, bottomPt);
        }
        for (int index = 0; index < 2; ++index) {
            if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
                insert((double) index, flipped ? 1 - t : t, line[index]);
            }
        }
    }
    int result = vertical_coincident(line, x);
    if (result == 1 && fUsed == 0) {
        fT[0][0] = VerticalIntercept(line, x);
        double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
        if (between(top, yIntercept, bottom)) {
            fT[1][0] = (yIntercept - top) / (bottom - top);
            if (flipped) {
                // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
                for (int index = 0; index < result; ++index) {
                    fT[1][index] = 1 - fT[1][index];
                }
            }
            fPt[0].fX = x;
            fPt[0].fY = yIntercept;
            fUsed = 1;
        }
    }
    if (fAllowNear || result == 2) {
        if ((t = line.nearPoint(topPt, nullptr)) >= 0) {
            insert(t, (double) flipped, topPt);
        }
        if (top != bottom) {
            SkDPoint bottomPt = { x, bottom };
            if ((t = line.nearPoint(bottomPt, nullptr)) >= 0) {
                insert(t, (double) !flipped, bottomPt);
            }
            for (int index = 0; index < 2; ++index) {
                if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
                    insert((double) index, flipped ? 1 - t : t, line[index]);
                }
            }
        }
    }
    cleanUpParallelLines(result == 2);
    SkASSERT(fUsed <= 2);
    return fUsed;
}
int SkIntersections::horizontal(const SkDLine& line, double left, double right,
                                double y, bool flipped) {
    fMax = 3;  // clean up parallel at the end will limit the result to 2 at the most
    // see if end points intersect the opposite line
    double t;
    const SkDPoint leftPt = { left, y };
    if ((t = line.exactPoint(leftPt)) >= 0) {
        insert(t, (double) flipped, leftPt);
    }
    if (left != right) {
        const SkDPoint rightPt = { right, y };
        if ((t = line.exactPoint(rightPt)) >= 0) {
            insert(t, (double) !flipped, rightPt);
        }
        for (int index = 0; index < 2; ++index) {
            if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
                insert((double) index, flipped ? 1 - t : t, line[index]);
            }
        }
    }
    int result = horizontal_coincident(line, y);
    if (result == 1 && fUsed == 0) {
        fT[0][0] = HorizontalIntercept(line, y);
        double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
        if (between(left, xIntercept, right)) {
            fT[1][0] = (xIntercept - left) / (right - left);
            if (flipped) {
                // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
                for (int index = 0; index < result; ++index) {
                    fT[1][index] = 1 - fT[1][index];
                }
            }
            fPt[0].fX = xIntercept;
            fPt[0].fY = y;
            fUsed = 1;
        }
    }
    if (fAllowNear || result == 2) {
        if ((t = line.nearPoint(leftPt, nullptr)) >= 0) {
            insert(t, (double) flipped, leftPt);
        }
        if (left != right) {
            const SkDPoint rightPt = { right, y };
            if ((t = line.nearPoint(rightPt, nullptr)) >= 0) {
                insert(t, (double) !flipped, rightPt);
            }
            for (int index = 0; index < 2; ++index) {
                if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
                    insert((double) index, flipped ? 1 - t : t, line[index]);
                }
            }
        }
    }
    cleanUpParallelLines(result == 2);
    return fUsed;
}
Example #7
0
// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2)
// OPTIMIZE: a specialty routine could speed this up -- may not be called very often though
bool SkDLine::NearRay(double x1, double y1, double x2, double y2) {
    double denom1 = x1 * x1 + y1 * y1;
    double denom2 = x2 * x2 + y2 * y2;
    SkDLine line = {{{0, 0}, {x1, y1}}};
    SkDPoint pt = {x2, y2};
    if (denom2 > denom1) {
        SkTSwap(line[1], pt);
    }
    return line.nearRay(pt);
}
static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
                          const SkIntersections& ts) {
    for (int i = 0; i < ts.used(); ++i) {
        SkDPoint result1 = line1.ptAtT(ts[0][i]);
        SkDPoint result2 = line2.ptAtT(ts[1][i]);
        if (!result1.approximatelyEqual(result2)) {
            REPORTER_ASSERT(reporter, ts.used() != 1);
            result2 = line2.ptAtT(ts[1][i ^ 1]);
            REPORTER_ASSERT(reporter, result1.approximatelyEqual(result2));
            REPORTER_ASSERT(reporter, result1.approximatelyEqual(ts.pt(i).asSkPoint()));
        }
    }
}
static void testLineIntersect(skiatest::Reporter* reporter, const SkDQuad& quad,
                              const SkDLine& line, const double x, const double y) {
    char pathStr[1024];
    sk_bzero(pathStr, sizeof(pathStr));
    char* str = pathStr;
    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", quad[0].fX, quad[0].fY);
    str += sprintf(str, "    path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].fX,
                   quad[1].fY, quad[2].fX, quad[2].fY);
    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", line[0].fX, line[0].fY);
    str += sprintf(str, "    path.lineTo(%1.9g, %1.9g);\n", line[1].fX, line[1].fY);

    SkIntersections intersections;
    bool flipped = false;
    int result = doIntersect(intersections, quad, line, flipped);
    bool found = false;
    for (int index = 0; index < result; ++index) {
        double quadT = intersections[0][index];
        SkDPoint quadXY = quad.ptAtT(quadT);
        double lineT = intersections[1][index];
        SkDPoint lineXY = line.ptAtT(lineT);
        if (quadXY.approximatelyEqual(lineXY)) {
            found = true;
        }
    }
    REPORTER_ASSERT(reporter, found);
}
static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) {
    for (int index = 0; index < 3; ++index) {
        double t = q1.nearestT(q2[index]);
        SkDPoint onQuad = q1.ptAtT(t);
        SkDebugf("%s t=%1.9g (%1.9g,%1.9g) dist=%1.9g\n", __FUNCTION__, t, onQuad.fX, onQuad.fY,
                onQuad.distance(q2[index]));
        double left[3];
        left[0] = ((const SkDLine&) q1[0]).isLeft(q2[index]);
        left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]);
        SkDLine diag = {{q1[0], q1[2]}};
        left[2] = diag.isLeft(q2[index]);
        SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
                floatSign(left[1]), floatSign(left[2]),
                q1.pointInHull(q2[index]) ? "true" : "false");
    }
    SkDebugf("\n");
}
// note that this only works if both lines are neither horizontal nor vertical
int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
    // see if end points intersect the opposite line
    double t;
    for (int iA = 0; iA < 2; ++iA) {
        if (!checkEndPoint(a[iA].fX, a[iA].fY, b, &t, -1)) {
            continue;
        }
        insert(iA, t, a[iA]);
    }
    for (int iB = 0; iB < 2; ++iB) {
        if (!checkEndPoint(b[iB].fX, b[iB].fY, a, &t, -1)) {
            continue;
        }
        insert(t, iB, b[iB]);
    }
    if (used() > 0) {
        SkASSERT(fUsed <= 2);
        return used(); // coincident lines are returned here
    }
    /* Determine the intersection point of two line segments
       Return FALSE if the lines don't intersect
       from: http://paulbourke.net/geometry/lineline2d/ */
    double axLen = a[1].fX - a[0].fX;
    double ayLen = a[1].fY - a[0].fY;
    double bxLen = b[1].fX - b[0].fX;
    double byLen = b[1].fY - b[0].fY;
    /* Slopes match when denom goes to zero:
                      axLen / ayLen ==                   bxLen / byLen
    (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
             byLen  * axLen         ==  ayLen          * bxLen
             byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
     */
    double denom = byLen * axLen - ayLen * bxLen;
    double ab0y = a[0].fY - b[0].fY;
    double ab0x = a[0].fX - b[0].fX;
    double numerA = ab0y * bxLen - byLen * ab0x;
    double numerB = ab0y * axLen - ayLen * ab0x;
    bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)
            || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB);
    numerA /= denom;
    numerB /= denom;
    if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA)
            && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA)
            && !sk_double_isnan(numerB)) {
        if (mayNotOverlap) {
            return 0;
        }
        fT[0][0] = numerA;
        fT[1][0] = numerB;
        fPt[0] = a.xyAtT(numerA);
        return computePoints(a, 1);
    }
    return 0;
}
Example #12
0
bool SkDCubic::controlsContainedByEnds() const {
    SkDVector startTan = fPts[1] - fPts[0];
    if (startTan.fX == 0 && startTan.fY == 0) {
        startTan = fPts[2] - fPts[0];
    }
    SkDVector endTan = fPts[2] - fPts[3];
    if (endTan.fX == 0 && endTan.fY == 0) {
        endTan = fPts[1] - fPts[3];
    }
    if (startTan.dot(endTan) >= 0) {
        return false;
    }
    SkDLine startEdge = {{fPts[0], fPts[0]}};
    startEdge[1].fX -= startTan.fY;
    startEdge[1].fY += startTan.fX;
    SkDLine endEdge = {{fPts[3], fPts[3]}};
    endEdge[1].fX -= endTan.fY;
    endEdge[1].fY += endTan.fX;
    double leftStart1 = startEdge.isLeft(fPts[1]);
    if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
        return false;
    }
    double leftEnd1 = endEdge.isLeft(fPts[1]);
    if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
        return false;
    }
    return leftStart1 * leftEnd1 >= 0;
}
Example #13
0
static void setup(const SortSet* set, const size_t idx,
        SkOpSegment* seg, int* ts, const SkPoint& startPt) {
    SkPoint start, end;
    const SkPoint* data = set[idx].ptData;
    bool useIntersectPt = startPt.fX != 0 || startPt.fY != 0;
    if (useIntersectPt) {
        start = startPt;
        end = set[idx].endPt;
    }
    switch(set[idx].ptCount) {
        case 2: {
            SkASSERT(ValidPoints(data, 2));
            seg->addLine(data, false, false);
            SkDLine dLine;
            dLine.set(set[idx].ptData);
            SkASSERT(ValidLine(dLine));
            if (useIntersectPt) {
                break;
            }
            start = dLine.ptAtT(set[idx].tStart).asSkPoint();
            end = dLine.ptAtT(set[idx].tEnd).asSkPoint();
            } break;
        case 3: {
            SkASSERT(ValidPoints(data, 3));
            seg->addQuad(data, false, false);
            SkDQuad dQuad;
            dQuad.set(set[idx].ptData);
            SkASSERT(ValidQuad(dQuad));
             if (useIntersectPt) {
                break;
            }
            start = dQuad.ptAtT(set[idx].tStart).asSkPoint();
            end = dQuad.ptAtT(set[idx].tEnd).asSkPoint();
            } break;
        case 4: {
            SkASSERT(ValidPoints(data, 4));
            seg->addCubic(data, false, false);
            SkDCubic dCubic;
            dCubic.set(set[idx].ptData);
            SkASSERT(ValidCubic(dCubic));
            if (useIntersectPt) {
                break;
            }
            start = dCubic.ptAtT(set[idx].tStart).asSkPoint();
            end = dCubic.ptAtT(set[idx].tEnd).asSkPoint();
            } break;
    }
    double tStart = set[idx].tStart;
    double tEnd = set[idx].tEnd;
    seg->addT(NULL, start, tStart);
    seg->addT(NULL, end, tEnd);
    if (tStart != 0 && tEnd != 0) {
        seg->addT(NULL, set[idx].ptData[0], 0);
    }
    if (tStart != 1 && tEnd != 1) {
        seg->addT(NULL, set[idx].ptData[set[idx].ptCount - 1], 1);
    }
    int tIndex = 0;
    ts[0] = 0;
    ts[1] = 1;
    do {
        if (seg->t(tIndex) == set[idx].tStart) {
            ts[0] = tIndex;
        }
        if (seg->t(tIndex) == set[idx].tEnd) {
            ts[1] = tIndex;
        }
        if (seg->t(tIndex) >= 1) {
            break;
        }
    } while (++tIndex);
}
Example #14
0
void DumpT(const SkDQuad& quad, double t) {
    SkDLine line = {{quad.ptAtT(t), quad[0]}};
    line.dump();
}
Example #15
0
int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
    double axLen = a[1].fX - a[0].fX;
    double ayLen = a[1].fY - a[0].fY;
    double bxLen = b[1].fX - b[0].fX;
    double byLen = b[1].fY - b[0].fY;
    /* Slopes match when denom goes to zero:
                      axLen / ayLen ==                   bxLen / byLen
    (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
             byLen  * axLen         ==  ayLen          * bxLen
             byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
     */
    double denom = byLen * axLen - ayLen * bxLen;
    double ab0y = a[0].fY - b[0].fY;
    double ab0x = a[0].fX - b[0].fX;
    double numerA = ab0y * bxLen - byLen * ab0x;
    double numerB = ab0y * axLen - ayLen * ab0x;
    bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)
            || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB);
    numerA /= denom;
    numerB /= denom;
    if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA)
            && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA)
            && !sk_double_isnan(numerB)) {
        if (mayNotOverlap) {
            return fUsed = 0;
        }
        fT[0][0] = numerA;
        fT[1][0] = numerB;
        fPt[0] = a.xyAtT(numerA);
        return computePoints(a, 1);
    }
   /* See if the axis intercepts match:
              ay - ax * ayLen / axLen  ==          by - bx * ayLen / axLen
     axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
     axLen *  ay - ax * ayLen          == axLen *  by - bx * ayLen
    */
    if (!AlmostEqualUlps(axLen * a[0].fY - ayLen * a[0].fX,
            axLen * b[0].fY - ayLen * b[0].fX)) {
        return fUsed = 0;
    }
    const double* aPtr;
    const double* bPtr;
    if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
        aPtr = &a[0].fX;
        bPtr = &b[0].fX;
    } else {
        aPtr = &a[0].fY;
        bPtr = &b[0].fY;
    }
    double a0 = aPtr[0];
    double a1 = aPtr[2];
    double b0 = bPtr[0];
    double b1 = bPtr[2];
    // OPTIMIZATION: restructure to reject before the divide
    // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
    // (except efficient)
    double aDenom = a0 - a1;
    if (approximately_zero(aDenom)) {
        if (!between(b0, a0, b1)) {
            return fUsed = 0;
        }
        fT[0][0] = fT[0][1] = 0;
    } else {
        double at0 = (a0 - b0) / aDenom;
        double at1 = (a0 - b1) / aDenom;
        if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
            return fUsed = 0;
        }
        fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
        fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
    }
    double bDenom = b0 - b1;
    if (approximately_zero(bDenom)) {
        fT[1][0] = fT[1][1] = 0;
    } else {
        int bIn = aDenom * bDenom < 0;
        fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0);
        fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0);
    }
    bool second = fabs(fT[0][0] - fT[0][1]) > FLT_EPSILON;
    SkASSERT((fabs(fT[1][0] - fT[1][1]) <= FLT_EPSILON) ^ second);
    return computePoints(a, 1 + second);
}
int SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
    SkDCubic cubic;
    cubic.set(pointsPtr);
    if (cubic.monotonicInX() && cubic.monotonicInY()) {
        return 0;
    }
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
    switch (cubicType) {
        case kLoop_SkCubicType: {
            // crib code from gpu path utils that finds t values where loop self-intersects
            // use it to find mid of t values which should be a friendly place to chop
            SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
            SkScalar ls = d[1] - tempSqrt;
            SkScalar lt = 2.f * d[0];
            SkScalar ms = d[1] + tempSqrt;
            SkScalar mt = 2.f * d[0];
            if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
                ls = ls / lt;
                ms = ms / mt;
                SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
                t[0] = (ls + ms) / 2;
                SkASSERT(roughly_between(0, *t, 1));
                return (int) (t[0] > 0 && t[0] < 1);
            }
        }
        // fall through if no t value found
        case kSerpentine_SkCubicType:
        case kCusp_SkCubicType: {
            double inflectionTs[2];
            int infTCount = cubic.findInflections(inflectionTs);
            double maxCurvature[3];
            int roots = cubic.findMaxCurvature(maxCurvature);
    #if DEBUG_CUBIC_SPLIT
            SkDebugf("%s\n", __FUNCTION__);
            cubic.dump();
            for (int index = 0; index < infTCount; ++index) {
                SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
                SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
                SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
            for (int index = 0; index < roots; ++index) {
                SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
                SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
                SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
    #endif
            if (infTCount == 2) {
                for (int index = 0; index < roots; ++index) {
                    if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
                        t[0] = maxCurvature[index];
                        return (int) (t[0] > 0 && t[0] < 1);
                    }
                }
            } else {
                int resultCount = 0;
                // FIXME: constant found through experimentation -- maybe there's a better way....
                double precision = cubic.calcPrecision() * 2;
                for (int index = 0; index < roots; ++index) {
                    double testT = maxCurvature[index];
                    if (0 >= testT || testT >= 1) {
                        continue;
                    }
                    // don't call dxdyAtT since we want (0,0) results
                    SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
                            derivative_at_t(&cubic.fPts[0].fY, testT) };
                    double dPtLen = dPt.length();
                    if (dPtLen < precision) {
                        t[resultCount++] = testT;
                    }
                }
                if (!resultCount && infTCount == 1) {
                    t[0] = inflectionTs[0];
                    resultCount = (int) (t[0] > 0 && t[0] < 1);
                }
                return resultCount;
            }
        }
        default:
            ;
    }
    return 0;
}
// note that this only works if both lines are neither horizontal nor vertical
int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
    fMax = 3;  // note that we clean up so that there is no more than two in the end
    // see if end points intersect the opposite line
    double t;
    for (int iA = 0; iA < 2; ++iA) {
        if ((t = b.exactPoint(a[iA])) >= 0) {
            insert(iA, t, a[iA]);
        }
    }
    for (int iB = 0; iB < 2; ++iB) {
        if ((t = a.exactPoint(b[iB])) >= 0) {
            insert(t, iB, b[iB]);
        }
    }
    /* Determine the intersection point of two line segments
       Return FALSE if the lines don't intersect
       from: http://paulbourke.net/geometry/lineline2d/ */
    double axLen = a[1].fX - a[0].fX;
    double ayLen = a[1].fY - a[0].fY;
    double bxLen = b[1].fX - b[0].fX;
    double byLen = b[1].fY - b[0].fY;
    /* Slopes match when denom goes to zero:
                      axLen / ayLen ==                   bxLen / byLen
    (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
             byLen  * axLen         ==  ayLen          * bxLen
             byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
     */
    double axByLen = axLen * byLen;
    double ayBxLen = ayLen * bxLen;
    // detect parallel lines the same way here and in SkOpAngle operator <
    // so that non-parallel means they are also sortable
    bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen)
            : NotAlmostDequalUlps(axByLen, ayBxLen);
    if (unparallel && fUsed == 0) {
        double ab0y = a[0].fY - b[0].fY;
        double ab0x = a[0].fX - b[0].fX;
        double numerA = ab0y * bxLen - byLen * ab0x;
        double numerB = ab0y * axLen - ayLen * ab0x;
        double denom = axByLen - ayBxLen;
        if (between(0, numerA, denom) && between(0, numerB, denom)) {
            fT[0][0] = numerA / denom;
            fT[1][0] = numerB / denom;
            computePoints(a, 1);
        }
    }
/* Allow tracking that both sets of end points are near each other -- the lines are entirely 
   coincident -- even when the end points are not exactly the same.
   Mark this as a 'wild card' for the end points, so that either point is considered totally
   coincident. Then, avoid folding the lines over each other, but allow either end to mate 
   to the next set of lines.
 */
    if (fAllowNear || !unparallel) {
        double aNearB[2];
        double bNearA[2];
        bool aNotB[2] = {false, false};
        bool bNotA[2] = {false, false};
        int nearCount = 0;
        for (int index = 0; index < 2; ++index) {
            aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]);
            nearCount += t >= 0;
            bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]);
            nearCount += t >= 0;
        }
        if (nearCount > 0) {
            // Skip if each segment contributes to one end point.
            if (nearCount != 2 || aNotB[0] == aNotB[1]) {
                for (int iA = 0; iA < 2; ++iA) {
                    if (!aNotB[iA]) {
                        continue;
                    }
                    int nearer = aNearB[iA] > 0.5;
                    if (!bNotA[nearer]) {
                        continue;
                    }
                    SkASSERT(a[iA] != b[nearer]);
                    SkASSERT(iA == (bNearA[nearer] > 0.5));
                    insertNear(iA, nearer, a[iA], b[nearer]);
                    aNearB[iA] = -1;
                    bNearA[nearer] = -1;
                    nearCount -= 2;
                }
            }
            if (nearCount > 0) {
                for (int iA = 0; iA < 2; ++iA) {
                    if (aNearB[iA] >= 0) {
                        insert(iA, aNearB[iA], a[iA]);
                    }
                }
                for (int iB = 0; iB < 2; ++iB) {
                    if (bNearA[iB] >= 0) {
                        insert(bNearA[iB], iB, b[iB]);
                    }
                }
            }
        }
    }
    cleanUpParallelLines(!unparallel);
    SkASSERT(fUsed <= 2);
    return fUsed;
}
void SkIntersections::computePoints(const SkDLine& line, int used) {
    fPt[0] = line.ptAtT(fT[0][0]);
    if ((fUsed = used) == 2) {
        fPt[1] = line.ptAtT(fT[0][1]);
    }
}