【问题标题】:How to find out if a ray intersects a rectangle?如何判断光线是否与矩形相交?
【发布时间】:2012-06-05 23:31:08
【问题描述】:

我们有一条射线,它从点 A(X, Y) 开始,一直持续到给定点 B(X, Y) != A。我们有一个由点 @​​987654323@ 定义的矩形,每个点都有其 (X, Y)

我想知道如何检测我们的光线是否与矩形的任何点相交(获取bool,而不是精确坐标)?计算这个值的算法是什么?

【问题讨论】:

  • 无限林维斯?请编辑您的问题标题,谢谢。

标签: algorithm math geometry 2d


【解决方案1】:

让我直说。你有一个向量v(b_x - a_x, b_y - a_y) 方向前进,并从(a_x, a_y) 开始。

考虑向量w = (b_y - a_y, a_x - b_x)。它与第一个成直角。 (使用点积进行验证。)因此,对于任何点 (p_x, p_y),您可以通过将 (p_x - a_x, p_y - a_y)w 的点积并查看符号来轻松判断它位于向量的哪一侧。

因此,用矩形的所有 4 个角取那个点积。如果任何一个给出一个 0 点积,它们就在向量上,如果符号改变有一个交集,如果符号始终相同则没有交集。

【讨论】:

  • 我认为这个问题也与光线方向有关。例如,如果光线指向右侧,但矩形在A 的左侧,则矩形可能与AB 相交,但不会与光线相交。
  • 简洁的答案。 @comingstorm 问题很容易通过要求向量的点积和至少一个p_i - a 为正数来解决。
  • @comingstorm 好点。我只是给你与线的交点。并且不像基思所说的那么容易解决,因为在这种情况下你可以很容易地有 2 个角,但矩形仍然缺少光线。我得考虑一下。
【解决方案2】:

您可以使用扫描线算法来做到这一点。

http://en.wikipedia.org/wiki/Sweep_line_algorithm

【讨论】:

    【解决方案3】:

    一种不太聪明但概念上更简单的方法:当且仅当射线与矩形的至少一个边相交时,它才与矩形相交。因此,对于矩形的每一边,找到通过端点的线与射线 AB 的交点(如果有的话);那么它只是一个范围检查,以确定该交点是否位于矩形边界上的线段的一部分,或者它是否在外部。

    【讨论】:

      【解决方案4】:

      您可能想要计算与矩形相交的射线AB 的线段(如果有)。如果你的矩形是轴对齐的,这在数字意义上会更容易计算,但逻辑应该是相似的。


      您可以将有向线L 表示为[a, b, c],这样,如果点P(X, Y)

      let L(P) =  a*X + b*Y + c
      
      then, if L(P) == 0, point P is on L
            if L(P) > 0, point P is to the left of L
            if L(P) < 0, point P is to the right of L
      

      请注意,这是多余的,因为给定任何k &gt; 0,[k*a, k*b, k*c] 代表同一行(此属性使其成为@ 987654321@)。我们还可以通过用第三个坐标增加它们来表示具有齐次坐标的点:

      2D point P = (X, Y)
      -> homogeneous coordinates [x, y, w] for P are [X, Y, 1]
         L(P)  =  L.a*P.x + L.b*P.y + L.c*P.w  ==  a*X + b*Y + c*1
      

      在任何情况下,给定矩形的两个角(例如,PQ),您可以使用 3-D 叉积计算通过 PQ 的直线的齐次坐标它们的齐次坐标:

      homogeneous coordinates for line PQ are:  [P.X, P.Y, 1] cross [Q.X, Q.Y, 1]
        -> PQ.a = P.Y - Q.Y
           PQ.b = Q.X - P.X
           PQ.c = P.X*Q.Y - Q.X*P.Y
      

      您可以在数学上验证点 P 和 Q 都在上述线 PQ 上。


      要表示与矩形相交的线段AB,首先计算向量V = B - A,如@btilly 的答案。对于齐次坐标,其工作原理如下:

      A  =  [A.X, A.Y, 1]
      B  =  [B.X, B.Y, 1]
      -> V  =  B - A  =  [B.X-A.X, B.Y-A.Y, 0]
      
      for any point C on AB: homogeneous coordinates for C = u*A + v*V
        (where u and v are not both zero)
      

      仅当uv 都为非负时,点C 才会位于直线的射线部分。 (与C = A + lambda * V 的通常表述相比,这种表示可能看起来晦涩难懂,但这样做可以避免不必要的被零除的情况......)


      现在,我们可以计算射线的交点:我们用每个端点的参数 [u,v] 坐标表示线段 AB{ start = [start.u, start.v]; end = [end.u, end.v] }

      我们以逆时针方向计算矩形的边缘,因此矩形内的点位于每条边缘的左侧/正侧 (L(P)&gt;0)。

      Starting segment is entire ray:
        start.u = 1;  start.v = 0
        end.u = 0;  end.v = 1
      
      for each counterclockwise-directed edge L of the rectangle:
        compute:
          L(A) = L.a*A.X + L.b*A.Y + L.c
          L(V) = L.a*V.X + L.b*V.Y
          L(start) = start.u * L(A) + start.v * L(V)
          L(end) = end.u * L(A) + end.v * L(V)
        if L(start) and L(end) are both less than zero:
          exit early: return "no intersection found"
        if L(start) and L(end) are both greater or equal to zero:
          do not update the segment; continue with the next line
        else, if L(start) < 0:
          update start coordinates:
            start.u :=  L(V)
            start.v := -L(A)
        else, if L(end) < 0:
          update end coordinates:
            end.u := -L(V)
            end.v :=  L(A)
      
      on normal loop exit, the ray does intersect the rectangle;
      the part of the ray inside the rectangle is the segment between points:
        homog_start = start.u * A + start.v * V
        homog_end = end.u * A + end.v * V
      return "intersection found":
        intersection_start.X = homog_start.x/homog_start.w
        intersection_start.Y = homog_start.y/homog_start.w
        intersection_end.X = homog_end.x/homog_end.w
        intersection_end.Y = homog_end.y/homog_end.w
      

      请注意,这适用于任意凸多边形,而不仅仅是矩形;上面其实是一个通用的射线/凸多边形相交算法。对于矩形,您可以展开 for 循环;而且,如果矩形是轴对齐的,则可以大大简化算术。但是,内部循环中的 4-case 决策应该对每条边保持相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-06
        • 2018-04-06
        • 2011-02-25
        • 1970-01-01
        • 2011-10-30
        • 1970-01-01
        相关资源
        最近更新 更多