#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::{DualQuaternion, Point3, Unit, UnitDualQuaternion, UnitQuaternion, Vector3}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest!( #[test] fn isometry_equivalence(iso in isometry3(), p in point3(), v in vector3()) { let dq = UnitDualQuaternion::from_isometry(&iso); prop_assert!(relative_eq!(iso * p, dq * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * v, dq * v, epsilon = 1.0e-7)); } #[test] fn inverse_is_identity(i in unit_dual_quaternion(), p in point3(), v in vector3()) { let ii = i.inverse(); prop_assert!(relative_eq!(i * ii, UnitDualQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(ii * i, UnitDualQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7)); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn multiply_equals_alga_transform( dq in unit_dual_quaternion(), v in vector3(), p in point3() ) { prop_assert!(dq * v == dq.transform_vector(&v) && dq * p == dq.transform_point(&p) && relative_eq!( dq.inverse() * v, dq.inverse_transform_vector(&v), epsilon = 1.0e-7 ) && relative_eq!( dq.inverse() * p, dq.inverse_transform_point(&p), epsilon = 1.0e-7 )); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn composition( dq in unit_dual_quaternion(), uq in unit_quaternion(), t in translation3(), v in vector3(), p in point3() ) { // (rotation × dual quaternion) * point = rotation × (dual quaternion * point) prop_assert!(relative_eq!((uq * dq) * v, uq * (dq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * dq) * p, uq * (dq * p), epsilon = 1.0e-7)); // (dual quaternion × rotation) * point = dual quaternion × (rotation * point) prop_assert!(relative_eq!((dq * uq) * v, dq * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((dq * uq) * p, dq * (uq * p), epsilon = 1.0e-7)); // (translation × dual quaternion) * point = translation × (dual quaternion * point) prop_assert!(relative_eq!((t * dq) * v, (dq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * dq) * p, t * (dq * p), epsilon = 1.0e-7)); // (dual quaternion × translation) * point = dual quaternion × (translation * point) prop_assert!(relative_eq!((dq * t) * v, dq * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((dq * t) * p, dq * (t * p), epsilon = 1.0e-7)); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn sclerp_is_defined_for_identical_orientations( dq in unit_dual_quaternion(), s in -1.0f64..2.0f64, t in translation3(), ) { // Should not panic. prop_assert!(relative_eq!(dq.sclerp(&dq, 0.0), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, 0.5), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, 1.0), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, s), dq, epsilon = 1.0e-7)); let unit = UnitDualQuaternion::identity(); prop_assert!(relative_eq!(unit.sclerp(&unit, 0.0), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, 0.5), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, 1.0), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, s), unit, epsilon = 1.0e-7)); // ScLERPing two unit dual quaternions with nearly equal rotation // components should result in a unit dual quaternion with a rotation // component nearly equal to either input. let dq2 = t * dq; prop_assert!(relative_eq!(dq.sclerp(&dq2, 0.0).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, 0.5).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, 1.0).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, s).real, dq.real, epsilon = 1.0e-7)); // ScLERPing two unit dual quaternions with nearly equal rotation // components should result in a unit dual quaternion with a translation // component which is nearly equal to linearly interpolating the // translation components of the inputs. prop_assert!(relative_eq!( dq.sclerp(&dq2, s).translation().vector, dq.translation().vector.lerp(&dq2.translation().vector, s), epsilon = 1.0e-7 )); let unit2 = t * unit; prop_assert!(relative_eq!(unit.sclerp(&unit2, 0.0).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, 0.5).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, 1.0).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, s).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!( unit.sclerp(&unit2, s).translation().vector, unit.translation().vector.lerp(&unit2.translation().vector, s), epsilon = 1.0e-7 )); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn sclerp_is_not_defined_for_opposite_orientations( dq in unit_dual_quaternion(), s in 0.1f64..0.9f64, t in translation3(), t2 in translation3(), v in vector3(), ) { let iso = dq.to_isometry(); let rot = iso.rotation; if let Some((axis, angle)) = rot.axis_angle() { let flipped = UnitQuaternion::from_axis_angle(&axis, angle + std::f64::consts::PI); let dqf = flipped * rot.inverse() * dq.clone(); prop_assert!(dq.try_sclerp(&dqf, 0.5, 1.0e-7).is_none()); prop_assert!(dq.try_sclerp(&dqf, s, 1.0e-7).is_none()); } let dq2 = t * dq; let iso2 = dq2.to_isometry(); let rot2 = iso2.rotation; if let Some((axis, angle)) = rot2.axis_angle() { let flipped = UnitQuaternion::from_axis_angle(&axis, angle + std::f64::consts::PI); let dq3f = t2 * flipped * rot.inverse() * dq.clone(); prop_assert!(dq2.try_sclerp(&dq3f, 0.5, 1.0e-7).is_none()); prop_assert!(dq2.try_sclerp(&dq3f, s, 1.0e-7).is_none()); } if let Some(axis) = Unit::try_new(v, 1.0e-7) { let unit = UnitDualQuaternion::identity(); let flip = UnitQuaternion::from_axis_angle(&axis, std::f64::consts::PI); let unitf = flip * unit; prop_assert!(unit.try_sclerp(&unitf, 0.5, 1.0e-7).is_none()); prop_assert!(unit.try_sclerp(&unitf, s, 1.0e-7).is_none()); let unit2f = t * unit * flip; prop_assert!(unit.try_sclerp(&unit2f, 0.5, 1.0e-7).is_none()); prop_assert!(unit.try_sclerp(&unit2f, s, 1.0e-7).is_none()); } } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn all_op_exist( dq in dual_quaternion(), udq in unit_dual_quaternion(), uq in unit_quaternion(), s in PROPTEST_F64, t in translation3(), v in vector3(), p in point3() ) { let dqMs: DualQuaternion<_> = dq * s; let dqMdq: DualQuaternion<_> = dq * dq; let dqMudq: DualQuaternion<_> = dq * udq; let udqMdq: DualQuaternion<_> = udq * dq; let iMi: UnitDualQuaternion<_> = udq * udq; let iMuq: UnitDualQuaternion<_> = udq * uq; let iDi: UnitDualQuaternion<_> = udq / udq; let iDuq: UnitDualQuaternion<_> = udq / uq; let iMp: Point3<_> = udq * p; let iMv: Vector3<_> = udq * v; let iMt: UnitDualQuaternion<_> = udq * t; let tMi: UnitDualQuaternion<_> = t * udq; let uqMi: UnitDualQuaternion<_> = uq * udq; let uqDi: UnitDualQuaternion<_> = uq / udq; let mut dqMs1 = dq; let mut dqMdq1 = dq; let mut dqMdq2 = dq; let mut dqMudq1 = dq; let mut dqMudq2 = dq; let mut iMt1 = udq; let mut iMt2 = udq; let mut iMi1 = udq; let mut iMi2 = udq; let mut iMuq1 = udq; let mut iMuq2 = udq; let mut iDi1 = udq; let mut iDi2 = udq; let mut iDuq1 = udq; let mut iDuq2 = udq; dqMs1 *= s; dqMdq1 *= dq; dqMdq2 *= &dq; dqMudq1 *= udq; dqMudq2 *= &udq; iMt1 *= t; iMt2 *= &t; iMi1 *= udq; iMi2 *= &udq; iMuq1 *= uq; iMuq2 *= &uq; iDi1 /= udq; iDi2 /= &udq; iDuq1 /= uq; iDuq2 /= &uq; prop_assert!(dqMs == dqMs1 && dqMdq == dqMdq1 && dqMdq == dqMdq2 && dqMudq == dqMudq1 && dqMudq == dqMudq2 && iMt == iMt1 && iMt == iMt2 && iMi == iMi1 && iMi == iMi2 && iMuq == iMuq1 && iMuq == iMuq2 && iDi == iDi1 && iDi == iDi2 && iDuq == iDuq1 && iDuq == iDuq2 && dqMs == &dq * s && dqMdq == &dq * &dq && dqMdq == dq * &dq && dqMdq == &dq * dq && dqMudq == &dq * &udq && dqMudq == dq * &udq && dqMudq == &dq * udq && udqMdq == &udq * &dq && udqMdq == udq * &dq && udqMdq == &udq * dq && iMi == &udq * &udq && iMi == udq * &udq && iMi == &udq * udq && iMuq == &udq * &uq && iMuq == udq * &uq && iMuq == &udq * uq && iDi == &udq / &udq && iDi == udq / &udq && iDi == &udq / udq && iDuq == &udq / &uq && iDuq == udq / &uq && iDuq == &udq / uq && iMp == &udq * &p && iMp == udq * &p && iMp == &udq * p && iMv == &udq * &v && iMv == udq * &v && iMv == &udq * v && iMt == &udq * &t && iMt == udq * &t && iMt == &udq * t && tMi == &t * &udq && tMi == t * &udq && tMi == &t * udq && uqMi == &uq * &udq && uqMi == uq * &udq && uqMi == &uq * udq && uqDi == &uq / &udq && uqDi == uq / &udq && uqDi == &uq / udq) } );