在C ++中查找两个线段是否相交

时间:2016-02-18 05:58:06

标签: c++ algorithm c++11 geometry

我编写了以下程序,用于查明两个线段是否相交。对于某些情况,该程序似乎工作不正常。有人可以帮我纠正我哪里出错了吗?

bool FindLineIntersection(double start1[2], double end1[2], double start2[2], double end2[2])
{
    float denom = ((end1[0] - start1[0]) * (end2[1] - start2[1])) - ((end1[1] - start1[1]) * (end2[0] - start2[0]));

    //  AB & CD are parallel 
    if (denom == 0)
        return false;

    float numer = ((start1[1] - start2[1]) * (end2[0] - start2[0])) - ((start1[0] - start2[0]) * (end2[1] - start2[1]));
    float r = numer / denom;

    float numer2 = ((start1[1] - start2[1]) * (end1[0] - start1[0])) - ((start1[0] - start2[0]) * (end1[1] - start1[1]));

    float s = numer2 / denom;

    if ((r < 0 || r > 1) || (s < 0 || s > 1))
        return false;

        return true;
}

1 个答案:

答案 0 :(得分:3)

您想要找到两行[P]a的交叉点b

[P] = [A] + [a]*r = [B] + [b] * s

产生线性方程:

[a]*r - [b]*s = [S] - [R]

| ax   -bx |   | r |    |Sx - Rx|
|          | * |   | =  |       |
| ay   -by |   | s |    |Sy - Ry|

从你的决定因素来看,这不是你想要解决的等式。我认为你的功能应该是这样的:

bool intersection(double start1[2], double end1[2],
                  double start2[2], double end2[2])
{
    float ax = end1[0] - start1[0];     // direction of line a
    float ay = end1[1] - start1[1];     // ax and ay as above

    float bx = start2[0] - end2[0];     // direction of line b, reversed
    float by = start2[1] - end2[1];     // really -by and -by as above

    float dx = start2[0] - start1[0];   // right-hand side
    float dy = start2[1] - start1[1];

    float det = ax * by - ay * bx;

    if (det == 0) return false;

    float r = (dx * by - dy * bx) / det;
    float s = (ax * dy - ay * dx) / det;

    return !(r < 0 || r > 1 || s < 0 || s > 1);
}

编辑II :上述算法非常粗糙。它正确地计算以非零角度彼此交叉的两个线段的交点。以下问题未得到解决:

  • 共线性。当主要行列式为零时,方向矢量是平行的。要检查两个线段是否共线,请测试一个方向矢量的叉积和两个起点的差值是否为零:

        ax * dy - ay * dx == 0
    

    共线性并不一定意味着线段重叠。为此,第二条曲线的起始点和终点的系数r必须与区间[0, 1]重叠。

  • 零长度细分。这里,零长度线段的系数是不确定的,主要决定因素是零。算法可以检查长度并确定简并分段的起点是否位于另一个上。

  • 两个零长度的细分。测试点的相等性或确定点可以重合但不相交。

  • 精密。这里的代码只测试零,这在使用浮点坐标时通常不是一个好主意。测试可能应该是明智地选择的epsilon:

        if (fabs(det) < epsilon) ...
    

强健的线路交叉算法应该适应这些情况。正如他们所说,这是留给读者的练习。

编辑:我已将逻辑转换为Javascript,以便直观地检查功能。 (我在此过程中发现了两个错误,我已经修复了。)为了您的交互式交叉乐趣,将下面的网页复制到一个html文件,并在可以解释HTML5画布的浏览器中运行它。随机生成一对线,交叉时应为红色,否则为灰色。重新加载。

<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8" />
<title>Intersect!</title>
<script type="text/javascript">

    function point(cx, a)
    {
        cx.fillRect(a[0] - 3, a[1] - 3, 7, 7);
    }

    function line(cx, a, b)
    {
        cx.beginPath();
        cx.moveTo(a[0], a[1]);
        cx.lineTo(b[0], b[1]);
        cx.stroke();
    }

    function uni(x) {
        return (Math.random() * x) | 0;
    }

    window.onload = function() {
        var cv = document.getElementById("c");
        var cx = cv.getContext("2d");

        var a1 = [uni(500), uni(500)];
        var a2 = [uni(500), uni(500)];
        var b1 = [uni(500), uni(500)];
        var b2 = [uni(500), uni(500)];

        if (intersect(a1, a2, b1, b2)) {
            cx.strokeStyle = "crimson";
            cx.fillStyle = "crimson";
        } else {
            cx.strokeStyle = "slategray";
            cx.fillStyle = "slategray";
        }

        point(cx, a1);
        point(cx, a2);
        point(cx, b1);
        point(cx, b2);
        line(cx, a1, a2);
        line(cx, b1, b2);
    }

</script>
<style type="text/css">

</style>
</head>

<body>

<canvas id="c" width="500" height="500">Kann nix.</canvas>

</body>

</html>