示例#1
0
bool isLinear(const Quadratic& quad, int startIndex, int endIndex) {
    LineParameters lineParameters;
    lineParameters.quadEndPoints(quad, startIndex, endIndex);
    // FIXME: maybe it's possible to avoid this and compare non-normalized
    lineParameters.normalize();
    double distance = lineParameters.controlPtDistance(quad);
    return approximately_zero(distance);
}
示例#2
0
bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
    LineParameters lineParameters;
    lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
    // FIXME: maybe it's possible to avoid this and compare non-normalized
    lineParameters.normalize();
    double distance = lineParameters.controlPtDistance(cubic, 1);
    if (!approximately_zero(distance)) {
        return false;
    }
    distance = lineParameters.controlPtDistance(cubic, 2);
    return approximately_zero(distance);
}
示例#3
0
bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
    LineParameters lineParameters;
    lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
    double normalSquared = lineParameters.normalSquared();
    double distance[2]; // distance is not normalized
    int mask = other_two(startIndex, endIndex);
    int inner1 = startIndex ^ mask;
    int inner2 = endIndex ^ mask;
    lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
    double limit = normalSquared;
    int index;
    for (index = 0; index < 2; ++index) {
        double distSq = distance[index];
        distSq *= distSq;
        if (approximately_greater(distSq, limit)) {
            return false;
        }
    }
    return true;
}
示例#4
0
void LineParameter_Test() {
    for (size_t index = firstLineParameterTest; index < tests_count; ++index) {
        LineParameters lineParameters;
        const Cubic& cubic = tests[index];
        lineParameters.cubicEndPoints(cubic);
        double denormalizedDistance[2];
        denormalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
        denormalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
        double normalSquared = lineParameters.normalSquared();
        size_t inner;
        for (inner = 0; inner < 2; ++inner) {
            double distSq = denormalizedDistance[inner];
            distSq *= distSq;
            double answersSq = answers[index][inner];
            answersSq *= answersSq;
            if (AlmostEqualUlps(distSq, normalSquared * answersSq)) {
                continue;
            }
            SkDebugf("%s [%d,%d] denormalizedDistance:%g != answer:%g"
                    " distSq:%g answerSq:%g normalSquared:%g\n",
                    __FUNCTION__, (int)index, (int)inner,
                    denormalizedDistance[inner], answers[index][inner],
                    distSq, answersSq, normalSquared);
        }
        lineParameters.normalize();
        double normalizedDistance[2];
        normalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
        normalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
        for (inner = 0; inner < 2; ++inner) {
            if (AlmostEqualUlps(fabs(normalizedDistance[inner]), answers[index][inner])) {
                continue;
            }
            SkDebugf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n",
                    __FUNCTION__, (int)index, (int)inner,
                    normalizedDistance[inner], answers[index][inner]);
        }
    }
}
示例#5
0
static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
    // look to see if non-coincident data basically has unsortable tangents

    // look to see if a point between non-coincident data is on the curve
    int cIndex;
    for (int uIndex = 0; uIndex < i.fUsed; ) {
        double bestDist1 = 1;
        double bestDist2 = 1;
        int closest1 = -1;
        int closest2 = -1;
        for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) {
            double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]);
            if (bestDist1 > dist) {
                bestDist1 = dist;
                closest1 = cIndex;
            }
            dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]);
            if (bestDist2 > dist) {
                bestDist2 = dist;
                closest2 = cIndex;
            }
        }
        _Line ends;
        _Point mid;
        double t1 = i.fT[0][uIndex];
        xy_at_t(q1, t1, ends[0].x, ends[0].y);
        xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y);
        double midT = (t1 + i.fCoincidentT[0][closest1]) / 2;
        xy_at_t(q1, midT, mid.x, mid.y);
        LineParameters params;
        params.lineEndPoints(ends);
        double midDist = params.pointDistance(mid);
        // Note that we prefer to always measure t error, which does not scale,
        // instead of point error, which is scale dependent. FIXME
        if (!approximately_zero(midDist)) {
            ++uIndex;
            continue;
        }
        double t2 = i.fT[1][uIndex];
        xy_at_t(q2, t2, ends[0].x, ends[0].y);
        xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y);
        midT = (t2 + i.fCoincidentT[1][closest2]) / 2;
        xy_at_t(q2, midT, mid.x, mid.y);
        params.lineEndPoints(ends);
        midDist = params.pointDistance(mid);
        if (!approximately_zero(midDist)) {
            ++uIndex;
            continue;
        }
        // if both midpoints are close to the line, lengthen coincident span
        int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs
        if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1])) {
            i.fCoincidentT[0][closest1] = t1;
        }
        cEnd = closest2 ^ 1;
        if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2])) {
            i.fCoincidentT[0][closest2] = t2;
        }
        int remaining = --i.fUsed - uIndex;
        if (remaining > 0) {
            memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) * remaining);
            memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) * remaining);
        }
    }
    // if coincident data is subjectively a tiny span, replace it with a single point
    for (cIndex = 0; cIndex < i.fCoincidentUsed; ) {
        double start1 = i.fCoincidentT[0][cIndex];
        double end1 = i.fCoincidentT[0][cIndex + 1];
        _Line ends1;
        xy_at_t(q1, start1, ends1[0].x, ends1[0].y);
        xy_at_t(q1, end1, ends1[1].x, ends1[1].y);
        if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0].y, ends1[1].y)) {
            cIndex += 2;
            continue;
        }
        double start2 = i.fCoincidentT[1][cIndex];
        double end2 = i.fCoincidentT[1][cIndex + 1];
        _Line ends2;
        xy_at_t(q2, start2, ends2[0].x, ends2[0].y);
        xy_at_t(q2, end2, ends2[1].x, ends2[1].y);
        // again, approximately should be used with T values, not points FIXME
        if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0].y, ends2[1].y)) {
            cIndex += 2;
            continue;
        }
        if (approximately_less_than_zero(start1) || approximately_less_than_zero(end1)) {
            start1 = 0;
        } else if (approximately_greater_than_one(start1) || approximately_greater_than_one(end1)) {
            start1 = 1;
        } else {
            start1 = (start1 + end1) / 2;
        }
        if (approximately_less_than_zero(start2) || approximately_less_than_zero(end2)) {
            start2 = 0;
        } else if (approximately_greater_than_one(start2) || approximately_greater_than_one(end2)) {
            start2 = 1;
        } else {
            start2 = (start2 + end2) / 2;
        }
        i.insert(start1, start2);
        i.fCoincidentUsed -= 2;
        int remaining = i.fCoincidentUsed - cIndex;
        if (remaining > 0) {
            memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2], sizeof(i.fCoincidentT[0][0]) * remaining);
            memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2], sizeof(i.fCoincidentT[1][0]) * remaining);
        }
    }
}
示例#6
0
static int check_linear(const Cubic& cubic, Cubic& reduction,
        int minX, int maxX, int minY, int maxY) {
    int startIndex = 0;
    int endIndex = 3;
    while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
        --endIndex;
        if (endIndex == 0) {
            printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
            assert(0);
        }
    }
    LineParameters lineParameters;
    lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
    double normalSquared = lineParameters.normalSquared();
    double distance[2]; // distance is not normalized
    int mask = other_two(startIndex, endIndex);
    int inner1 = startIndex ^ mask;
    int inner2 = endIndex ^ mask;
    lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
    double limit = normalSquared * SquaredEpsilon;
    int index;
    for (index = 0; index < 2; ++index) {
        double distSq = distance[index];
        distSq *= distSq;
        if (distSq > limit) {
            return 0;
        }
    }
    // four are colinear: return line formed by outside
    reduction[0] = cubic[0];
    reduction[1] = cubic[3];
    int sameSide1;
    int sameSide2;
    bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y;
    if (useX) {
        sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x);
        sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x);
    } else {
        sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y);
        sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y);
    }
    if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) {
        return 2;
    }
    double tValues[2];
    int roots;
    if (useX) {
        roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
    } else {
        roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
    }
    for (index = 0; index < roots; ++index) {
        _Point extrema;
        extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]);
        extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]);
        // sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller
        int replace;
        if (useX) {
            if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) {
                continue;
            }
            replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x)
                    ^ cubic[0].x < cubic[3].x;
        } else {
            if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) {
                continue;
            }
            replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y)
                    ^ cubic[0].y < cubic[3].y;
        }
        reduction[replace] = extrema;
    }
    return 2;
}
示例#7
0
// return false if unable to clip (e.g., unable to create implicit line)
// caller should subdivide, or create degenerate if the values are too small
bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT) {
    minT = 1;
    maxT = 0;
    // determine normalized implicit line equation for pt[0] to pt[3]
    //   of the form ax + by + c = 0, where a*a + b*b == 1
    
    // find the implicit line equation parameters
    LineParameters endLine;
    endLine.cubicEndPoints(cubic1);
    if (!endLine.normalize()) {
        printf("line cannot be normalized: need more code here\n");
        return false;
    }

    double distance[2];
    endLine.controlPtDistance(cubic1, distance);
    
    // find fat line
    double top = distance[0];
    double bottom = distance[1];
    if (top > bottom) {
        std::swap(top, bottom);
    }
    if (top * bottom >= 0) {
        const double scale = 3/4.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (13)
        if (top < 0) {
            top *= scale;
            bottom = 0;
        } else {
            top = 0;
            bottom *= scale;
        }
    } else {
        const double scale = 4/9.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (15)
        top *= scale;
        bottom *= scale;
    }
    
    // compute intersecting candidate distance
    Cubic distance2y; // points with X of (0, 1/3, 2/3, 1)
    endLine.cubicDistanceY(cubic2, distance2y);
    
    int flags = 0;
    if (approximately_lesser(distance2y[0].y, top)) {
        flags |= kFindTopMin;
    } else if (approximately_greater(distance2y[0].y, bottom)) {
        flags |= kFindBottomMin;
    } else {
        minT = 0;
    }

    if (approximately_lesser(distance2y[3].y, top)) {
        flags |= kFindTopMax;
    } else if (approximately_greater(distance2y[3].y, bottom)) {
        flags |= kFindBottomMax;
    } else {
        maxT = 1;
    }
    // Find the intersection of distance convex hull and fat line.
    char to_0[2];
    char to_3[2];
    bool do_1_2_edge = convex_x_hull(distance2y, to_0, to_3);
    x_at(distance2y[0], distance2y[to_0[0]], top, bottom, flags, minT, maxT);
    if (to_0[0] != to_0[1]) {
        x_at(distance2y[0], distance2y[to_0[1]], top, bottom, flags, minT, maxT);
    }
    x_at(distance2y[to_3[0]], distance2y[3], top, bottom, flags, minT, maxT);
    if (to_3[0] != to_3[1]) {
        x_at(distance2y[to_3[1]], distance2y[3], top, bottom, flags, minT, maxT);
    }
    if (do_1_2_edge) {
        x_at(distance2y[1], distance2y[2], top, bottom, flags, minT, maxT);
    }
    
    return minT < maxT; // returns false if distance shows no intersection
}
示例#8
0
// return false if unable to clip (e.g., unable to create implicit line)
// caller should subdivide, or create degenerate if the values are too small
bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT) {
    minT = 1;
    maxT = 0;
    // determine normalized implicit line equation for pt[0] to pt[3]
    //   of the form ax + by + c = 0, where a*a + b*b == 1

    // find the implicit line equation parameters
    LineParameters endLine;
    endLine.quadEndPoints(q1);
    if (!endLine.normalize()) {
        printf("line cannot be normalized: need more code here\n");
        assert(0);
        return false;
    }

    double distance = endLine.controlPtDistance(q1);

    // find fat line
    double top = 0;
    double bottom = distance / 2; // http://students.cs.byu.edu/~tom/557/text/cic.pdf (7.6)
    if (top > bottom) {
        std::swap(top, bottom);
    }

    // compute intersecting candidate distance
    Quadratic distance2y; // points with X of (0, 1/2, 1)
    endLine.quadDistanceY(q2, distance2y);

    int flags = 0;
    if (approximately_lesser(distance2y[0].y, top)) {
        flags |= kFindTopMin;
    } else if (approximately_greater(distance2y[0].y, bottom)) {
        flags |= kFindBottomMin;
    } else {
        minT = 0;
    }

    if (approximately_lesser(distance2y[2].y, top)) {
        flags |= kFindTopMax;
    } else if (approximately_greater(distance2y[2].y, bottom)) {
        flags |= kFindBottomMax;
    } else {
        maxT = 1;
    }
    // Find the intersection of distance convex hull and fat line.
    int idx = 0;
    do {
        int next = idx + 1;
        if (next == 3) {
            next = 0;
        }
        x_at(distance2y[idx], distance2y[next], top, bottom, flags, minT, maxT);
        idx = next;
    } while (idx);
#if DEBUG_BEZIER_CLIP
    _Rect r1, r2;
    r1.setBounds(q1);
    r2.setBounds(q2);
    _Point testPt = {0.487, 0.337};
    if (r1.contains(testPt) && r2.contains(testPt)) {
        printf("%s q1=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
                " q2=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) minT=%1.9g maxT=%1.9g\n",
                __FUNCTION__, q1[0].x, q1[0].y, q1[1].x, q1[1].y, q1[2].x, q1[2].y,
                q2[0].x, q2[0].y, q2[1].x, q2[1].y, q2[2].x, q2[2].y, minT, maxT);
    }
#endif
    return minT < maxT; // returns false if distance shows no intersection
}