use crate::base::constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint}; use crate::base::{Const, Matrix, Unit, Vector}; use crate::dimension::{Dim, U1}; use crate::storage::{Storage, StorageMut}; use simba::scalar::ComplexField; use crate::geometry::Point; /// A reflection wrt. a plane. pub struct Reflection { axis: Vector, bias: T, } impl>, const D: usize> Reflection, S> { /// Creates a new reflection wrt. the plane orthogonal to the given axis and that contains the /// point `pt`. pub fn new_containing_point(axis: Unit, S>>, pt: &Point) -> Self { let bias = axis.dotc(&pt.coords); Self::new(axis, bias) } } impl> Reflection { /// Creates a new reflection wrt. the plane orthogonal to the given axis and bias. /// /// The bias is the position of the plane on the axis. In particular, a bias equal to zero /// represents a plane that passes through the origin. pub fn new(axis: Unit>, bias: T) -> Self { Self { axis: axis.into_inner(), bias, } } /// The reflection axis. #[must_use] pub fn axis(&self) -> &Vector { &self.axis } /// The reflection bias. /// /// The bias is the position of the plane on the axis. In particular, a bias equal to zero /// represents a plane that passes through the origin. #[must_use] pub fn bias(&self) -> T { self.bias.clone() } // TODO: naming convention: reflect_to, reflect_assign ? /// Applies the reflection to the columns of `rhs`. pub fn reflect(&self, rhs: &mut Matrix) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..rhs.ncols() { // NOTE: we borrow the column twice here. First it is borrowed immutably for the // dot product, and then mutably. Somehow, this allows significantly // better optimizations of the dot product from the compiler. let m_two: T = crate::convert(-2.0f64); let factor = (self.axis.dotc(&rhs.column(i)) - self.bias.clone()) * m_two; rhs.column_mut(i).axpy(factor, &self.axis, T::one()); } } // TODO: naming convention: reflect_to, reflect_assign ? /// Applies the reflection to the columns of `rhs`. pub fn reflect_with_sign(&self, rhs: &mut Matrix, sign: T) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..rhs.ncols() { // NOTE: we borrow the column twice here. First it is borrowed immutably for the // dot product, and then mutably. Somehow, this allows significantly // better optimizations of the dot product from the compiler. let m_two = sign.clone().scale(crate::convert(-2.0f64)); let factor = (self.axis.dotc(&rhs.column(i)) - self.bias.clone()) * m_two; rhs.column_mut(i).axpy(factor, &self.axis, sign.clone()); } } /// Applies the reflection to the rows of `lhs`. pub fn reflect_rows( &self, lhs: &mut Matrix, work: &mut Vector, ) where S2: StorageMut, S3: StorageMut, ShapeConstraint: DimEq + AreMultipliable, { lhs.mul_to(&self.axis, work); if !self.bias.is_zero() { work.add_scalar_mut(-self.bias.clone()); } let m_two: T = crate::convert(-2.0f64); lhs.gerc(m_two, work, &self.axis, T::one()); } /// Applies the reflection to the rows of `lhs`. pub fn reflect_rows_with_sign( &self, lhs: &mut Matrix, work: &mut Vector, sign: T, ) where S2: StorageMut, S3: StorageMut, ShapeConstraint: DimEq + AreMultipliable, { lhs.mul_to(&self.axis, work); if !self.bias.is_zero() { work.add_scalar_mut(-self.bias.clone()); } let m_two = sign.clone().scale(crate::convert(-2.0f64)); lhs.gerc(m_two, work, &self.axis, sign); } }