2D 旋转,3D 旋转和矩阵变换简要记录
[編輯] [转简体] (简体译文)概要
正文
2D 旋转推导
只要你学过一点三角函数,2D 旋转一定难不倒你,接下来我将推导 2D 旋转公式。
如图,如果我们知道一点 P(x, y),那么我们就可以求得它与原点的距离 OP:
r = √(x^2 + y^2)
那么 OP 与 x 轴的夹角即为
α = asin(y / r)
据此,如果我们要将 P 点逆时针旋转 β,只需要根据以下公式就可以得到新的坐标:
x' = r * (cos(α + β))
y' = r * (sin(α + β))
在 C++ 中可以表示为:
struct dPOINT { double x; double y; }; dPOINT rotate2D(dPOINT p, double radian) { double r = sqrt(p.x * p.x + p.y * p.y); double _radian = asin(p.y / r); return dPOINT{ r * (cos(radian + _radian)),r * (sin(radian + _radian)) }; }
此外,还可以将其推导为矩阵形式。如果我们把点 P 看作是一个向量,那么他就是 x 倍 x 轴方向的单位向量,和 y 倍 y 轴方向的单位向量复合产生的向量。
在这种情况下,旋转点 P 相当于将 x 轴和 y 轴的单位向量进行了旋转,我们暂称旋转后的两个单位向量为 <x> 和 <y>。
用这两个旋转后的单位向量再次以原先比例复合,得到的也就是旋转后的点 P。
而计算 P 点在 xOy 下的坐标,就只需要将 <x> 和 <y> 在 xOy 上的分量找出,也就是把 <x> 和 <y> 投影到 x 轴和 y 轴上。投影后
要得到新的 P 点坐标,只需要将其和此矩阵相乘:
x' = x * cos(β) - y * sin(β)
y' = x * sin(β) + y * cos(β)
如果你看不懂矩阵,其实这也就是向量,<x> 和 <y> 可以写为坐标:
<x> = ( cos(β), sin(β) )
<y> = ( -sin(β), cos(β) )
旋转后的点 P 可表示为
P = x * <x> + y * <y> = x * ( cos(β), sin(β) ) + y * ( -sin(β), cos(β) ) = ( x * cos(β) - y * sin(β), x * sin(β) + y * cos(β) )
即可求得 P 点坐标。
这个算法的好处就在于:不需要计算 OP 和点 P 与 x 轴的夹角。
在 C++ 中:
struct dPOINT { double x; double y; }; dPOINT rotate2D_2(dPOINT p, double radian) { return dPOINT{ p.x * cos(radian) - p.y * sin(radian),p.x * sin(radian) + p.y * cos(radian) }; }
3D 旋转
3D 旋转其实是绕 x, y, z 轴的旋转。首先需要模仿上面的 2D 旋转矩阵写出 3D 的绕一个轴的旋转矩阵,然后再将三个轴的旋转复合。注意,三个轴的旋转是有先后次序的,不同的旋转次序会产生不同的结果。通常采用 z - y - x 的旋转顺序。
最终的复合矩阵如下:
在 C++ 中:
struct dCPOINT3D { double x, y, z; COLORREF c; }; dCPOINT3D rotate3D(dCPOINT3D p, double Z, double Y, double X) { return { p.x * (cos(Y) * cos(Z)) + p.y * (-(cos(X) * sin(Z)) + (sin(X) * sin(Y) * cos(Z))) + p.z * ((sin(X) * sin(Z)) + (cos(X) * sin(Y) * cos(Z))), p.x * (cos(Y) * sin(Z)) + p.y * ((cos(X) * cos(Z)) + (sin(X) * sin(Y) * sin(Z))) + p.z * ((-sin(X) * cos(Z)) + (cos(X) * sin(Y) * sin(Z))), p.x * (-sin(Y)) + p.y * (sin(X) * cos(Y)) + p.z * (cos(X) * cos(Y)), p.c }; }