给定两个以x,y,宽度,高度为单位的矩形和以度为单位的旋转值 - 如何计算轮廓彼此的最近距离?
背景:在用Lua编写的游戏中我随机生成地图,但是想要确保某些矩形彼此不太接近 - 这是必要的,因为如果矩形进入某个近距离,地图变得无法解决位置,因为球需要在它们之间传递。速度不是一个大问题,因为我没有很多矩形,而且每个级别只生成一次地图。我在StackOverflow上找到的以前链接是this和this
非常感谢提前!
答案 0 :(得分:21)
不是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)
答案 1 :(得分:11)
编辑:正如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).
答案 2 :(得分:3)
的伪代码:
distance_between_rectangles = some_scary_big_number;
对于Rectangle1中的每个edge1:
对于Rectangle2中的每个edge2:
距离=计算最短distance between edge1 and edge2
if(距离&lt; distance_between_rectangles)
distance_between_rectangles =距离
答案 3 :(得分:1)
有很多算法可以解决这个问题,Agnius算法运行正常。但是我更喜欢下面的内容,因为它看起来更直观(你可以在一张纸上做)并且它们不依赖于找到线之间的最小距离,而是依赖点和线之间的距离。
困难的部分是实现数学函数以找到直线和点之间的距离,并找出点是否面向直线。你可以用简单的三角函数解决所有这些问题。我有以下方法来做到这一点。
对于任意角度的多边形(三角形,矩形,六边形等)
只要形状的任何两个边缘不会产生超过180度的角度,这些算法就会起作用。原因是,如果某些东西高于180度,则意味着某些角落在内部膨胀,就像在星星中一样。
边缘与点之间的最小距离
Area = 12⋅base⋅height
计算高度,base
为边长。检查某个点是否面向边缘
和以前一样,从边缘和点开始制作三角形。现在使用Cosine law,只需知道边距即可找到所有角度。只要从边缘到该点的每个角度都低于90度,该点就会面向边缘。
如果您有兴趣,我在Python中有所有这些here的实现。
答案 4 :(得分:1)
实际上有一个快速的数学解决方案。
Length(Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)))
Center = ((Maximum - Minimum) / 2) + Minimum
和Extent = (Maximum - Minimum) / 2
的位置。
基本上,零轴以上的代码是重叠的,因此距离总是正确的。
最好将矩形保持为这种格式,因为它在许多情况下更为可取(例如旋转更容易)。
答案 5 :(得分:1)
我只是为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
中所有顶点的距离。它始终是顶点之一。
答案 6 :(得分:0)
这个问题取决于什么样的距离。你想要的,中心距离,边缘距离或最近角落的距离?
我认为你的意思是最后一个。如果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 :(得分:0)
请检查这是否为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);
}