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

Find whether two line segments intersect or not in C++

本文关键字:C++ 相交 是否 两条线 段是否 查找      更新时间:2023-10-16

我编写了以下程序来找出两条线段是否相交。在某些情况下,该程序似乎无法正常工作。有人可以帮我纠正我哪里出错了吗?

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;
}

你想找到两条线ab的交点[P]

[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);
}

编辑二:上面介绍的算法非常粗糙。它正确计算以非零角度相互交叉的两个线段的交点。以下问题未解决:

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

        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>