判断两个旋转矩形是否有重叠(碰撞)

1,846 阅读3分钟

先构建两个矩形:

aOrigin = np.array([
    [2, 1],
    [5, 1],
    [5, 3],
    [2, 3],
])
bOrigin = np.array([
    [2, 3],
    [4, 3],
    [4, 4],
    [2, 4],
])

# 中心点位置
aPosOrigin = np.array([3.5, 2])
bPosOrigin = np.array([3, 3.5])

# x,y一半的长度
ahOrigin = np.array([1.5, 1])
bhOrigin = np.array([1, 0.5])

image.png

加入旋转:

rotateA = np.array([
    [math.cos(math.pi / 180 * 40), math.sin(math.pi / 180 * 40) * -1],
    [math.sin(math.pi / 180 * 40), math.cos(math.pi / 180 * 40)]
])

rotateB = np.array([
    [math.cos(math.pi / 180 * 50), math.sin(math.pi / 180 * 50) * -1],
    [math.sin(math.pi / 180 * 50), math.cos(math.pi / 180 * 50)]
])

iRotateA = np.linalg.inv(rotateA) # A的逆矩阵,后续有用

a = np.array([rotateA.dot(item) for item in aOrigin])
b = np.array([rotateB.dot(item) for item in bOrigin])
aPos = rotateA.dot(aPosOrigin)
bPos = rotateB.dot(bPosOrigin)

image.png

此刻我们得到了两个旋转过的矩形,下面来判断它两是否有重叠部分

首先我们将坐标系变换为以a矩形的旋转为基准的坐标系,这样做可以让a矩形变为横平竖直方便后续计算

aBase = np.array([iRotateA.dot(item) for item in a])
iAPos = iRotateA.dot(aPos)

image.png

接下里将b矩形也变换到a矩形的坐标系中

bBase = np.array([iRotateA.dot(item) for item in b])
iBPos = iRotateA.dot(bPos)

image.png

此时让两个矩形的中心点向量相减就可以得到中心点之间的x,y轴距离

posTotal = abs(iBPos - iAPos) # array([1.15334536, 1.96777167])

中心点x轴相差1.15334536,y轴差1.96777167

在建立矩形时我们已知了横屏竖直的a矩形的x,y一半的长度为变量ahOrigin 如果我们计算出此时b矩形的最大x,y一半的长度(下图绿色线段,假设为bh)

image.png

posTotal - bh - ahOrigin得到的向量如果x,y都为负数则两个矩形是有重叠的

遍历b矩形四个点即可求出bh,这里我们介绍一种无需遍历的方法:

我们已知横平竖直的b矩形的最大x,y的一半为bhOrigin,b矩形一共经历了两次旋转变换rotateBiRotateA

先合并两次变换:

rotateC = iRotateA.dot(rotateB)

将rotateC取绝对值,再点乘bhOrigin即可得到bh

bh = np.array([ abs(item) for item in rotateC ]).dot(bhOrigin)

posTotal - bh - ahOrigin # 结果:array([-1.41828648,  0.30171961]) x,y不全为负,所以不碰撞

下面介绍下bh的求法原理(个人理解,有不对请评论指出):

由矩阵乘法公式我们可以得到rotateC和一个点的点积结果的x轴值为(y轴同理):

cos * x - sin * y

cos和sin在坐标系四个象限的正负分别是

[+, +]
[-, +]
[-, -]
[+, -]

我们现在以b矩形的中心为[0, 0]点

image.png

f向量为bhOrigin

假设现在旋转角度为30度(第一象限),那么cos和sin都为正,矩形四个点的坐标为:

cos * -1 - sin * 0.5
cos * 1 - sin * 0.5
cos * 1 + sin * 0.5
cos * -1 + sin * 0.5

最大的x轴值为 cos * 1 + sin * 0.5

假设现在旋转角度为100度(第二象限),那么cos是负,sin是正,矩形四个点的坐标为:

cos * 1 - sin * 0.5
-cos * 1 - sin * 0.5
-cos * 1 + sin * 0.5
cos * 1 + sin * 0.5

最大的x轴值还是 cos * 1 + sin * 0.5

以此类推会发现无论旋转角度在哪个象限最大的x轴值都是 cos * 1 + sin * 0.5

所以仅需将变换矩阵每一项取绝对值然后点乘bhOrigin,即可得到想要的bh

参考资料: box2d-lite Box2D-Lite源码阅读笔记