之所以会写这个东西,是因为我在半个月前coding的时候遇到了我难以解决的问题,直接要我复习了高中的数学知识,也记录下这难忘的一刻。
1 点绕向量旋转的分类
看来点绕向量旋转一般分为两类,第一是点在坐标原点绕x y z轴旋转,第二种是点绕任意向量旋转。 这两种的难度依次增加,下面我会介绍这两种情况的具体解法。下面我都会右手坐标作为讲解。
2 点绕坐标轴旋转
其实绕着x轴旋转,就是把视野中的坐标系降维到二维YOZ,然后绕着原点旋转相应的角度。所以由此得出以下的公式。
2.1 点绕x轴旋转a角
C++代码实现:
void codeRotateByX(double y, double z, double thetax, double& outy, double& outz)
{
double y1 = y;//将变量拷贝一次,保证&y == &y这种情况下也能计算正确
double z1 = z;
double rx = thetax * CV_PI / 180;
outy = cos(rx) * y1 - sin(rx) * z1;
outz = cos(rx) * z1 + sin(rx) * y1;
}
2.2点绕y轴旋转β角
C++代码实现:
void codeRotateByY(double x, double z, double thetay, double& outx, double& outz)
{
double x1 = x;
double z1 = z;
double ry = thetay * CV_PI / 180;
outx = cos(ry) * x1 + sin(ry) * z1;
outz = cos(ry) * z1 - sin(ry) * x1;
}
2.3点绕z轴旋转γ角
C++代码实现:
void codeRotateByZ(double x, double y, double thetaz, double& outx, double& outy)
{
double x1 = x;//将变量拷贝一次,保证&x == &outx这种情况下也能计算正确
double y1 = y;
double rz = thetaz * CV_PI / 180;
outx = cos(rz) * x1 - sin(rz) * y1;
outy = sin(rz) * x1 + cos(rz) * y1;
}
3 点绕任意向量旋转
为了方便使用右手坐标系,v1,v2为旋转轴,θ表示旋转角度。
第一步 :将v1v2平移到原点,对应矩阵:
步骤2是一个旋转操作,将p(p = v2 -v1)旋转至XOZ平面,步骤3也是一个旋转操作,将p旋转至与Z轴重合,这两个操作对应的图如下。
做点p在平面YOZ上的投影点q。再过q做Z轴垂线,则r是p绕X轴旋转所得,且旋转角度为α,且
,
于是旋转矩阵为
现在将r绕Y轴旋转至与Z轴重合,旋转的角度为-beta(方向为顺时针),且
,
于是得到旋转矩阵为
最后是绕Z轴旋转,对应的矩阵如下
然后把中间五个矩阵连乘起来,再转置一下,得到下面的绕任意轴旋转的矩阵
即
而在我coding的情景是已经将v1v2组成的旋转轴简化成旋转轴v,我只要把旋转轴单位化后,单位向量是只有方向意义,所以可以认为它在坐标原点。 点p为需要旋转的点(一个double类型,长度为3的数组),origin原来的坐标原点,z_axis为旋转轴,alpha为旋转角度。
int Pdgm_trans_point(ads_point p, ads_point origin, ads_point z_axis, ads_real alpha)
{
double a0 = origin[X];
double b0 = origin[Y];
double c0 = origin[Z];
double x = p[X];
double y = p[Y];
double z = p[Z];
P3Normalize(z_axis);//向量单位化
double u = z_axis[X];
double v = z_axis[Y];
double w = z_axis[Z]; //方向向量(u,v,w)需为单位向量!!!
ads_point xyz;
double SinA = sin(alpha * GM_PI / 180);
double CosA = cos(alpha * GM_PI / 180);
double uu = u * u;
double vv = v * v;
double ww = w * w;
double uv = u * v;
double uw = u * w;
double vw = v * w;
float t00 = uu + (vv + ww) * CosA;
float t10 = uv * (1 - CosA) + w * SinA;
float t20 = uw * (1 - CosA) - v * SinA;
float t30 = 0;
float t01 = uv * (1 - CosA) - w * SinA;
float t11 = vv + (uu + ww) * CosA;
float t21 = vw * (1 - CosA) + u * SinA;
float t31 = 0;
float t02 = uw * (1 - CosA) + v * SinA;
float t12 = vw * (1 - CosA) - u * SinA;
float t22 = ww + (uu + vv) * CosA;
float t32 = 0;
float t03 = (a0 * (vv + ww) - u * (b0 * v + c0 * w)) * (1 - CosA) + (b0 * w - c0 * v) * SinA;
float t13 = (b0 * (uu + ww) - v * (a0 * u + c0 * w)) * (1 - CosA) + (c0 * u - a0 * w) * SinA;
float t23 = (c0 * (uu + vv) - w * (a0 * u + b0 * v)) * (1 - CosA) + (a0 * v - b0 * u) * SinA;
float t33 = 1;
p[0] = t00 * x + t01 * y + t02 * z + t03;
p[1] = t10 * x + t11 * y + t12 * z + t13;
p[2] = t20 * x + t21 * y + t22 * z + t23;
return GM_OK;
}
后记
嘶,感觉这东西挺麻烦的,搞了我差不多两个星期(单纯的工作时间比较少)。其实解决这个问题还可以调库,想CAD的,OSG的,OpenGL的这些都有相对应的旋转库,再按照上面的步骤就可以实现了。 ps:数学底子不好,有写错的地方多多包涵。