[英]How to calculate distance between two rectangles? (Context: a game in Lua.)
不在 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
是点之间的欧几里德距离(x1, y1)
和(x1b, y1b)
(x2, y2)
和(x2b, y2b)
编辑:正如 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).
实际上有一个快速的数学解决方案。
Length(Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)))
其中Center = ((Maximum - Minimum) / 2) + Minimum
and Extent = (Maximum - Minimum) / 2
。 基本上零轴上方的代码是重叠的,因此距离总是正确的。
最好将矩形保持在这种格式,因为它在许多情况下更可取(ae 旋转更容易)。
伪代码:
distance_between_rectangles = some_scary_big_number;
对于 Rectangle1 中的每个 edge1:
对于 Rectangle2 中的每个 edge2:
距离 = 计算edge1 和 edge2 之间的最短距离
如果(距离 < distance_between_rectangles)
distance_between_rectangles = 距离
有很多算法可以解决这个问题,Agnius 算法工作正常。 但是我更喜欢下面的,因为它看起来更直观(你可以在一张纸上做),而且它们不依赖于找到线之间的最小距离,而是点和线之间的距离。
困难的部分是实现数学函数来找到一条线和一个点之间的距离,以及确定一个点是否面对一条线。 不过,您可以使用简单的三角函数来解决所有这些问题。 我有以下方法来做到这一点。
对于任意角度的多边形(三角形、矩形、六边形等)
只要形状的任意两条边不会产生超过 180 度的角度,这些算法就可以工作。 原因是,如果某物超过 180 度,则意味着某些角落在内部膨胀,就像星星一样。
边与点之间的最小距离
Area = 12⋅base⋅height
计算高度, base
是边的长度。检查点是否面向边缘
和以前一样,你用一条边和一个点制作一个三角形。 现在使用余弦定律,您只需知道边缘距离就可以找到所有角度。 只要边到点的每个角度都在 90 度以下,则该点面向边。
如果您有兴趣,我在这里有一个 Python 实现。
请检查 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);
}
这个问题要看什么样的距离。 您想要中心距离、边缘距离还是最近角的距离?
我猜你是说最后一个。 如果 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))。
我希望这对你有帮助
我只是在 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
所有顶点的距离。 它始终是顶点之一。
我解决问题的方法:
这是 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);
}
另一种解决方案,它计算矩形上的点数并选择距离最小的一对。
优点:适用于所有多边形。
缺点:准确性稍差且速度较慢。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.