use crate::IsoTransform;
use crate::Quat;
use crate::Vec3;
use crate::Vec4;
use crate::Vec4Swizzles;
#[cfg(target_arch = "spirv")]
use num_traits::Float;
#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "speedy", derive(speedy::Writable, speedy::Readable))]
#[cfg_attr(
feature = "bytemuck",
derive(bytemuck::NoUninit, bytemuck::AnyBitPattern)
)]
#[repr(C)]
pub struct DualScalar {
pub real: f32,
pub dual: f32,
}
impl DualScalar {
#[inline]
pub fn sqrt(self) -> Self {
let real_sqrt = self.real.sqrt();
let dual = self.dual / 2.0 * real_sqrt;
Self {
real: real_sqrt,
dual,
}
}
#[inline]
pub fn inverse_sqrt(self) -> Self {
let real_sqrt = self.real.sqrt();
Self {
real: 1.0 / real_sqrt,
dual: -self.dual / (2.0 * self.real * real_sqrt),
}
}
#[inline]
pub fn inverse(self) -> Self {
let real_inv = 1.0 / self.real;
Self {
real: real_inv,
dual: -self.dual * real_inv * real_inv,
}
}
}
impl core::ops::Mul for DualScalar {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self {
real: self.real * rhs.real,
dual: self.real * rhs.dual + self.dual * rhs.real,
}
}
}
impl core::ops::Add for DualScalar {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
real: self.real + rhs.real,
dual: self.dual + rhs.dual,
}
}
}
impl core::ops::Sub for DualScalar {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
real: self.real - rhs.real,
dual: self.dual - rhs.dual,
}
}
}
impl core::ops::Mul<f32> for DualScalar {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self {
real: self.real * rhs,
dual: self.dual * rhs,
}
}
}
#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "speedy", derive(speedy::Writable, speedy::Readable))]
#[cfg_attr(
feature = "bytemuck",
derive(bytemuck::NoUninit, bytemuck::AnyBitPattern)
)]
#[repr(C, align(16))]
pub struct DualQuat {
pub real: Quat,
pub dual: Quat,
}
impl DualQuat {
const fn _assert_repr() {
let _: [(); core::mem::size_of::<Self>()] = [(); 32];
let _: [(); core::mem::align_of::<Self>()] = [(); 16];
}
pub const IDENTITY: Self = Self {
real: Quat::IDENTITY,
dual: Quat::from_xyzw(0.0, 0.0, 0.0, 0.0),
};
pub const ZERO: Self = Self {
real: Quat::from_xyzw(0.0, 0.0, 0.0, 0.0),
dual: Quat::from_xyzw(0.0, 0.0, 0.0, 0.0),
};
#[inline]
pub fn from_iso_transform(iso_transform: IsoTransform) -> Self {
Self::from_rotation_translation(iso_transform.rotation, iso_transform.translation())
}
#[inline]
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
Self {
real: rotation,
dual: Quat::from_vec4((translation * 0.5).extend(0.0)) * rotation,
}
}
#[inline]
pub fn from_translation(translation: Vec3) -> Self {
Self {
real: Quat::IDENTITY,
dual: Quat::from_vec4((translation * 0.5).extend(0.0)),
}
}
#[inline]
pub fn from_quat(rotation: Quat) -> Self {
Self {
real: rotation,
dual: Quat::from_xyzw(0.0, 0.0, 0.0, 0.0),
}
}
#[inline]
pub fn to_rotation_translation(self) -> (Quat, Vec3) {
let translation = 2.0 * Vec4::from(self.dual * self.real.conjugate()).xyz();
(self.real, translation)
}
#[inline]
pub fn conjugate(self) -> Self {
Self {
real: self.real.conjugate(),
dual: self.dual.conjugate(),
}
}
#[inline]
pub fn inverse(self) -> Self {
debug_assert!(self.is_normalized());
self.conjugate()
}
#[inline]
pub fn norm_squared(self) -> DualScalar {
let real_vec = Vec4::from(self.real);
let dual_vec = Vec4::from(self.dual);
let dot = real_vec.dot(dual_vec);
DualScalar {
real: self.real.length_squared(),
dual: 2.0 * dot,
}
}
#[inline]
pub fn norm(self) -> DualScalar {
let real_mag = self.real.length();
let real_vec = Vec4::from(self.real);
let dual_vec = Vec4::from(self.dual);
let dot = real_vec.dot(dual_vec);
DualScalar {
real: real_mag,
dual: dot / real_mag,
}
}
#[inline]
pub fn is_normalized(self) -> bool {
let norm_sq = self.norm_squared();
let eps = 1e-4; (norm_sq.real - 1.0).abs() < eps && norm_sq.dual.abs() < eps
}
#[inline]
pub fn normalize_full(self) -> Self {
let real_normsq = self.real.length_squared();
let real_norm = real_normsq.sqrt();
let real_vec = Vec4::from(self.real);
let dual_vec = Vec4::from(self.dual);
let dot = real_vec.dot(dual_vec);
let normalizer = DualScalar {
real: 1.0 / real_norm,
dual: -dot / (real_norm * real_normsq),
};
normalizer * self
}
#[inline]
pub fn normalize_to_rotation_translation(mut self) -> (Quat, Vec3) {
let real_norm_inv = self.real.length_recip();
self.real = self.real * real_norm_inv;
self.dual = self.dual * real_norm_inv;
self.to_rotation_translation()
}
#[inline]
pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
self.real.abs_diff_eq(other.real, max_abs_diff)
&& self.dual.abs_diff_eq(other.dual, max_abs_diff)
}
#[inline]
pub fn right_mul_translation(mut self, trans: Vec3) -> Self {
self.dual.w -=
0.5 * (self.real.x * trans.x + self.real.y * trans.y + self.real.z * trans.z);
self.dual.x +=
0.5 * (self.real.w * trans.x + self.real.y * trans.z - self.real.z * trans.y);
self.dual.y +=
0.5 * (self.real.w * trans.y + self.real.z * trans.x - self.real.x * trans.z);
self.dual.z +=
0.5 * (self.real.w * trans.z + self.real.x * trans.y - self.real.y * trans.x);
self
}
}
impl core::ops::Mul for DualQuat {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let Self { real: q0, dual: q1 } = self;
let Self { real: q2, dual: q3 } = rhs;
let real = q0 * q2;
let dual = q0 * q3 + q1 * q2;
Self { real, dual }
}
}
impl core::ops::Add for DualQuat {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self {
real: self.real + rhs.real,
dual: self.dual + rhs.dual,
}
}
}
impl core::ops::AddAssign for DualQuat {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.real = self.real + rhs.real;
self.dual = self.dual + rhs.dual;
}
}
impl core::ops::Sub for DualQuat {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self {
real: self.real - rhs.real,
dual: self.dual - rhs.dual,
}
}
}
impl core::ops::SubAssign for DualQuat {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.real = self.real - rhs.real;
self.dual = self.dual - rhs.dual;
}
}
impl core::ops::Mul<f32> for DualQuat {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self {
real: self.real * rhs,
dual: self.dual * rhs,
}
}
}
impl core::ops::Mul<DualQuat> for f32 {
type Output = DualQuat;
#[inline]
fn mul(self, rhs: DualQuat) -> Self::Output {
rhs * self
}
}
impl core::ops::Mul<DualQuat> for DualScalar {
type Output = DualQuat;
#[inline]
fn mul(self, rhs: DualQuat) -> Self::Output {
DualQuat {
real: rhs.real * self.real,
dual: (rhs.dual * self.real) + (rhs.real * self.dual),
}
}
}
impl core::ops::Mul<DualScalar> for DualQuat {
type Output = Self;
#[inline]
fn mul(self, rhs: DualScalar) -> Self::Output {
rhs * self
}
}
#[cfg(feature = "std")]
impl core::fmt::Debug for DualQuat {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let (rot, translation) = self.to_rotation_translation();
let (axis, angle) = rot.to_axis_angle();
f.debug_struct("DualQuat")
.field(
"translation",
&format!("[{} {} {}]", translation.x, translation.y, translation.z),
)
.field(
"rotation",
&format!(
"{:.1}° around [{} {} {}]",
angle.to_degrees(),
axis[0],
axis[1],
axis[2],
),
)
.field("real(raw)", &self.real)
.field("dual(raw)", &self.dual)
.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
fn approx_eq_dualquat(a: DualQuat, b: DualQuat) -> bool {
let max_abs_diff = 1e-6;
a.abs_diff_eq(b, max_abs_diff)
}
macro_rules! assert_approx_eq_dualquat {
($a: expr, $b: expr) => {
assert!(approx_eq_dualquat($a, $b), "{:#?} != {:#?}", $a, $b,);
};
}
#[test]
fn test_inverse() {
let transform = DualQuat::from_rotation_translation(
Quat::from_rotation_y(std::f32::consts::PI),
Vec3::ONE,
);
let identity = transform * transform.inverse();
assert_approx_eq_dualquat!(identity, DualQuat::IDENTITY);
let transform = DualQuat::from_rotation_translation(
Quat::from_axis_angle(Vec3::ONE.normalize(), 1.234),
Vec3::new(1.0, 2.0, 3.0),
);
let identity = transform * transform.inverse();
assert_approx_eq_dualquat!(identity, DualQuat::IDENTITY);
}
}