// rotated: your rotated edge
// v(n-1) any point from the edge.
// testpoint: the point you want to find out which side it's on.
side = sign (rotated.x * (testpoint.x - v(n-1).x) +
rotated.y * (testpoint.y - v(n-1).y);
现在测试矩形 A 的所有点对应矩形 B 的边,反之亦然。如果你发现一个分离的边缘物体不相交(提供所有其他点在 B 的另一边的边缘正在测试-见下图)。如果没有发现任何分隔边,则表示矩形相交或一个矩形包含在另一个矩形中。
该测试适用于任何凸多边形。
修正: 为了识别一个分离边,仅仅测试一个矩形的所有点对另一个矩形的每一个边是不够的。候选边 E (下图)可以被看作是一个分离边,因为 A 中的所有点都在 E 的同一个半平面上。然而,它不是一个分离边,因为 B 中的顶点 Vb1和 Vb2也在那个半平面上。如果不是这样的话,它只会是一个分离的边缘
Http://www.iassess.com/collision.png
角斗士的回答是正确的,我更喜欢它。
分离轴测试 是检测矩形重叠最简单和标准的方法。投影间隔不重叠的线称为 分离轴。尼尔斯•皮彭布林克(Nils Pipenbrinck)的解法过于笼统。它使用 点积检查一个形状是否完全在另一个边缘的一边。这个解实际上可以归结为 n 边凸多边形。但是,它不适用于两个矩形。
M _ pGladiator 答案的临界点是我们应该检查两个矩形在两个轴(x 和 y)上的投影。如果两个投影是重叠的,那么我们可以说这两个矩形是重叠的。所以上面对 m _ pGladiator 的回答的评论是错误的。
对于简单的情况,如果两个矩形不旋转,
我们用结构表示一个矩形:
struct Rect {
x, // the center in x axis
y, // the center in y axis
width,
height
}
我们命名矩形 A,B 和 rectA,rectB。
if Math.abs(rectA.x - rectB.x) < (Math.abs(rectA.width + rectB.width) / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(rectA.height + rectB.height) / 2))
then
// A and B collide
end if
如果两个矩形中的任何一个被旋转,
它可能需要一些努力来确定它们在 x 轴和 y 轴上的投影。如下定义 struct RoatedRect:
struct RotatedRect : Rect {
double angle; // the rotating angle oriented to its center
}
if Math.abs(rectA.x - rectB.x) < (Math.abs(widthA' + widthB') / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(heightA' + heightB') / 2))
then
// A and B collide
end if
function olap_flag = ol(A,B,sub)
%A and B should be 4 x 2 matrices containing the xy coordinates of the corners in clockwise order
if nargin == 2
olap_flag = ol(A,B,1) && ol(B,A,1);
return;
end
urdl = diff(A([1:4 1],:));
s = sum(urdl .* A, 2);
sdiff = B * urdl' - repmat(s,[1 4]);
olap_flag = ~any(max(sdiff)<0);
关于 分离轴试验分离轴试验的 接受的答案非常有启发性,但是我仍然觉得应用它并不是微不足道的。我将分享我认为的伪代码,“优化”第一与 边界圆测试边界圆测试边界圆测试(见 另一个答案) ,以防它可能帮助其他人。我考虑了两个大小相同的矩形 A 和 B (但是考虑一般情况很简单)。
1边界圆测试:
function isRectangleACollidingWithRectangleB:
if d > 2 * R:
return False
...
计算速度比分离轴测试快得多。您只需要在两个圆圈碰撞的情况下考虑分离轴测试。
2分离轴试验
主要思想是:
考虑一个矩形,沿着它的顶点 V (i)循环。
计算矢量 Si + 1: V (i + 1)-V (i)。
用 Si + 1: Ni = (- Si + 1计算矢量 Ni。Y,Si + 1.X).这个矢量是图像中的蓝色。从 V (i)到其他顶点和 Ni 的向量之间的点积的符号将定义分离轴(品红虚线)。
计算矢量 Si-1: V (i-1)-V (i)。Si-1和 Ni 之间的点积符号将确定第一个矩形相对于分离轴的位置。在图片的例子中,它们走向不同的方向,所以符号是负的。
循环所有顶点 j 的第二个平方和计算向量 Sij = V (j)-V (i)。
如果对于任何点 V (j) ,矢量 Sij 与 Ni 的点积的符号与矢量 Si-1与 Ni 的点积的符号相同,这意味着两个点 V (i)和 V (j)都在品红虚线的同一侧,因此,顶点 V (i)没有分离轴。所以我们可以跳过顶点 V (i) ,重复下一个顶点 V (i + 1)。但首先我们更新了 Si-1 =-Si + 1。当我们到达最后一个顶点(i = 4)时,如果我们没有找到一个分离轴,我们重复另一个矩形。如果我们仍然没有找到一个分离轴,这意味着没有分离轴,两个矩形碰撞。
如果对于一个给定的顶点 V (i)和所有顶点 V (j) ,矢量 Sij 与 Ni 的点积的符号与矢量 Si-1与 Ni 的点积的符号不同(如图中所示) ,这意味着我们发现了分离轴和矩形不发生碰撞。
在伪代码中:
function isRectangleACollidingWithRectangleB:
...
#Consider first rectangle A:
Si-1 = Vertex_A[4] - Vertex_A[1]
for i in Vertex_A:
Si+1 = Vertex_A[i+1] - Vertex_A[i]
Ni = [- Si+1.y, Si+1.x ]
sgn_i = sign( dot_product(Si-1, Ni) ) #sgn_i is the sign of rectangle A with respect the separating axis
for j in Vertex_B:
sij = Vertex_B[j] - Vertex_A[i]
sgn_j = sign( dot_product(sij, Ni) ) #sgnj is the sign of vertex j of square B with respect the separating axis
if sgn_i * sgn_j > 0: #i.e., we have the same sign
break #Vertex i does not define separating axis
else:
if j == 4: #we have reached the last vertex so vertex i defines the separating axis
return False
Si-1 = - Si+1
#Repeat for rectangle B
...
#If we do not find any separating axis
return True