// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; use simba::scalar::{ClosedAdd, ClosedMul}; use simba::simd::SimdRealField; use crate::base::{SVector, Unit}; use crate::Scalar; use crate::geometry::{ AbstractRotation, Isometry, Point, Rotation, Translation, UnitComplex, UnitQuaternion, }; // TODO: there are several cloning of rotations that we could probably get rid of (but we didn't // yet because that would require to add a bound like `where for<'a, 'b> &'a R: Mul<&'b R, Output = R>` // which is quite ugly. /* * * In this file, we provide: * ========================= * * * (Operators) * * Isometry × Isometry * Isometry × R * * * Isometry ÷ Isometry * Isometry ÷ R * * Isometry × Point * Isometry × Vector * Isometry × Unit * * * Isometry × Translation * Translation × Isometry * Translation × R -> Isometry * * NOTE: The following are provided explicitly because we can't have R × Isometry. * Rotation × Isometry * UnitQuaternion × Isometry * * Rotation ÷ Isometry * UnitQuaternion ÷ Isometry * * Rotation × Translation -> Isometry * UnitQuaternion × Translation -> Isometry * * * (Assignment Operators) * * Isometry ×= Translation * * Isometry ×= Isometry * Isometry ×= R * * Isometry ÷= Isometry * Isometry ÷= R * */ macro_rules! isometry_binop_impl( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField, R, const D: usize> $Op<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation, { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! isometry_binop_impl_all( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { isometry_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); isometry_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); isometry_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); isometry_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); macro_rules! isometry_binop_assign_impl_all( ($OpAssign: ident, $op_assign: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; [val] => $action_val: expr; [ref] => $action_ref: expr;) => { impl $OpAssign<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action_val } } impl<'b, T: SimdRealField, R, const D: usize> $OpAssign<&'b $Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { #[inline] fn $op_assign(&mut $lhs, $rhs: &'b $Rhs) { $action_ref } } } ); // Isometry × Isometry // Isometry ÷ Isometry isometry_binop_impl_all!( Mul, mul; self: Isometry, rhs: Isometry, Output = Isometry; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let shift = self.rotation.transform_vector(&rhs.translation.vector); #[allow(clippy::suspicious_arithmetic_impl)] Isometry::from_parts(Translation::from(&self.translation.vector + shift), self.rotation.clone() * rhs.rotation.clone()) // TODO: too bad we have to clone. }; ); isometry_binop_impl_all!( Div, div; self: Isometry, rhs: Isometry, Output = Isometry; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Isometry ×= Translation isometry_binop_assign_impl_all!( MulAssign, mul_assign; self: Isometry, rhs: Translation; [val] => *self *= &rhs; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { let shift = self.rotation.transform_vector(&rhs.vector); self.translation.vector += shift; }; ); // Isometry ×= Isometry // Isometry ÷= Isometry isometry_binop_assign_impl_all!( MulAssign, mul_assign; self: Isometry, rhs: Isometry; [val] => *self *= &rhs; [ref] => { let shift = self.rotation.transform_vector(&rhs.translation.vector); self.translation.vector += shift; self.rotation *= rhs.rotation.clone(); }; ); isometry_binop_assign_impl_all!( DivAssign, div_assign; self: Isometry, rhs: Isometry; [val] => *self /= &rhs; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Isometry ×= R // Isometry ÷= R md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Isometry, D>, rhs: Rotation; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Isometry, D>, rhs: Rotation; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Isometry, 3>, rhs: UnitQuaternion; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Isometry, 3>, rhs: UnitQuaternion; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Isometry, 2>, rhs: UnitComplex; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Isometry, 2>, rhs: UnitComplex; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Isometry × Point isometry_binop_impl_all!( Mul, mul; self: Isometry, right: Point, Output = Point; [val val] => self.translation * self.rotation.transform_point(&right); [ref val] => &self.translation * self.rotation.transform_point(&right); [val ref] => self.translation * self.rotation.transform_point(right); [ref ref] => &self.translation * self.rotation.transform_point(right); ); // Isometry × Vector isometry_binop_impl_all!( Mul, mul; // TODO: because of `transform_vector`, we cant use a generic storage type for the rhs vector, // i.e., right: Vector where S: Storage. self: Isometry, right: SVector, Output = SVector; [val val] => self.rotation.transform_vector(&right); [ref val] => self.rotation.transform_vector(&right); [val ref] => self.rotation.transform_vector(right); [ref ref] => self.rotation.transform_vector(right); ); // Isometry × Unit isometry_binop_impl_all!( Mul, mul; // TODO: because of `transform_vector`, we cant use a generic storage type for the rhs vector, // i.e., right: Vector where S: Storage. self: Isometry, right: Unit>, Output = Unit>; [val val] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [ref val] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [val ref] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [ref ref] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); ); // Isometry × Translation isometry_binop_impl_all!( Mul, mul; self: Isometry, right: Translation, Output = Isometry; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { #[allow(clippy::suspicious_arithmetic_impl)] let new_tr = &self.translation.vector + self.rotation.transform_vector(&right.vector); Isometry::from_parts(Translation::from(new_tr), self.rotation.clone()) }; ); // Translation × Isometry isometry_binop_impl_all!( Mul, mul; self: Translation, right: Isometry, Output = Isometry; [val val] => Isometry::from_parts(self * right.translation, right.rotation); [ref val] => Isometry::from_parts(self * &right.translation, right.rotation); [val ref] => Isometry::from_parts(self * &right.translation, right.rotation.clone()); [ref ref] => Isometry::from_parts(self * &right.translation, right.rotation.clone()); ); macro_rules! isometry_from_composition_impl( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, const $Dims: usize)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! isometry_from_composition_impl_all( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); // Rotation × Translation isometry_from_composition_impl_all!( Mul, mul; D; self: Rotation, right: Translation, Output = Isometry, D>; [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); [ref val] => Isometry::from_parts(Translation::from(self * right.vector), self.clone()); [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); [ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), self.clone()); ); // UnitQuaternion × Translation isometry_from_composition_impl_all!( Mul, mul; ; self: UnitQuaternion, right: Translation, Output = Isometry, 3>; [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); [ref val] => Isometry::from_parts(Translation::from( self * right.vector), self.clone()); [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); [ref ref] => Isometry::from_parts(Translation::from( self * &right.vector), self.clone()); ); // Isometry × Rotation isometry_from_composition_impl_all!( Mul, mul; D; self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // Rotation × Isometry isometry_from_composition_impl_all!( Mul, mul; D; self: Rotation, right: Isometry, D>, Output = Isometry, D>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); // Isometry ÷ Rotation isometry_from_composition_impl_all!( Div, div; D; self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); ); // Rotation ÷ Isometry isometry_from_composition_impl_all!( Div, div; D; self: Rotation, right: Isometry, D>, Output = Isometry, D>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Isometry × UnitQuaternion isometry_from_composition_impl_all!( Mul, mul; ; self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // UnitQuaternion × Isometry isometry_from_composition_impl_all!( Mul, mul; ; self: UnitQuaternion, right: Isometry, 3>, Output = Isometry, 3>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); // Isometry ÷ UnitQuaternion isometry_from_composition_impl_all!( Div, div; ; self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); ); // UnitQuaternion ÷ Isometry isometry_from_composition_impl_all!( Div, div; ; self: UnitQuaternion, right: Isometry, 3>, Output = Isometry, 3>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Translation × Rotation isometry_from_composition_impl_all!( Mul, mul; D; self: Translation, right: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self, right); [ref val] => Isometry::from_parts(self.clone(), right); [val ref] => Isometry::from_parts(self, right.clone()); [ref ref] => Isometry::from_parts(self.clone(), right.clone()); ); // Translation × UnitQuaternion isometry_from_composition_impl_all!( Mul, mul; ; self: Translation, right: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self, right); [ref val] => Isometry::from_parts(self.clone(), right); [val ref] => Isometry::from_parts(self, right.clone()); [ref ref] => Isometry::from_parts(self.clone(), right.clone()); ); // Isometry × UnitComplex isometry_from_composition_impl_all!( Mul, mul; ; self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // Isometry ÷ UnitComplex isometry_from_composition_impl_all!( Div, div; ; self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); );