【问题标题】:Fast detecting of line segment and rectangle intersection, trough Cohen–Sutherland algorithm通过 Cohen-Sutherland 算法快速检测线段和矩形交点
【发布时间】:2015-12-25 01:29:19
【问题描述】:

受以下问题/答案的启发,我加入了社区:Line intersection with AABB Rectangle?

在那里,原始发帖人要求一种方法来检测直线和矩形之间的交点。其中一个答案提到,使用 Cohen-Sutherland 算法将比进行 4 个 Line-to-Line 交叉点快得多。但是,没有关于如何做的解释(或者最多是模糊的),虽然我可以找到关于该定理的信息,但我没有找到任何进一步的解释/代码示例来说明如何使其适应手头的问题。

所以,我来向您询问更多信息,可能是 C#、JavaScript 或伪代码示例,说明如何使用 Cohen-Sutherland 算法有效地检测直线和矩形之间的交点。如果有另一种我不知道的其他算法的快速方法,我会欢迎启发。

非常感谢

【问题讨论】:

  • 网络上有很多参考资料,甚至原始参考资料中的 wiki 文章的链接......那你到底在寻找什么?
  • 您好,感谢您的评论。因此,有许多关于 Cohen-Sutherland 算法的许多其他用途的代码参考,例如屏幕剪辑。不是为了我所描述的,即测试一条线是否与一个矩形相交。
  • 但是...因为这个算法屏幕只是矩形,不是吗?
  • 线还是线段?请具体。
  • 嗨@Yves Daoust,嗯,我以为我提到了它的线段。抱歉,如果结果令人困惑。

标签: c# algorithm geometry line intersection


【解决方案1】:

我最喜欢的解决方案不是经典的。它依赖于闵可夫斯基和的概念,也就是膨胀。

主要思想是检测矩形与线段的交点与检测矩形和线段之和中是否包含线段端点相同。 (您在扩张矩形的同时沿线的方向侵蚀线段)。

如您所见,总和是一个轴对齐的六边形,可以看作是一个更大的矩形(原始窗口放大了线段边界框的大小),由两个三角形切出。

通过二分法,您可以通过三个测试准确判断该点是否在六边形内(通过相对顶点检查您在直线的哪一侧;六边形 => 四边形 =>三角形)。这不是首选方式,因为这些线中的大多数都是倾斜的并且具有完整的方程。

通常最好先检查较大矩形的内部,这样可以在四次比较中快速拒绝,然后与三角形的两个对角进行比较。

对于段AB和窗口[X0,X1]x[Y0,Y1],讨论会是这样的

if Xb > Xa and Yb > Ya:
    # NW-SE segment
    Dx= Xb - Xa; Dy= Yb - Ya
    if Xb < X0 or X1 + Dx < Xb or Yb < X0 or Y1 + Dy < Yb:
        # No intersection (no overlap between the bounding boxes)
    else
        if LeftOf(Xb, Yb, X0, Y0, X1 + Dx, Y0 + Dy):
            # No intersection, inside the NE triangle
        else if LeftOf(Xb, Yb, Dx, Y1 + Dy, X0, Y1):
            # No intersection, inside the SW triangle
        else:
            # Intersection, inside the hexagon
...

函数LeftOf计算由三个点组成的三角形面积的符号(众所周知的行列式)。

必须继续讨论所有象限,并且可以对水平/垂直部分进行简化。还要注意

X1 + Dx < Xb 

只是

X1 < xa

【讨论】:

  • 非常感谢 Yves 提供的解决方案。我将对其进行测试。尽管如此,就处理时间而言,我担心这听起来比通常的 Cohen-Sutherland 成本更高。以 LeftOf() 为例,它总共会被调用八次。让我们来看看。无论如何,我会支持你,但该网站不允许我缺乏声誉
  • 一点也不。代码块需要复制四次,但只调用了一个实例。此外,大边界框测试在很大一部分情况下(比如 50%)避免调用 LeftOf。最后,如果第一个 LeftOf 返回 true,则另一个是不必要的。综上所述,LeftOf 平均每个段调用不到一次(不是 8 次)!与您的看法相反,该算法比 Cohen-Sutherland 高效得多。它甚至不执行除法!
  • 更新:由于三角形的斜边是平行的,第二个 LeftOf 测试可以计算得更快一些(如果有的话)。
  • 哈,感谢您的补充说明。我会试一试。但是,我不得不承认我很难正确理解一些事情。第一部分是第一部分,关于如何在给定矩形和线的位置和旋转的情况下获取窗口。第二件事,我仍然对如何将讨论扩展到其他象限感到有些困惑,但我仍然可以在那里做一些尝试和错误。总而言之,我仍在尝试提出这一点;输入代码并不像听起来那么直观。我稍后会回来
  • 窗口计算在代码中(Dx, Dy)。不幸的是,我暂时没有时间提供完整的版本。
【解决方案2】:

在我等待更多答案的同时,我想我应该带来我自己同时实施的解决方案。我不是 100% 确定它可以完美运行(即它可以处理所有情况),但到目前为止它似乎对我有用。请注意,这是一个非常简单的解决方案,因此欢迎批评..

设 r[1:4] 为矩形的顶点,p[1:2] 为线段的顶点。然后,在 C# 中:

bool DoLineRecIntersect(Vector2 p1, Vector2 p2, Vector2 r1, Vector2 r2, Vector2 r3, Vector2 r4)
{

    if(p1.x > r1.x && p1.x > r2.x && p1.x > r3.x && p1.x > r4.x && p2.x > r1.x && p2.x > r2.x && p2.x > r3.x && p2.x > r4.x ) return false;
    if(p1.x < r1.x && p1.x < r2.x && p1.x < r3.x && p1.x < r4.x && p2.x < r1.x && p2.x < r2.x && p2.x < r3.x && p2.x < r4.x ) return false;
    if(p1.y > r1.y && p1.y > r2.y && p1.y > r3.y && p1.y > r4.y && p2.y > r1.y && p2.y > r2.y && p2.y > r3.y && p2.y > r4.y ) return false;
    if(p1.y < r1.y && p1.y < r2.y && p1.y < r3.y && p1.y < r4.y && p2.y < r1.y && p2.y < r2.y && p2.y < r3.y && p2.y < r4.y ) return false;


    float f1 = (p2.y-p1.y)*r1.x + (p1.x-p2.x)*r1.y + (p2.x*p1.y-p1.x*p2.y);
    float f2 = (p2.y-p1.y)*r2.x + (p1.x-p2.x)*r2.y + (p2.x*p1.y-p1.x*p2.y);
    float f3 = (p2.y-p1.y)*r3.x + (p1.x-p2.x)*r3.y + (p2.x*p1.y-p1.x*p2.y);
    float f4 = (p2.y-p1.y)*r4.x + (p1.x-p2.x)*r4.y + (p2.x*p1.y-p1.x*p2.y);

    if(f1<0 && f2<0 && f3<0 && f4<0) return false;
    if(f1>0 && f2>0 && f3>0 && f4>0) return false;

    return true;

}

【讨论】:

    猜你喜欢
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    相关资源
    最近更新 更多