【问题标题】:How to calculate distance between two rectangles? (Context: a game in Lua.)如何计算两个矩形之间的距离? (背景:Lua 中的游戏。)
【发布时间】:2011-06-26 02:04:15
【问题描述】:

给定两个具有 x、y、宽度、高度(以像素为单位)和以度为单位的旋转值的矩形 - 我如何计算它们的轮廓彼此之间的最近距离?

背景:在一个用 Lua 编写的游戏中,我随机生成地图,但希望确保某些矩形彼此不会太近——这是必需的,因为如果矩形进入某个近距离,地图将变得无法解决位置,因为球需要在他们之间通过。速度不是一个大问题,因为我没有很多矩形,而且每个级别只生成一次地图。我在 StackOverflow 上找到的以前的链接是 thisthis

非常感谢!

【问题讨论】:

标签: lua distance rectangles


【解决方案1】:

不在 Lua 中,基于 M Katz 建议的 Python 代码:

def rect_distance((x1, y1, x1b, y1b), (x2, y2, x2b, y2b)):
    left = x2b < x1
    right = x1b < x2
    bottom = y2b < y1
    top = y1b < y2
    if top and left:
        return dist((x1, y1b), (x2b, y2))
    elif left and bottom:
        return dist((x1, y1), (x2b, y2b))
    elif bottom and right:
        return dist((x1b, y1), (x2, y2b))
    elif right and top:
        return dist((x1b, y1b), (x2, y2))
    elif left:
        return x1 - x2b
    elif right:
        return x2 - x1b
    elif bottom:
        return y1 - y2b
    elif top:
        return y2 - y1b
    else:             # rectangles intersect
        return 0.

在哪里

  • dist 是点之间的欧式距离
  • 正确。 1 由点 (x1, y1)(x1b, y1b) 组成
  • 正确。 2 由点 (x2, y2)(x2b, y2b) 组成

【讨论】:

  • 我是对的,最后一个(缺失的)else 案例将涵盖交集案例吗?所以如果我认为相交矩形的正确值,我可以在这里返回 0?
  • 是的,没错。我已将其包含在代码中,因为这种情况不会受到伤害。谢谢
  • 此解决方案不考虑矩形的旋转。假设左上角有一个矩形,第二个是倾斜的,那么计算的第二个点不应该是角之一。
【解决方案2】:

编辑:正如 OK 指出的那样,此解决方案假定所有矩形都是直立的。为了使它适用于 OP 要求的旋转矩形,您还必须计算从每个矩形的角到另一个矩形最近边的距离。但是在大多数情况下,如果该点位于线段的两个端点之上或之下,并且位于两个线段的左侧或右侧(电话位置 1、3、7 或 9 相对于线段)。

Agnius 的答案依赖于 DistanceBetweenLineSegments() 函数。下面是一个没有的案例分析:

(1) Check if the rects intersect. If so, the distance between them is 0.
(2) If not, think of r2 as the center of a telephone key pad, #5.
(3) r1 may be fully in one of the extreme quadrants (#1, #3, #7, or #9). If so
    the distance is the distance from one rect corner to another (e.g., if r1 is
    in quadrant #1, the distance is the distance from the lower-right corner of
    r1 to the upper-left corner of r2).
(4) Otherwise r1 is to the left, right, above, or below r2 and the distance is
    the distance between the relevant sides (e.g., if r1 is above, the distance
    is the distance between r1's low y and r2's high y).

【讨论】:

  • 这错过了旋转方面,例如如果 r1 以这样一种方式旋转,即到 r2 左上角的最短连接从 r1 的线而不是角开始。
  • @好的,你是对的。我错过了 OP 包括可以旋转矩形。我突然想到矩形没有旋转,因为这是一个很常见的问题。
【解决方案3】:

其实有一个快速的数学解决方案。

Length(Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)))

Center = ((Maximum - Minimum) / 2) + MinimumExtent = (Maximum - Minimum) / 2 的位置。 基本上零轴上方的代码是重叠的,因此距离总是正确的。

最好保持这种格式的矩形,因为它在许多情况下更可取(即旋转更容易)。

【讨论】:

  • 这个公式我不清楚。你如何计算向量的^2?您是指自身的点积吗?
  • @Suma 它是向量中每个数字的指数运算符(别名 power 2 )。
  • 如何对向量分量求和以使 Sqrt 返回标量距离? (我不懂lua,也许这是个基础知识)。
  • @Suma 对不起,我很困惑,因为我很久以前写了这段代码。 Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)) 计算每个组件的距离。然后你基本上计算向量的长度是Sqrt((x * x) + (y * y))
  • @FelixK。你能给我一个这个公式的例子吗?我不明白这里的最大值和最小值。例如,如果我们有两个点,(3,4) 和 (6,1),并假设范围分别为 2 和 4,那么我们如何计算长度?
【解决方案4】:

伪代码:

distance_between_rectangles = some_scary_big_number;
对于 Rectangle1 中的每个 edge1:
对于 Rectangle2 中的每个 edge2:
距离 = 计算最短 distance between edge1 and edge2
如果(距离 distance_between_rectangles = 距离

【讨论】:

    【解决方案5】:

    有很多算法可以解决这个问题,Agnius 算法可以正常工作。不过我更喜欢下面的,因为它看起来更直观(你可以在一张纸上做),而且它们不依赖于找到线之间的最小距离,而是依赖于点和线之间的距离。

    困难的部分是实现数学函数来找到一条线和一个点之间的距离,以及确定一个点是否面向一条线。不过,您可以使用简单的三角函数来解决所有这些问题。我有以下方法可以做到这一点。

    对于任意角度的多边形(三角形、矩形、六边形等)

    1. 如果多边形重叠,则返回 0
    2. 在两个多边形的中心之间画一条线。
    3. 从每个多边形中选择相交边。 (这里我们减少了问题)
    4. 找出这两条边的最小距离。 (您可以遍历每 4 个点并寻找到另一个形状边缘的最小距离)。

    只要形状的任意两条边形成的角度不超过 180 度,这些算法就会起作用。原因是,如果某物在 180 度以上,则意味着某些角在内部膨胀,就像在星星中一样。

    边与点之间的最短距离

    1. 如果点不面向面,则返回点与边角之间的两个距离中的最小值。
    2. 从三个点(边缘点加上单点)绘制一个三角形。
    3. 我们可以通过Pythagorean Theorem轻松获取绘制的三条线之间的距离。
    4. Heron's formula获取三角形的面积。
    5. 现在用Area = 12⋅base⋅height 计算高度,base 是边的长度。

    检查一个点是否面向边缘

    和之前一样,你从一条边和一个点组成一个三角形。现在使用Cosine law,您只需知道边缘距离即可找到所有角度。只要从边缘到点的每个角度都在 90 度以下,那么点就朝向边缘。

    如果您有兴趣,我在 Python 中实现了所有这些 here

    【讨论】:

      【解决方案6】:

      这个问题取决于什么样的距离。你想要中心的距离,边缘的距离还是最近角落的距离?

      我假设你的意思是最后一个。如果 X 和 Y 值表示矩形的中心,那么您可以通过应用此技巧找到每个角

      //Pseudo code
      Vector2 BottomLeftCorner = new Vector2(width / 2, heigth / 2);
      BottomLeftCorner = BottomLeftCorner * Matrix.CreateRotation(MathHelper.ToRadians(degrees));
      //If LUA has no built in Vector/Matrix calculus search for "rotate Vector" on the web.
      //this helps: http://www.kirupa.com/forum/archive/index.php/t-12181.html
      
      BottomLeftCorner += new Vector2(X, Y); //add the origin so that we have to world position.
      

      对所有矩形的所有角执行此操作,然后遍历所有角并计算距离(只是 abs(v1 - v2))。

      希望对你有帮助

      【讨论】:

      • 谢谢罗伊。澄清一下,我的意思是每个矩形的轮廓之间的距离,而不是最近的角——我的目标是知道一个特定给定半径的球是否仍然可以在两个矩形之间的任何点上放在两个矩形之间。想象这两个矩形是石头(都随机旋转,但从不零旋转),世界有重力——我想知道理论上是否有可能从上面掉下来的球可能会卡在两块石头。
      • 嗯,嗯,这很难计算,我会赞成这个问题,我很好奇是否有人有一个很好的算法,它应该在那里。
      【解决方案7】:

      我只是在 n 维中为此编写了代码。我无法轻易找到通用解决方案。

      // considering a rectangle object that contains two points (min and max)
      double distance(const rectangle& a, const rectangle& b) const {
          // whatever type you are using for points
          point_type closest_point;
          for (size_t i = 0; i < b.dimensions(); ++i) {
              closest_point[i] = b.min[i] > a.min[i] ? a.max[i] : a.min[i];
          }
          // use usual euclidian distance here
          return distance(a, closest_point);
      }
      

      要计算矩形和点之间的距离,您可以:

      double distance(const rectangle& a, const point_type& p) const {
          double dist = 0.0;
          for (size_t i = 0; i < dimensions(); ++i) {
              double di = std::max(std::max(a.min[i] - p[i], p[i] - a.max[i]), 0.0);
              dist += di * di;
          }
          return sqrt(dist);
      }
      

      如果要旋转其中一个矩形,则需要旋转坐标系。

      如果要旋转两个矩形,可以旋转矩形a 的坐标系。然后我们必须改变这一行:

      closest_point[i] = b.min[i] > a.min[i] ? a.max[i] : a.min[i];
      

      因为这认为在b 中只有一个候选点作为最近的顶点。您必须更改它以检查到b 中所有顶点的距离。它始终是顶点之一。

      见:https://i.stack.imgur.com/EKJmr.png

      【讨论】:

        【解决方案8】:

        另一种解决方案,计算矩形上的点数并选择距离最小的点。

        优点:适用于所有多边形。

        缺点:准确性稍差,速度较慢。

        import numpy as np
        import math
        
        POINTS_PER_LINE = 100
        
        # get points on polygon outer lines
        # format of polygons: ((x1, y1), (x2, y2), ...)
        def get_points_on_polygon(poly, points_per_line=POINTS_PER_LINE):
        
            all_res = []
        
            for i in range(len(poly)):
        
                a = poly[i]
        
                if i == 0:
                    b = poly[-1]
        
                else:
                    b = poly[i-1]
        
                res = list(np.linspace(a, b, points_per_line))
        
                all_res += res
        
            return all_res
        
        
        
        # compute minimum distance between two polygons
        # format of polygons: ((x1, y1), (x2, y2), ...)
        def min_poly_distance(poly1, poly2, points_per_line=POINTS_PER_LINE):
        
            poly1_points = get_points_on_polygon(poly1, points_per_line=points_per_line)
        
            poly2_points = get_points_on_polygon(poly2, points_per_line=points_per_line)
        
            distance = min([math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) for a in poly1_points for b in poly2_points])
        
            # slower
            # distance = min([np.linalg.norm(a - b) for a in poly1_points for b in poly2_points])
        
            return distance
        

        【讨论】:

          【解决方案9】:

          请检查Java,它有约束所有矩形都是平行的,所有相交的矩形都返回0:

             public static double findClosest(Rectangle rec1, Rectangle rec2) {
                double x1, x2, y1, y2;
                double w, h;
                if (rec1.x > rec2.x) {
                   x1 = rec2.x; w = rec2.width; x2 = rec1.x;
                } else {
                   x1 = rec1.x; w = rec1.width; x2 = rec2.x;
                }
                if (rec1.y > rec2.y) {
                   y1 = rec2.y; h = rec2.height; y2 = rec1.y;
                } else {
                   y1 = rec1.y; h = rec1.height; y2 = rec2.y;
                }
                double a = Math.max(0, x2 - x1 - w);
                double b = Math.max(0, y2 - y1 - h);
                return Math.sqrt(a*a+b*b);
             }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-07
            • 1970-01-01
            • 2019-05-17
            • 2019-02-16
            • 2016-07-01
            • 1970-01-01
            相关资源
            最近更新 更多