use std::ops::Mul;
use nalgebra::Rotation3;
use super::exts::Vec3Ext;
use super::{
ArticulatedBodyInertia, Mat3, SpatialForceVector, SpatialMotionVector, UnitQuat, UnitVec3, Vec3,
};
use crate::exts::MatrixExt;
use crate::{Real, SMatrix, VEC3_ZERO, unit_quat};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PlukerRotation(Rotation3<Real>);
impl PlukerRotation {
#[inline]
pub fn from_axis_angle(axis: UnitVec3, angle: Real) -> Self {
Self(Rotation3::from_axis_angle(&axis, angle).inverse())
}
#[inline]
pub fn from_quat(quat: UnitQuat) -> Self {
Self(Rotation3::from(quat))
}
#[inline]
pub fn into_quat(self) -> UnitQuat {
self.0.into()
}
#[inline]
pub fn into_matrix(self) -> Mat3 {
self.0.into_inner()
}
#[inline]
pub fn identity() -> Self {
Self(Rotation3::identity())
}
#[inline]
#[must_use]
pub fn transpose(&self) -> Self {
Self(self.0.transpose())
}
#[inline]
pub fn matrix(&self) -> Mat3 {
self.0.into_inner()
}
#[inline]
pub fn inverse_transform_vector(&self, v: Vec3) -> Vec3 {
self.0.inverse_transform_vector(&v)
}
#[inline]
pub fn inverse_transform_unitvec(&self, v: UnitVec3) -> UnitVec3 {
self.0.inverse_transform_unit_vector(&v)
}
#[inline]
pub fn axis_angle(&self) -> Option<(UnitVec3, Real)> {
self.0.axis_angle().map(|(axis, angle)| (axis, -angle))
}
#[inline]
pub fn from_matrix_unchecked(matrix: Mat3) -> Self {
Self(Rotation3::from_matrix_unchecked(matrix))
}
#[inline]
pub fn as_slice(&self) -> &[Real] {
self.0.matrix().as_slice()
}
}
impl Mul<Vec3> for PlukerRotation {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Vec3 {
self.0 * rhs
}
}
impl Mul<Self> for PlukerRotation {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self(self.0 * rhs.0)
}
}
impl Mul<Mat3> for PlukerRotation {
type Output = Mat3;
#[inline]
fn mul(self, rhs: Mat3) -> Mat3 {
self.0 * rhs
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PluckerTransform {
pub rotation: PlukerRotation,
pub translation: Vec3,
}
impl Default for PluckerTransform {
#[inline]
fn default() -> Self {
Self {
rotation: PlukerRotation::identity(),
translation: Vec3::zeros(),
}
}
}
impl PluckerTransform {
#[inline]
pub fn identity() -> Self {
Self {
rotation: PlukerRotation::identity(),
translation: VEC3_ZERO,
}
}
#[inline]
pub fn from_pose(pose: Pose) -> Self {
Self {
rotation: PlukerRotation::from_quat(pose.rotation.inverse()),
translation: pose.translation,
}
}
#[inline]
pub fn any_nan(&self) -> bool {
self.rotation.matrix().any_nan() || self.translation.any_nan()
}
#[inline]
#[must_use]
pub fn mul_transform(&self, rhs: &PluckerTransform) -> PluckerTransform {
PluckerTransform {
rotation: self.rotation * rhs.rotation,
translation: rhs.translation + rhs.rotation.transpose() * self.translation,
}
}
#[inline]
#[must_use]
pub fn inverse(&self) -> PluckerTransform {
PluckerTransform {
rotation: self.rotation.transpose(),
translation: -(self.rotation * self.translation),
}
}
#[inline]
pub fn transform_motion_vec(&self, v: SpatialMotionVector) -> SpatialMotionVector {
let w = v.top;
let v = v.bottom;
let w1 = self.rotation * w;
let v1 = self.rotation * (v - self.translation.cross(&w));
SpatialMotionVector::from_pair(w1, v1)
}
#[inline]
pub fn inverse_transform_motion_vec(&self, v: SpatialMotionVector) -> SpatialMotionVector {
let w = v.top;
let v = v.bottom;
let e_transpose = self.rotation.transpose();
let w = e_transpose * w;
let v = e_transpose * v + self.translation.cross(&w);
SpatialMotionVector::from_pair(w, v)
}
#[inline]
pub fn transform_force_vec(&self, f: SpatialForceVector) -> SpatialForceVector {
let n = f.top;
let f = f.bottom;
let e = self.rotation;
let r = self.translation;
let n = e * (n - r.cross(&f));
let f = e * f;
SpatialForceVector::from_pair(n, f)
}
#[inline]
pub fn inverse_transform_force_vec(&self, f: SpatialForceVector) -> SpatialForceVector {
let n = f.top;
let f = f.bottom;
let e_transpose = self.rotation.transpose();
let f = e_transpose * f;
let n = e_transpose * n + self.translation.cross(&f);
SpatialForceVector::from_pair(n, f)
}
#[inline]
pub fn transform_point(&self, point: Vec3) -> Vec3 {
let v = point - self.translation;
self.rotation * v
}
#[inline]
pub fn transform_vec3(&self, vec3: Vec3) -> Vec3 {
self.rotation * vec3
}
#[inline]
pub fn inverse_transform_point(&self, point: Vec3) -> Vec3 {
let v = self.rotation.inverse_transform_vector(point);
v + self.translation
}
#[inline]
pub fn inverse_transform_vec3(&self, vec3: Vec3) -> Vec3 {
self.rotation.inverse_transform_vector(vec3)
}
#[inline]
pub fn inverse_transform_unitvec3(&self, vec3: UnitVec3) -> UnitVec3 {
self.rotation.inverse_transform_unitvec(vec3)
}
#[inline]
pub fn transform_articulated_inertia(
&self,
inertia: &ArticulatedBodyInertia,
) -> ArticulatedBodyInertia {
let rotation = self.rotation.matrix();
let rotation_t = rotation.transpose();
let m = inertia.mass.mat3();
let r = self.translation.into_cross_matrix();
let i = inertia.top_left.mat3();
let h = inertia.h;
let new_m = rotation * m * rotation_t;
let new_h = rotation * (h - r * m) * rotation_t;
let new_inertia = rotation * (i - r * h.transpose() + (h - r * m) * r) * rotation_t;
ArticulatedBodyInertia {
top_left: new_inertia.into(),
h: new_h,
mass: new_m.into(),
}
}
#[inline]
pub fn matrix(&self) -> SMatrix<6, 6> {
let mut result = SMatrix::<6, 6>::zeros();
let rotation = self.rotation.matrix();
let bottom_left = -(rotation * self.translation.into_cross_matrix());
result.fixed_view_mut::<3, 3>(0, 0).copy_from(&rotation);
result.fixed_view_mut::<3, 3>(3, 3).copy_from(&rotation);
result.fixed_view_mut::<3, 3>(3, 0).copy_from(&bottom_left);
result
}
#[inline]
pub fn dual_matrix(&self) -> SMatrix<6, 6> {
let mut result = SMatrix::<6, 6>::zeros();
let rotation = self.rotation.matrix();
result.fixed_view_mut::<3, 3>(0, 0).copy_from(&rotation);
result.fixed_view_mut::<3, 3>(3, 3).copy_from(&rotation);
let top_right = -rotation * self.translation.into_cross_matrix();
result.fixed_view_mut::<3, 3>(0, 3).copy_from(&top_right);
result
}
#[inline]
pub fn is_near_identity(&self, epsilon: Real) -> bool {
self.rotation.0.angle() < epsilon && self.translation.iter().all(|x| x.abs() < epsilon)
}
}
impl Mul<Self> for PluckerTransform {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.mul_transform(&rhs)
}
}
impl Mul<SpatialMotionVector> for PluckerTransform {
type Output = SpatialMotionVector;
#[inline]
fn mul(self, rhs: SpatialMotionVector) -> SpatialMotionVector {
self.transform_motion_vec(rhs)
}
}
impl Mul<SpatialForceVector> for PluckerTransform {
type Output = SpatialForceVector;
#[inline]
fn mul(self, rhs: SpatialForceVector) -> SpatialForceVector {
self.transform_force_vec(rhs)
}
}
impl Mul<&ArticulatedBodyInertia> for PluckerTransform {
type Output = ArticulatedBodyInertia;
#[inline]
fn mul(self, rhs: &ArticulatedBodyInertia) -> ArticulatedBodyInertia {
self.transform_articulated_inertia(rhs)
}
}
impl PluckerTransform {
#[inline]
pub fn pose(&self) -> Pose {
let quat: UnitQuat = self.rotation.0.into();
Pose {
rotation: quat.inverse(),
translation: self.translation,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Default, Clone, Copy)]
pub struct Pose {
pub rotation: UnitQuat,
pub translation: Vec3,
}
impl Pose {
#[inline]
pub fn transform_point(&self, local: Vec3) -> Vec3 {
self.rotation * local + self.translation
}
#[inline]
pub fn transform_vec(&self, local: Vec3) -> Vec3 {
self.rotation * local
}
#[inline]
#[must_use]
pub fn inverse(&self) -> Pose {
let r = self.rotation.inverse();
let t = -self.translation;
Pose {
rotation: r,
translation: r * t,
}
}
#[inline]
pub fn is_identity(&self, epsilon: Real) -> bool {
self.rotation.angle() < epsilon && self.translation.iter().all(|x| x.abs() < epsilon)
}
#[inline]
pub fn into_array(self) -> [Real; 7] {
let p = self.translation;
let r = self.rotation;
[p.x, p.y, p.z, r.i, r.j, r.k, r.w]
}
#[inline]
pub fn from_array(arr: [Real; 7]) -> Self {
Pose {
translation: Vec3::new(arr[0], arr[1], arr[2]),
rotation: unit_quat(arr[3], arr[4], arr[5], arr[6]),
}
}
#[inline]
pub fn from_slice(arr: &[Real]) -> Self {
assert_eq!(arr.len(), 7);
Pose {
translation: Vec3::new(arr[0], arr[1], arr[2]),
rotation: unit_quat(arr[3], arr[4], arr[5], arr[6]),
}
}
}
impl From<Pose> for PluckerTransform {
#[inline]
fn from(pose: Pose) -> Self {
PluckerTransform::from_pose(pose)
}
}
impl Mul<Pose> for Pose {
type Output = Pose;
#[inline]
fn mul(self, rhs: Pose) -> Pose {
let rotation = self.rotation * rhs.rotation;
let translation = self.rotation * rhs.translation + self.translation;
Pose {
rotation,
translation,
}
}
}
#[cfg(feature = "approx")]
mod approx_eq {
use crate::Real;
impl approx::AbsDiffEq for super::PlukerRotation {
type Epsilon = Real;
#[inline]
fn default_epsilon() -> Self::Epsilon {
Real::EPSILON
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.0.abs_diff_eq(&other.0, epsilon)
}
}
impl approx::RelativeEq for super::PlukerRotation {
#[inline]
fn default_max_relative() -> Self::Epsilon {
Real::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.0.relative_eq(&other.0, epsilon, max_relative)
}
}
impl approx::AbsDiffEq for super::PluckerTransform {
type Epsilon = Real;
#[inline]
fn default_epsilon() -> Self::Epsilon {
Real::EPSILON
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.rotation.abs_diff_eq(&other.rotation, epsilon)
& self.translation.abs_diff_eq(&other.translation, epsilon)
}
}
impl approx::RelativeEq for super::PluckerTransform {
#[inline]
fn default_max_relative() -> Self::Epsilon {
Real::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.rotation
.relative_eq(&other.rotation, epsilon, max_relative)
& self
.translation
.relative_eq(&other.translation, epsilon, max_relative)
}
}
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::PluckerTransform;
use crate::exts::Vec3Ext;
use crate::transform::PlukerRotation;
use crate::{
ArticulatedBodyInertia, Mat3, Real, RigidBodyInertia, SpatialMotionVector, SymmetricMat3,
UnitQuat, UnitVec3, Vec3, unit_vec3, vec3,
};
#[test]
fn test_transform_motion_vec() {
let transform = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
translation: vec3(1., 2., 3.),
};
let v0 = SpatialMotionVector::from_array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
let v1 = transform * v0;
assert_relative_eq!(v1.top, transform.rotation * v0.top);
assert_relative_eq!(
v1.bottom,
transform.rotation * (v0.bottom + v0.top.cross(&transform.translation))
);
}
#[test]
fn test_transform_mul() {
let a_to_b = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
translation: Vec3::zeros(),
};
let b_to_c = PluckerTransform {
rotation: PlukerRotation::identity(),
translation: vec3(1.0, 0.0, 0.0),
};
let a_to_c = b_to_c * a_to_b;
assert_relative_eq!(a_to_c.translation, vec3(0.0, 1.0, 0.0));
}
#[test]
fn test_transform_inverse() {
let transform = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
translation: vec3(1.0, 0.0, 0.0),
};
let transform_inv = transform.inverse();
let result = transform_inv * transform;
assert_relative_eq!(result.rotation.matrix(), Mat3::identity());
assert_relative_eq!(result.translation, Vec3::zeros());
}
#[test]
fn test_transform_to_matrix() {
let transform = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(
UnitVec3::new_normalize(vec3(1.0, 2.0, 3.0)),
Real::to_radians(45.0),
),
translation: vec3(1.0, 2.0, 3.0),
};
let matrix = transform.matrix();
let e = transform.rotation.matrix();
let rx = transform.translation.into_cross_matrix();
{
let top_left = matrix.fixed_view::<3, 3>(0, 0);
let bottom_right = matrix.fixed_view::<3, 3>(3, 3);
assert_relative_eq!(bottom_right, top_left);
let top_left: Vec<_> = top_left.iter().collect();
let e_data = e.iter().collect::<Vec<_>>();
assert_eq!(top_left, e_data);
}
{
let top_right = matrix.fixed_view::<3, 3>(0, 3);
top_right.iter().for_each(|&x| assert_relative_eq!(x, 0.0));
}
{
let bottom_left = matrix.fixed_view::<3, 3>(3, 0);
let expected = -e * rx;
bottom_left.iter().zip(expected.iter()).for_each(|(a, b)| {
assert_relative_eq!(a, b);
});
}
let motion_vec = SpatialMotionVector::from_pair(vec3(1.0, 2.0, 3.0), vec3(4.0, 5.0, 6.0));
assert_relative_eq!(
(transform * motion_vec).into_vec6(),
matrix * motion_vec.into_vec6(),
epsilon = 1e-6
);
}
#[test]
fn test_transform_to_dual_matrix() {
let transform = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(
UnitVec3::new_normalize(vec3(1.0, 2.0, 3.0)),
Real::to_radians(90.0),
),
translation: vec3(1.0, 2.0, 3.0),
};
let matrix = transform.matrix();
let dual_matrix = transform.dual_matrix();
assert_relative_eq!(
matrix.transpose().try_inverse().unwrap(),
dual_matrix,
epsilon = 1e-6
);
}
#[test]
fn test_transform_articulated_inertia() {
let transform = PluckerTransform {
rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
translation: vec3(1.0, 0.0, 0.0),
};
let inertia: ArticulatedBodyInertia =
RigidBodyInertia::new(1.0, vec3(1.0, 2.0, 3.0), SymmetricMat3::ONE).into();
let new_inertia = transform.transform_articulated_inertia(&inertia);
let dual_x = transform.dual_matrix();
let inverse_x = transform.matrix().try_inverse().unwrap();
let expected_inertia = dual_x * inertia.matrix() * inverse_x;
assert_relative_eq!(new_inertia.matrix(), expected_inertia, epsilon = 1e-6);
assert_relative_eq!(
new_inertia.mass.mat3(),
transform.rotation.matrix()
* inertia.mass.mat3()
* transform.rotation.matrix().transpose(),
epsilon = 1e-6
);
}
#[test]
fn test_pose_inverse() {
let pose = super::Pose {
rotation: UnitQuat::from_axis_angle(&unit_vec3(1., 2., 3.), Real::to_radians(36.0)),
translation: vec3(1.0, 2.0, 3.0),
};
let inv = pose.inverse();
let result = inv * pose;
assert_relative_eq!(result.rotation, UnitQuat::identity());
assert_relative_eq!(result.translation, Vec3::zeros());
}
}