如何计算两个矩形之间的距离?(背景:Lua中的游戏。)
给定两个带有像素的x,y,宽度,高度和以度为单位的旋转值的矩形-如何计算它们之间轮廓的最近距离?
背景:我在使用Lua编写的游戏中随机生成地图,但想要确保某些矩形彼此之间不会太接近-这是必需的,因为如果矩形进入某些近距离位置,地图将变得不可解决,因为一个球需要从它们之间通过。速度不是太大的问题,因为我没有很多矩形,而且地图仅在每个级别生成一次。我在StackOverflow上找到了以下两个链接:这和这。
非常感谢!
原文链接 https://stackoverflow.com/questions/4978323
伪代码:
distance_between_rectangles = some_scary_big_number;
对于每一个矩形1的边缘edge1:
对于每一个矩形2的边缘edge2:
distance = 计算从edge1到edge2的最短距离
if (distance < distance_between_rectangles):
distance_between_rectangles = distance
在每个矩形的边缘上循环,计算两个矩形之间最短的距离。如果找到的距离比目前记录的距离更短,就更新记录的距离。使用此处提到的方法来计算从一个边缘到另一个边缘的最短距离。
编辑:正如OK所指出的,这个解决方案假设所有的矩形都是竖直的。要使其适用于 OP 所要求的旋转矩形,您还必须计算每个矩形的角到另一个矩形最近边的距离。但如果该点位于线段的两个端点以上或以下,并且在两个线段的左侧或右侧(在电话位置1、3、7或9与线段相关的位置),则在大多数情况下可以避免进行该计算。
Agnius的答案依赖于一个DistanceBetweenLineSegments()函数。这里有一个不需要这个函数的案例分析:
(1)检查矩形是否相交。如果是,则它们之间的距离为0。
(2)如果不是,则将r2视为电话键盘“#5”的中心。
(3)r1可能完全在极端象限之一中(#1,#3,#7或#9)。如果是这样,距离就是一个矩形角落到另一个矩形角落的距离(例如,如果r1在象限#1中,则距离是从r1的右下角到r2的左上角的距离)。
(4)否则,r1位于r2的左侧、右侧、上方或下方,距离就是相关边之间的距离(例如,如果r1在上方,则距离为r1的低y和r2的高y之间的距离)。
有许多算法可以解决这个问题,Agnius 算法效果很好。然而,我更喜欢下面的方法,因为它看起来更直观(你可以在纸上完成)并且它们不依赖于查找两条线之间的最小距离,而是依赖于点和线之间的距离。
困难的部分是实现数学函数,以找到一条直线和一个点之间的距离,并找到一个点是否面对一条直线。但是,你可以使用简单的三角函数解决所有这些问题。我在下面列出了解决这些问题的方法。
对于以任意角度呈放的多边形(三角形、矩形、六边形等)
- 如果多边形重叠,返回 0。
- 在两个多边形的中心之间画一条线。
- 从每个多边形中选择相交的边。(在这里,我们简化问题)
- 找到这两条边中的最小距离。(你可以只遍历每个多边形的 4 个点,查找到另一个形状的边的最小距离)。
只要形状的任意两条边不创建大于 180 度的角度,这些算法就可以工作。原因是,如果某个角度大于 180 度,那么这意味着一些角落会向内膨胀,就像一个星形。
边缘和点之间的最小距离
- 如果点没有面对面,那么返回点和边缘角之间的两个距离中的最小值。
- 从三个点(边的两个点加上单独的点)中绘制一个三角形。
- 我们可以使用勾股定理轻松获得三条绘制线之间的距离。
- 使用Heron 公式获得三角形的面积。
- 计算高度,现在使用
Area = 12â‹…baseâ‹…height
,其中base
是边的长度。
检查一个点是否面对一条边
和以前一样,从一条边和一个点绘制三角形。现在使用余弦定理仅通过知道边距离就可以找到所有角度。只要从边缘到点的每个角度都小于 90 度,该点就面向边缘。
如果你感兴趣,我在 这里 用 Python 实现了所有这些。
不是在 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: # 矩形相交
return 0.
其中:
dist
是点之间的欧几里得距离- 矩形 1 由点
(x1, y1)
和(x1b, y1b)
形成 - 矩形 2 由点
(x2, y2)
和(x2b, y2b)
形成
实际上有一个快速的数学解决方案。
Length(Max((0, 0), Abs(Center -otherCenter) -(Extent + otherExtent)))
其中 Center = ((Maximum - Minimum) / 2) + Minimum
, Extent = (Maximum - Minimum) / 2
。
基本上,上面的代码将有重叠的轴归零,因此距离始终正确。
最好保持矩形的这种格式,因为在许多情况下(例如旋转)更可取。
请检查 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);
}
我刚刚在 N 维空间写了这段代码。但是我并没有找到一个通用的解决方案。
// 考虑一个包含两个点(最小值和最大值)的矩形对象
double distance(const rectangle& a, const rectangle& b) const {
// 无论你使用的是什么类型的点
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];
}
// 在这里使用通常的欧几里德距离
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
中的距离。它总是一个顶点。
另一个解决方案是计算矩形上的点数,并选择距离最近的对。
优点:适用于所有多边形。
缺点:略微不精确且速度较慢。
import numpy as np
import math
POINTS_PER_LINE = 100
# 获取多边形外边界上的点
# 多边形格式:((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
# 计算两个多边形之间的最小距离
# 多边形格式:((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
我解决这个问题的方法:
1.将两个矩形合并成一个大矩形 2.从大矩形中减去第一个矩形和第二个矩形 3.减法后剩下的是两个矩形之间的矩形,该矩形的对角线是两个矩形之间的距离。
下面是C#的示例代码:
public static double GetRectDistance(this System.Drawing.Rectangle rect1, System.Drawing.Rectangle rect2)
{
if (rect1.IntersectsWith(rect2))
{
return 0;
}
var rectUnion = System.Drawing.Rectangle.Union(rect1, rect2);
rectUnion.Width -= rect1.Width + rect2.Width;
rectUnion.Width = Math.Max(0, rectUnion.Width);
rectUnion.Height -= rect1.Height + rect2.Height;
rectUnion.Height = Math.Max(0, rectUnion.Height);
return rectUnion.Diagonal();
}
public static double Diagonal(this System.Drawing.Rectangle rect)
{
return Math.Sqrt(rect.Height * rect.Height + rect.Width * rect.Width);
}
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
这个问题取决于你需要什么样的距离。你需要中心点的距离、边缘的距离还是最近角的距离?
我猜你指的是最后一种。如果X和Y值表示矩形的中心,那么你可以通过应用这个技巧找到每个角落。
//伪代码 Vector2 BottomLeftCorner = new Vector2(width / 2, heigth / 2); BottomLeftCorner = BottomLeftCorner * Matrix.CreateRotation(MathHelper.ToRadians(degrees)); //如果LUA没有内置的向量/矩阵计算请在网上搜索“旋转向量”。 //这个可以帮助:http://www.kirupa.com/forum/archive/index.php/t-12181.html BottomLeftCorner += new Vector2(X, Y); //添加原点以使它成为世界位置。
对所有矩形的所有角进行操作,然后只需循环遍历所有角并计算距离(使用 abs(v1 - v2))。
希望这可以帮助你。