//! This module defines the [Quaternion][1] and //! [DualQuaternion][2] structures. //! //! [1]: https://en.wikipedia.org/wiki/Quaternion //! [2]: https://en.wikipedia.org/wiki/Dual_quaternion use std::fmt::{Error, Formatter, Debug, Display}; use std::ops::{Add, Sub, Mul, Div, Neg}; use crate::zero::Zero; use crate::one::One; use crate::real::Real; use crate::trig::Radian; use crate::trig::Trig; use crate::vector::{Vector, EuclideanVector, Vector3}; /// A Quaternion is a combination of a scalar and a vector /// of complex numbers. /// /// [S, V] = [S, Xi + Yj + Zk] /// /// Remember that the complex number axes combine /// in the following manner. /// /// i^2 = j^2 = k^2 = -1 /// /// | | | /// |:-------:|:-------:| /// | ij = 1 | ik = -1 | /// | jk = 1 | kj = -1 | /// | ki = 1 | ji = -1 | #[derive(Clone, Copy)] pub struct Quaternion { /// TODO: Describe this. pub scalar: T, /// TODO: Describe this. pub vector: Vector3 } /// A macro that will define a binary operation /// for a Quaternion and its components for /// scalar values. macro_rules! binary_operator_impl { ($traitName: ident :: $funcName: ident, $structName: ident) => { impl $traitName for $structName where T: Trig { type Output = $structName; fn $funcName(self, scalar: T) -> $structName { $structName::new(self.scalar.$funcName(scalar), self.vector.$funcName(scalar)) } } impl<'a, T> $traitName for &'a $structName where T: Trig { type Output = $structName; fn $funcName(self, scalar: T) -> $structName { $structName::new(self.scalar.$funcName(scalar), self.vector.$funcName(scalar)) } } impl<'a, T> $traitName<&'a T> for $structName where T: Trig { type Output = $structName; fn $funcName(self, scalar: &'a T) -> $structName { $structName::new(self.scalar.$funcName(*scalar), self.vector.$funcName(*scalar)) } } impl<'a, 'b, T> $traitName<&'b T> for &'a $structName where T: Trig { type Output = $structName; fn $funcName(self, scalar: &'b T) -> $structName { $structName::new(self.scalar.$funcName(*scalar), self.vector.$funcName(*scalar)) } } } } // Implement the Quaternion's methods. impl Quaternion where T: Trig { /// Create a new Quaternion from a given scalar and Vector. /// ///``` /// // Create a new Quaternion where all /// // the components are 5.5f64. /// use sigils::vector::*; /// use sigils::quaternion::*; /// /// let quaternion = /// Quaternion::::new(5.5f64, Vector3::new(5.5f64, 5.5f64, 5.5f64)); /// # assert_eq!(quaternion.scalar, 5.5f64); /// # assert_eq!(quaternion.vector.x, 5.5f64); /// # assert_eq!(quaternion.vector.y, 5.5f64); /// # assert_eq!(quaternion.vector.z, 5.5f64); ///``` pub fn new(s: T, v: Vector3) -> Quaternion { Quaternion {scalar: s, vector: v} } /// Create a new Quaternion from the given values. /// ///``` /// // Create a new Quaternion where all /// // the components are 5.5f32. /// use sigils::quaternion::*; /// /// let quaternion = /// Quaternion::::from_values(5.5f32, 5.5f32, 5.5f32, 5.5f32); /// # assert_eq!(quaternion.scalar, 5.5f32); /// # assert_eq!(quaternion.vector.x, 5.5f32); /// # assert_eq!(quaternion.vector.y, 5.5f32); /// # assert_eq!(quaternion.vector.z, 5.5f32); ///``` pub fn from_values(s: T, xi: T, yj: T, zk: T) -> Quaternion { Quaternion::new(s, Vector3::new(xi, yj, zk)) } /// Create an identity Quaternion. This is a Quaternion /// with a zero Vector and a scalar of one. /// ///``` /// // Create a new identity Quaternion. /// use sigils::quaternion::*; /// /// let quaternion = /// Quaternion::::identity(); /// # assert_eq!(quaternion.scalar, 1.0f32); /// # assert_eq!(quaternion.vector.x, 0.0f32); /// # assert_eq!(quaternion.vector.y, 0.0f32); /// # assert_eq!(quaternion.vector.z, 0.0f32); ///``` pub fn identity() -> Quaternion { Quaternion::new(T::one(), Vector3::zero()) } /// Get the squared magnitude of the Quaternion. /// This is basically a length equation. /// /// S^2 + V^2 /// ///``` /// // Create a new Quaternion where all /// // the components are 5.5f64 and get its /// // squared magnitude. /// use sigils::vector::*; /// use sigils::quaternion::*; /// /// let quaternion = /// Quaternion::::from_values(5.5f64, 5.5f64, 5.5f64, 5.5f64); /// let magnitude = quaternion.get_magnitude_squared(); /// # assert_eq!(magnitude, 121.0f64); ///``` pub fn get_magnitude_squared(&self) -> T { (self.scalar * self.scalar) + self.vector.get_length_squared() } /// Get the magnitude of the Quaternion. /// This is basically a length equation. /// ///``` /// // Create a new Quaternion where all /// // the components are 5.5f32 and get its /// // magnitude. /// use sigils::quaternion::*; /// /// let quaternion = /// Quaternion::::from_values(5.5f32, 5.5f32, 5.5f32, 5.5f32); /// let magnitude = quaternion.get_magnitude(); /// # assert_eq!(magnitude, 11.0f32); ///``` pub fn get_magnitude(&self) -> T { let magnitude: T; // Calculate the magnitude for the Quaternion // and make sure the magnitude is actually // a number. magnitude = self.get_magnitude_squared().sqrt(); assert!(magnitude.is_finite()); // Return the calculated magnitude. magnitude } /// Set this Quaternion to the identity Quaternion. /// This is a Quaternion with a zero Vector and /// a scalar of one. pub fn set_identity(&mut self) { self.scalar = T::one(); self.vector = Vector3::zero(); } /// Negates only the Vector component. pub fn conjugate_self(&mut self) { self.vector = -self.vector; } /// Normalizes this Quaternions values. /// /// [S/magnitude, V/magnitude] pub fn normalize_self(&mut self) { let inverse_mag: T; // Get the inverse of the magnitude. inverse_mag = T::one() / self.get_magnitude(); // Multily the scalar and Vector components // by the inverse magnitude. self.scalar = self.scalar * inverse_mag; self.vector = self.vector * inverse_mag; } /// Invert this Quaternion. pub fn invert_self(&mut self) { // Conjugate this Quaternion // and then normalize it. self.conjugate_self(); self.normalize_self(); } /// Create a new Quaternion that is the same /// as this Quaternion with a negated Vector component. pub fn conjugate(&self) -> Quaternion { // Create a new Quaternion that has // the same scalar component value and // a negated version of the Vector component. Quaternion::new(self.scalar.clone(), -self.vector.clone()) } /// Create a new Quaternion that is a copy of this /// Quaternion with normalized values. /// /// [S/magnitude, V/magnitude] pub fn normalize(&self) -> Quaternion { let inverse_mag: T; inverse_mag = T::one() / self.get_magnitude(); self * inverse_mag } /// Create a new Quaternion that is a normalized /// conjugate of this Quaternion. pub fn invert(&self) -> Quaternion { self.conjugate().normalize() } // TODO: Add binary operations for editing self. } // Implement the Debug trait for the Quaternion. impl Debug for Quaternion where T: Trig { fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { write!(formatter, "[{:?}, {:?}]", self.scalar, self.vector) } } // Implement the Display trait for the Quaternion. impl Display for Quaternion where T: Trig { fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { write!(formatter, "[{}, {}]", self.scalar, self.vector) } } // Implement a default value for Quaternions which // returns the identity quaternion. impl Default for Quaternion where T: Trig { fn default() -> Quaternion { Quaternion::identity() } } // Implement the Zero and One traits for the Quaternion. impl Zero for Quaternion where T: Trig { fn zero() -> Quaternion { Quaternion::new(T::zero(), Vector3::zero()) } } impl One for Quaternion where T: Trig { fn one() -> Quaternion { Quaternion::identity() } } // Implement the binary operations for Quaternions and scalars. binary_operator_impl!(Mul::mul, Quaternion); //binary_operator_impl!(Div::div, Quaternion); //binary_operator_impl!(Neg::neg, Quaternion); // Implement Negating a Quaternion. impl Neg for Quaternion where T: Trig { type Output = Quaternion; fn neg(self) -> Quaternion { Quaternion::new(-self.scalar, -self.vector) } } impl<'a, T> Neg for &'a Quaternion where T: Trig { type Output = Quaternion; fn neg(self) -> Quaternion { Quaternion::new(-self.scalar, -self.vector) } } // Implement Adding Quaternions. impl Add> for Quaternion where T: Trig { type Output = Quaternion; fn add(self, _rhs: Quaternion) -> Quaternion { Quaternion::new(self.scalar + _rhs.scalar, self.vector + _rhs.vector) } } impl<'a, T> Add> for &'a Quaternion where T: Trig { type Output = Quaternion; fn add(self, _rhs: Quaternion) -> Quaternion { Quaternion::new(self.scalar + _rhs.scalar, self.vector + _rhs.vector) } } impl<'a, T> Add<&'a Quaternion> for Quaternion where T: Trig { type Output = Quaternion; fn add(self, _rhs: &'a Quaternion) -> Quaternion { Quaternion::new(self.scalar + _rhs.scalar, self.vector + _rhs.vector) } } impl<'a, 'b, T> Add<&'b Quaternion> for &'a Quaternion where T: Trig { type Output = Quaternion; fn add(self, _rhs: &'b Quaternion) -> Quaternion { Quaternion::new(self.scalar + _rhs.scalar, self.vector + _rhs.vector) } } // Implement subtracting Quaternions. impl Sub> for Quaternion where T: Trig { type Output = Quaternion; fn sub(self, _rhs: Quaternion) -> Quaternion { Quaternion::new(self.scalar - _rhs.scalar, self.vector - _rhs.vector) } } impl<'a, T> Sub> for &'a Quaternion where T: Trig { type Output = Quaternion; fn sub(self, _rhs: Quaternion) -> Quaternion { Quaternion::new(self.scalar - _rhs.scalar, self.vector - _rhs.vector) } } impl<'a, T> Sub<&'a Quaternion> for Quaternion where T: Trig { type Output = Quaternion; fn sub(self, _rhs: &'a Quaternion) -> Quaternion { Quaternion::new(self.scalar - _rhs.scalar, self.vector - _rhs.vector) } } impl<'a, 'b, T> Sub<&'b Quaternion> for &'a Quaternion where T: Trig { type Output = Quaternion; fn sub(self, _rhs: &'b Quaternion) -> Quaternion { Quaternion::new(self.scalar - _rhs.scalar, self.vector - _rhs.vector) } } // Implement Dividing Quaternions. impl Div> for Quaternion where T: Trig { type Output = Quaternion; fn div(self, _rhs: Quaternion) -> Quaternion { self * _rhs.invert() } } impl<'a, T> Div> for &'a Quaternion where T: Trig { type Output = Quaternion; fn div(self, _rhs: Quaternion) -> Quaternion { self * _rhs.invert() } } impl<'a, T> Div<&'a Quaternion> for Quaternion where T: Trig { type Output = Quaternion; fn div(self, _rhs: &'a Quaternion) -> Quaternion { self * _rhs.invert() } } impl<'a, 'b, T> Div<&'b Quaternion> for &'a Quaternion where T: Trig { type Output = Quaternion; fn div(self, _rhs: &'b Quaternion) -> Quaternion { self * _rhs.invert() } } // Implement multiplying Quaternions and Vectors. impl Mul> for Quaternion where T: Trig { type Output = Quaternion; fn mul(self, _rhs: Quaternion) -> Quaternion { multiply_quaternions(self.scalar, self.vector.x, self.vector.y, self.vector.z, _rhs.scalar, _rhs.vector.x, _rhs.vector.y, _rhs.vector.z) } } impl<'a, T> Mul> for &'a Quaternion where T: Trig { type Output = Quaternion; fn mul(self, _rhs: Quaternion) -> Quaternion { multiply_quaternions(self.scalar, self.vector.x, self.vector.y, self.vector.z, _rhs.scalar, _rhs.vector.x, _rhs.vector.y, _rhs.vector.z) } } impl<'a, T> Mul<&'a Quaternion> for Quaternion where T: Trig { type Output = Quaternion; fn mul(self, _rhs: &'a Quaternion) -> Quaternion { multiply_quaternions(self.scalar, self.vector.x, self.vector.y, self.vector.z, _rhs.scalar, _rhs.vector.x, _rhs.vector.y, _rhs.vector.z) } } impl<'a, 'b, T> Mul<&'b Quaternion> for &'a Quaternion where T: Trig { type Output = Quaternion; fn mul(self, _rhs: &'b Quaternion) -> Quaternion { multiply_quaternions(self.scalar, self.vector.x, self.vector.y, self.vector.z, _rhs.scalar, _rhs.vector.x, _rhs.vector.y, _rhs.vector.z) } } /// This is a private function for multiplying the /// values of two Quaternions. This was created to /// follow the DRY principle. /// /// Qa = [Sa, A] = [Sa, Xai + Yaj + Zak] /// Qb = [Sb, B] = [Sb, Xbi + Ybj + Zbk] /// /// QaQb = [Sa, A][Sb, B] = [Sa, Xai + Yaj + Zak][Sb, Xbi + Ybj + Zbk] /// QaQb = (SaSb - XaXb - YaYb - ZaZb) + /// (SaXb + XaSb + YaZb - ZaYb)i + /// (SaYb - XaZb + SbYa + ZaXb)j + /// (SaZb + XaYb - YaXb + SbZa)k fn multiply_quaternions(sa: T, xa: T, ya: T, za: T, sb: T, xb: T, yb: T, zb: T) -> Quaternion where T: Trig { let i: T; let j: T; let k: T; let scalar: T; i = (sa * xb) + (xa * sb) + (ya * zb) - (za * yb); j = (sa * yb) - (xa * zb) + (ya * sb) + (za * xb); k = (sa * zb) + (xa * yb) - (ya * xb) + (za * sb); scalar = (sa * sb) - (xa * xb) - (ya * yb) - (za * zb); Quaternion::new(scalar, Vector3::new(i, j, k)) }