在几何变换中,开发者经常要面对的一种情况:已知一个点A的坐标(x, y, z),该点基于一个旋转轴[轴向量为(a, b, c),基点O坐标为(x0, y0, z0)]旋转一个角度θ,需要知道旋转后A点的坐标。计算A点的新坐标,主要用到罗德里格斯旋转公式,下面对该公式进行介绍及推导以加深理解。
罗德里格斯旋转公式:任意三维向量v,已知旋转轴k是单位向量,旋转角为θ,按照右手法则(即逆时针)旋转,则旋转后的向量vrot为:
vrot=vcosθ+(1−cosθ)(k⋅v)k+k×vsinθ
旋转公式推导过程:向量v可以分解为平行于k的分量v∥和正交于k的分量v⊥(如图1),即:v=v∥+v⊥,其中
v∥=(v⋅k)k (1)
v⊥=v−v∥=v−(k⋅v)k=−k×(k×v) (2)
从图1还可以看出,v的平行分量v∥不会因为旋转而改变,旋转后的向量vrot的平行分量依然等于v∥,即v∥rot=v∥。而v的正交分量v⊥在旋转过程中大小不变,方向会发生变化,即:
|v⊥rot|=|v⊥| (3)
v⊥rot=v⊥cosθ+k×v⊥sinθ=v⊥cosθ+k×vsinθ (4)
则vrot=v∥rot+v⊥rot
=v∥+v⊥cosθ+k×vsinθ
=v∥+(v−v∥)cosθ+k×vsinθ
=vcosθ+(1−cosθ)v∥+k×vsinθ
=vcosθ+(1−cosθ)(k⋅v)k+k×vsinθ
罗德里格斯旋转公式注意事项:
1. 罗德里格斯旋转公式旋转对象为向量,对于点A需要先相对于基点O进行相关转换;
2. 旋转角存在方向,当前为逆时针,在实际使用过程中需要特别注意。
python实现:
from ansa import calcfrom ansa import baseimport mathclass Vector(object):@staticmethoddef point2Point(cls, point1:List[float], point2:List[float], unit:bool=True)->List[float]:'''计算两点构成的向量. 向量由point1指向point2,即point2-point1'''if isinstance(point1, base.Entity): point1 = point1.positionif isinstance(point2, base.Entity): point2 = point2.positionvect:List[float] = [p2-p1 for p1, p2 in zip(point1, point2)]return cls.unit(vect) if unit else vect@staticmethoddef move(point:Union[base.Entity, Iterable[float]], vector:Iterable[float], distance:float=1.0)->Tuple[float]:'''将点point沿向量vector平移distance距离得到新的坐标点'''if isinstance(point, base.Entity): point = point.positionreturn [p+distance*v for p, v in zip(point, vector)]@staticmethoddef unit(vector:List[float])->List[float]:'''将一个向量单位化'''return calc.Normalize(vector)@class methoddef rotate(cls, vector:Iterable[float], axis:Iterable[float], theta:float)->Tuple[float]:'''计算将向量vector绕旋转轴axis旋转theta角后的向量'''thetaRadian:float = math.radians(theta)cosTheta:float = math.cos(thetaRadian)sinTheta:float = math.sin(thetaRadian)unitAxis:List[float] = cls.unit(axis)dot:float = calc.DotProduct(unitAxis, vector)cross:List[float] = calc.CrossProduct(unitAxis, vector)return[(v1*cosTheta+v2*sinTheta+v3*dot*(1-cosTheta)) for v1, v2, v3 in zip(vector, cross, unitAxis)]def main():initPoint:List[float] = [1, 1, 1] # 旋转轴基点axis:List[float] = [2, 3, 4] # 旋转轴向量pointA:List[float] = [10, 5, 3] # 待旋转点坐标vector = Vector.point2Point(initPoint, pointA, unit=False) #rotateVect = Vector.rotate(vector, axis, 50) # 旋转50度newPointA = Vector.move(pointA, rotateVect)if __name__ == '__main__':main()
未经作者同意,不得转载该文!!!