use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; use simba::scalar::{ClosedAdd, ClosedMul}; use simba::simd::SimdRealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimName, U1, U2, U3, U4}; use crate::base::{DefaultAllocator, Scalar, VectorN}; use crate::geometry::{ AbstractRotation, Isometry, Point, Rotation, Similarity, 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) * * Similarity × Similarity * Similarity × R * Similarity × Isometry * * Isometry × Similarity * Isometry ÷ Similarity * * * Similarity ÷ Similarity * Similarity ÷ R * Similarity ÷ Isometry * * Similarity × Point * Similarity × Vector * * * Similarity × Translation * Translation × Similarity * * NOTE: The following are provided explicitly because we can't have R × Similarity. * Rotation × Similarity * UnitQuaternion × Similarity * * Rotation ÷ Similarity * UnitQuaternion ÷ Similarity * * (Assignment Operators) * * Similarity ×= Translation * * Similarity ×= Similarity * Similarity ×= Isometry * Similarity ×= R * * Similarity ÷= Similarity * Similarity ÷= Isometry * Similarity ÷= R * */ // XXX: code duplication: those macros are the same as for the isometry. macro_rules! similarity_binop_impl( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* N: SimdRealField, D: DimName, R> $Op<$Rhs> for $Lhs where N::Element: SimdRealField, R: AbstractRotation, DefaultAllocator: Allocator { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! similarity_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;) => { similarity_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); similarity_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); similarity_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); similarity_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); macro_rules! similarity_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 N::Element: SimdRealField, R: AbstractRotation, DefaultAllocator: Allocator { #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action_val } } impl<'b, N: SimdRealField, D: DimName, R> $OpAssign<&'b $Rhs> for $Lhs where N::Element: SimdRealField, R: AbstractRotation, DefaultAllocator: Allocator { #[inline] fn $op_assign(&mut $lhs, $rhs: &'b $Rhs) { $action_ref } } } ); // Similarity × Similarity // Similarity ÷ Similarity similarity_binop_impl_all!( Mul, mul; self: Similarity, rhs: Similarity, Output = Similarity; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let mut res = self * &rhs.isometry; res.prepend_scaling_mut(rhs.scaling()); res }; ); similarity_binop_impl_all!( Div, div; self: Similarity, rhs: Similarity, Output = Similarity; [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() }; ); // Similarity ×= Translation similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Translation; [val] => *self *= &rhs; [ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.vector) * self.scaling(); self.isometry.translation.vector += shift; }; ); // Similarity ×= Similarity // Similarity ÷= Similarity similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Similarity; [val] => *self *= &rhs; [ref] => { *self *= &rhs.isometry; self.prepend_scaling_mut(rhs.scaling()); }; ); similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: Similarity; [val] => *self /= &rhs; // TODO: don't invert explicitly. [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Similarity ×= Isometry // Similarity ÷= Isometry similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Isometry; [val] => *self *= &rhs; [ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling(); self.isometry.translation.vector += shift; self.isometry.rotation *= rhs.rotation.clone(); }; ); similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: Isometry; [val] => *self /= &rhs; // TODO: don't invert explicitly. [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Similarity ×= R // Similarity ÷= R md_assign_impl_all!( MulAssign, mul_assign where N: SimdRealField for N::Element: SimdRealField; (D, U1), (D, D) for D: DimName; self: Similarity>, rhs: Rotation; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where N: SimdRealField for N::Element: SimdRealField; (D, U1), (D, D) for D: DimName; self: Similarity>, 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 N: SimdRealField for N::Element: SimdRealField; (U3, U3), (U3, U3) for; self: Similarity>, rhs: UnitQuaternion; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= *rhs; ); md_assign_impl_all!( DivAssign, div_assign where N: SimdRealField for N::Element: SimdRealField; (U3, U3), (U3, U3) for; self: Similarity>, 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 N: SimdRealField for N::Element: SimdRealField; (U2, U2), (U2, U2) for; self: Similarity>, rhs: UnitComplex; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= *rhs; ); md_assign_impl_all!( DivAssign, div_assign where N: SimdRealField for N::Element: SimdRealField; (U2, U2), (U2, U2) for; self: Similarity>, 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() }; ); // Similarity × Isometry // Similarity ÷ Isometry similarity_binop_impl_all!( Mul, mul; self: Similarity, rhs: Isometry, Output = Similarity; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling(); Similarity::from_parts( #[allow(clippy::suspicious_arithmetic_impl)] Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone() * rhs.rotation.clone(), self.scaling()) }; ); similarity_binop_impl_all!( Div, div; self: Similarity, rhs: Isometry, Output = Similarity; [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 × Similarity // Isometry ÷ Similarity similarity_binop_impl_all!( Mul, mul; self: Isometry, rhs: Similarity, Output = Similarity; [val val] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * rhs.isometry, scaling) }; [ref val] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * rhs.isometry, scaling) }; [val ref] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * &rhs.isometry, scaling) }; [ref ref] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * &rhs.isometry, scaling) }; ); similarity_binop_impl_all!( Div, div; self: Isometry, rhs: Similarity, Output = Similarity; [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() }; ); // Similarity × Point similarity_binop_impl_all!( Mul, mul; self: Similarity, right: Point, Output = Point; [val val] => { let scaling = self.scaling(); self.isometry.translation * (self.isometry.rotation.transform_point(&right) * scaling) }; [ref val] => &self.isometry.translation * (self.isometry.rotation.transform_point(&right) * self.scaling()); [val ref] => { let scaling = self.scaling(); self.isometry.translation * (self.isometry.rotation.transform_point(right) * scaling) }; [ref ref] => &self.isometry.translation * (self.isometry.rotation.transform_point(right) * self.scaling()); ); // Similarity × Vector similarity_binop_impl_all!( Mul, mul; self: Similarity, right: VectorN, Output = VectorN; [val val] => self.isometry.rotation.transform_vector(&right) * self.scaling(); [ref val] => self.isometry.rotation.transform_vector(&right) * self.scaling(); [val ref] => self.isometry.rotation.transform_vector(right) * self.scaling(); [ref ref] => self.isometry.rotation.transform_vector(right) * self.scaling(); ); // Similarity × Translation similarity_binop_impl_all!( Mul, mul; self: Similarity, right: Translation, Output = Similarity; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self.isometry.rotation.transform_vector(&right.vector) * self.scaling(); Similarity::from_parts( #[allow(clippy::suspicious_arithmetic_impl)] Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone(), self.scaling()) }; ); // Translation × Similarity similarity_binop_impl_all!( Mul, mul; self: Translation, right: Similarity, Output = Similarity; [val val] => { let scaling = right.scaling(); Similarity::from_isometry(self * right.isometry, scaling) }; [ref val] => { let scaling = right.scaling(); Similarity::from_isometry(self * right.isometry, scaling) }; [val ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); macro_rules! similarity_from_composition_impl( ($Op: ident, $op: ident; ($R1: ty, $C1: ty),($R2: ty, $C2: ty) $(for $Dims: ident: $DimsBound: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* N: SimdRealField $(, $Dims: $DimsBound)*> $Op<$Rhs> for $Lhs where N::Element: SimdRealField, DefaultAllocator: Allocator + Allocator { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! similarity_from_composition_impl_all( ($Op: ident, $op: ident; ($R1: ty, $C1: ty),($R2: ty, $C2: ty) $(for $Dims: ident: $DimsBound: 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;) => { similarity_from_composition_impl!( $Op, $op; ($R1, $C1),($R2, $C2) $(for $Dims: $DimsBound),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); similarity_from_composition_impl!( $Op, $op; ($R1, $C1),($R2, $C2) $(for $Dims: $DimsBound),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); similarity_from_composition_impl!( $Op, $op; ($R1, $C1),($R2, $C2) $(for $Dims: $DimsBound),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); similarity_from_composition_impl!( $Op, $op; ($R1, $C1),($R2, $C2) $(for $Dims: $DimsBound),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); // Similarity × Rotation similarity_from_composition_impl_all!( Mul, mul; (D, D), (D, U1) for D: DimName; self: Similarity>, rhs: Rotation, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // Rotation × Similarity similarity_from_composition_impl_all!( Mul, mul; (D, D), (D, U1) for D: DimName; self: Rotation, right: Similarity>, Output = Similarity>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); // Similarity ÷ Rotation similarity_from_composition_impl_all!( Div, div; (D, D), (D, U1) for D: DimName; self: Similarity>, rhs: Rotation, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); ); // Rotation ÷ Similarity similarity_from_composition_impl_all!( Div, div; (D, D), (D, U1) for D: DimName; self: Rotation, right: Similarity>, Output = Similarity>; // 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() }; ); // Similarity × UnitQuaternion similarity_from_composition_impl_all!( Mul, mul; (U4, U1), (U3, U1); self: Similarity>, rhs: UnitQuaternion, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // UnitQuaternion × Similarity similarity_from_composition_impl_all!( Mul, mul; (U4, U1), (U3, U1); self: UnitQuaternion, right: Similarity>, Output = Similarity>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); // Similarity ÷ UnitQuaternion similarity_from_composition_impl_all!( Div, div; (U4, U1), (U3, U1); self: Similarity>, rhs: UnitQuaternion, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); ); // UnitQuaternion ÷ Similarity similarity_from_composition_impl_all!( Div, div; (U4, U1), (U3, U1); self: UnitQuaternion, right: Similarity>, Output = Similarity>; // 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() }; ); // Similarity × UnitComplex similarity_from_composition_impl_all!( Mul, mul; (U2, U1), (U2, U1); self: Similarity>, rhs: UnitComplex, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // Similarity ÷ UnitComplex similarity_from_composition_impl_all!( Div, div; (U2, U1), (U2, U1); self: Similarity>, rhs: UnitComplex, Output = Similarity>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); );