rigid.rs是一个表示三维刚体变换(Rigid Transformation)的结构体定义,用于在计算机图形学、机器人学以及物理模拟等领域中表示物体在三维空间中的旋转和平移。在这个定义中,所有长度在变换后都保持不变,这是刚体变换的一个基本特性。
一、rigid.rs源码
//! All matrix multiplication in this module is in row-vector notation,
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
//! before `T2` you use `T1 * T2`
use crate::approxeq::ApproxEq;
use crate::trig::Trig;
use crate::{Rotation3D, Transform3D, UnknownUnit, Vector3D};
use core::{fmt, hash};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
use num_traits::real::Real;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
pub rotation: Rotation3D<T, Src, Dst>,
pub translation: Vector3D<T, Dst>,
}
impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
/// Construct a new rigid transformation, where the `rotation` applies first
#[inline]
pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
Self {
rotation,
translation,
}
}
}
impl<T: Copy, Src, Dst> RigidTransform3D<T, Src, Dst> {
pub fn cast_unit<Src2, Dst2>(&self) -> RigidTransform3D<T, Src2, Dst2> {
RigidTransform3D {
rotation: self.rotation.cast_unit(),
translation: self.translation.cast_unit(),
}
}
}
impl<T: Real + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
/// Construct an identity transform
#[inline]
pub fn identity() -> Self {
Self {
rotation: Rotation3D::identity(),
translation: Vector3D::zero(),
}
}
/// Construct a new rigid transformation, where the `translation` applies first
#[inline]
pub fn new_from_reversed(
translation: Vector3D<T, Src>,
rotation: Rotation3D<T, Src, Dst>,
) -> Self {
// T * R
// = (R * R^-1) * T * R
// = R * (R^-1 * T * R)
// = R * T'
//
// T' = (R^-1 * T * R) is also a translation matrix
// It is equivalent to the translation matrix obtained by rotating the
// translation by R
let translation = rotation.transform_vector3d(translation);
Self {
rotation,
translation,
}
}
#[inline]
pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
Self {
rotation,
translation: Vector3D::zero(),
}
}
#[inline]
pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
Self {
translation,
rotation: Rotation3D::identity(),
}
}
/// Decompose this into a translation and an rotation to be applied in the opposite order
///
/// i.e., the translation is applied _first_
#[inline]
pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
// self = R * T
// = R * T * (R^-1 * R)
// = (R * T * R^-1) * R)
// = T' * R
//
// T' = (R^ * T * R^-1) is T rotated by R^-1
let translation = self.rotation.inverse().transform_vector3d(self.translation);
(translation, self.rotation)
}
/// Returns the multiplication of the two transforms such that
/// other's transformation applies after self's transformation.
///
/// i.e., this produces `self * other` in row-vector notation
#[inline]
pub fn then<Dst2>(
&self,
other: &RigidTransform3D<T, Dst, Dst2>,
) -> RigidTransform3D<T, Src, Dst2> {
// self = R1 * T1
// other = R2 * T2
// result = R1 * T1 * R2 * T2
// = R1 * (R2 * R2^-1) * T1 * R2 * T2
// = (R1 * R2) * (R2^-1 * T1 * R2) * T2
// = R' * T' * T2
// = R' * T''
//
// (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
// R1 * R2 = R'
// T' * T2 = T'' = vector addition of translations T2 and T'
let t_prime = other.rotation.transform_vector3d(self.translation);
let r_prime = self.rotation.then(&other.rotation);
let t_prime2 = t_prime + other.translation;
RigidTransform3D {
rotation: r_prime,
translation: t_prime2,
}
}
/// Inverts the transformation
#[inline]
pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
// result = (self)^-1
// = (R * T)^-1
// = T^-1 * R^-1
// = (R^-1 * R) * T^-1 * R^-1
// = R^-1 * (R * T^-1 * R^-1)
// = R' * T'
//
// T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
// R' = R^-1
//
// An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1
RigidTransform3D::new_from_reversed(-self.translation, self.rotation.inverse())
}
pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
where
T: Trig,
{
self.rotation
.to_transform()
.then(&self.translation.to_transform())
}
/// Drop the units, preserving only the numeric value.
#[inline]
pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
RigidTransform3D {
rotation: self.rotation.to_untyped(),
translation: self.translation.to_untyped(),
}
}
/// Tag a unitless value with units.
#[inline]
pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
RigidTransform3D {
rotation: Rotation3D::from_untyped(&transform.rotation),
translation: Vector3D::from_untyped(transform.translation),
}
}
}
impl<T: fmt::Debug, Src, Dst> fmt::Debug for RigidTransform3D<T, Src, Dst> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RigidTransform3D")
.field("rotation", &self.rotation)
.field("translation", &self.translation)
.finish()
}
}
impl<T: PartialEq, Src, Dst> PartialEq for RigidTransform3D<T, Src, Dst> {
fn eq(&self, other: &Self) -> bool {
self.rotation == other.rotation && self.translation == other.translation
}
}
impl<T: Eq, Src, Dst> Eq for RigidTransform3D<T, Src, Dst> {}
impl<T: hash::Hash, Src, Dst> hash::Hash for RigidTransform3D<T, Src, Dst> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.rotation.hash(state);
self.translation.hash(state);
}
}
impl<T: Copy, Src, Dst> Copy for RigidTransform3D<T, Src, Dst> {}
impl<T: Clone, Src, Dst> Clone for RigidTransform3D<T, Src, Dst> {
fn clone(&self) -> Self {
RigidTransform3D {
rotation: self.rotation.clone(),
translation: self.translation.clone(),
}
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for RigidTransform3D<T, Src, Dst>
where
T: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(RigidTransform3D {
rotation: arbitrary::Arbitrary::arbitrary(u)?,
translation: arbitrary::Arbitrary::arbitrary(u)?,
})
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Zeroable, Src, Dst> Zeroable for RigidTransform3D<T, Src, Dst> {}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for RigidTransform3D<T, Src, Dst> {}
impl<T: Real + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
for RigidTransform3D<T, Src, Dst>
{
fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
Self::from_rotation(rot)
}
}
impl<T: Real + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>> for RigidTransform3D<T, Src, Dst> {
fn from(t: Vector3D<T, Dst>) -> Self {
Self::from_translation(t)
}
}
#[cfg(test)]
mod test {
use super::RigidTransform3D;
use crate::default::{Rotation3D, Transform3D, Vector3D};
#[test]
fn test_rigid_construction() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
assert!(rigid
.to_transform()
.approx_eq(&rotation.to_transform().then(&translation.to_transform())));
let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
assert!(rigid
.to_transform()
.approx_eq(&translation.to_transform().then(&rotation.to_transform())));
}
#[test]
fn test_rigid_decomposition() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
let (t2, r2) = rigid.decompose_reversed();
assert!(rigid
.to_transform()
.approx_eq(&t2.to_transform().then(&r2.to_transform())));
}
#[test]
fn test_rigid_inverse() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let rigid = RigidTransform3D::new(rotation, translation);
let inverse = rigid.inverse();
assert!(rigid
.then(&inverse)
.to_transform()
.approx_eq(&Transform3D::identity()));
assert!(inverse
.to_transform()
.approx_eq(&rigid.to_transform().inverse().unwrap()));
}
#[test]
fn test_rigid_multiply() {
let translation = Vector3D::new(12.1, 17.8, -5.5);
let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
let translation2 = Vector3D::new(9.3, -3.9, 1.1);
let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
let rigid = RigidTransform3D::new(rotation, translation);
let rigid2 = RigidTransform3D::new(rotation2, translation2);
assert!(rigid
.then(&rigid2)
.to_transform()
.approx_eq(&rigid.to_transform().then(&rigid2.to_transform())));
assert!(rigid2
.then(&rigid)
.to_transform()
.approx_eq(&rigid2.to_transform().then(&rigid.to_transform())));
}
}
二、结构体字段说明:
- rotation: 这是一个Rotation3D类型的字段,表示旋转变换。Rotation3D是另一个结构体,用于表示三维空间中的旋转。Src和Dst是泛型参数,分别代表旋转前后的坐标系。
- translation: 这是一个Vector3D类型的字段,表示平移变换。Vector3D是一个表示三维向量的结构体,Dst表示平移后的坐标系。
- 变换顺序:在这个结构体中,旋转是先于平移应用的,即先进行旋转(Rotation * Translation),然后进行平移。这里的乘法表示的是变换的组合,但在行向量表示法中,通常理解为矩阵乘法,其中旋转矩阵乘以平移向量(实际上是通过扩展平移向量为齐次坐标形式后进行矩阵乘法)。
三、方法
- 构造方法 new:
- 这个方法用于创建一个新的 RigidTransform3D 实例。
- 它接受一个旋转 Rotation3D<T, Src, Dst> 和一个平移 Vector3D<T, Dst> 作为参数。
- 这里的 T 表示数值类型,Src 和 Dst 分别表示旋转和平移的单位类型。
- cast_unit 方法:
- 这个方法用于将 RigidTransform3D 实例的单位从一种类型转换为另一种类型。
- 它要求数值类型 T 实现 Copy trait,以便可以复制数值。
- Src2 和 Dst2 是新的单位类型。
- identity 方法:
- 用于构造一个表示恒等变换的 RigidTransform3D 实例。
- 恒等变换意味着物体不会发生任何旋转或平移。
+这个方法要求数值类型 T 实现 Real 和 ApproxEq traits。
- to_transform 方法:
- 这个方法将 RigidTransform3D 转换为一个更一般的 Transform3D 实例。
- 它要求数值类型 T 实现 Trig trait,以便可以进行三角函数计算。
- 方法内部,首先将旋转转换为变换,然后将平移转换为变换,并将这两个变换组合起来。
- to_untyped 和 from_untyped 方法:
- to_untyped 方法用于去除单位,只保留数值值,返回一个单位类型为 UnknownUnit 的 RigidTransform3D 实例。
- from_untyped 方法则相反,它接受一个单位类型为 UnknownUnit 的 RigidTransform3D 实例,并为其添加单位,返回一个新的 RigidTransform3D 实例。
- fmt::Debug实现:
- 为RigidTransform3D实现了Debug trait,允许使用{:?}格式化字符串来打印该类型的实例。
- 在实现中,通过f.debug_struct(“RigidTransform3D”)创建一个调试结构体,并添加rotation和translation两个字段,最后调用.finish()完成结构体的构建。
- PartialEq和Eq实现:
- 为RigidTransform3D实现了PartialEq trait,允许比较两个实例是否相等。
- 通过比较rotation和translation两个成员是否相等来确定两个RigidTransform3D实例是否相等。
- 实现了Eq trait,这通常是在实现PartialEq之后自动实现的,表示RigidTransform3D实例之间的相等性是一致和反射的。
- hash::Hash实现:
- 为RigidTransform3D实现了Hash trait,允许该类型的实例被哈希。
- 实现中,通过调用self.rotation.hash(state)和self.translation.hash(state)将rotation和translation两个成员的值合并到哈希状态中。
- Copy和Clone实现:
- 如果T实现了Copy trait,则RigidTransform3D也实现了Copy trait,允许通过复制位模式来复制实例。
- 如果T实现了Clone trait,则RigidTransform3D也实现了Clone trait,允许通过调用clone方法来创建实例的深拷贝。
- arbitrary::Arbitrary实现(依赖proptest库):
- 为RigidTransform3D实现了Arbitrary trait,允许在属性测试中自动生成实例。
- 通过调用arbitrary::Arbitrary::arbitrary(u)?分别为rotation和translation生成任意值。
- Zeroable和Pod实现(依赖bytemuck库):
- 如果T实现了Zeroable trait,则RigidTransform3D也实现了Zeroable trait,允许将实例的内存区域清零。
- 如果T实现了Pod trait(Plain Old Data,简单旧数据),且Src和Dst具有静态生命周期,则RigidTransform3D也实现了Pod trait,表示实例的内存布局是平凡的,可以安全地复制和比较内存块。
- 从Rotation3D和Vector3D的类型转换:
- 如果T实现了Real trait(通常表示实数类型,如浮点数)和ApproxEq trait(允许近似相等比较),则可以从Rotation3D<T, Src, Dst>和Vector3D<T, Dst>类型转换为RigidTransform3D<T, Src, Dst>。
- 提供了from_rotation和from_translation的便捷方法(虽然在这段代码中没有直接定义这些方法,但从上下文中可以推断出这一点)。