#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::One; use num_complex::Complex; use rand::distributions::{Distribution, Standard, OpenClosed01}; use rand::Rng; use alga::general::Real; use base::allocator::Allocator; use base::dimension::{U1, U2}; use base::storage::Storage; use base::{DefaultAllocator, Unit, Vector}; use geometry::{Rotation, UnitComplex}; impl UnitComplex { /// The unit complex number multiplicative identity. #[inline] pub fn identity() -> Self { Self::new_unchecked(Complex::new(N::one(), N::zero())) } /// Builds the unit complex number corresponding to the rotation with the angle. #[inline] pub fn new(angle: N) -> Self { let (sin, cos) = angle.sin_cos(); Self::from_cos_sin_unchecked(cos, sin) } /// Builds the unit complex number corresponding to the rotation with the angle. /// /// Same as `Self::new(angle)`. #[inline] pub fn from_angle(angle: N) -> Self { Self::new(angle) } /// Builds the unit complex number from the sinus and cosinus of the rotation angle. /// /// The input values are not checked. #[inline] pub fn from_cos_sin_unchecked(cos: N, sin: N) -> Self { UnitComplex::new_unchecked(Complex::new(cos, sin)) } /// Builds a unit complex rotation from an angle in radian wrapped in a 1-dimensional vector. /// /// Equivalent to `Self::new(axisangle[0])`. #[inline] pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::from_angle(axisangle[0]) } /// Creates a new unit complex number from a complex number. /// /// The input complex number will be normalized. #[inline] pub fn from_complex(q: Complex) -> Self { Self::from_complex_and_get(q).0 } /// Creates a new unit complex number from a complex number. /// /// The input complex number will be normalized. Returns the complex number norm as well. #[inline] pub fn from_complex_and_get(q: Complex) -> (Self, N) { let norm = (q.im * q.im + q.re * q.re).sqrt(); (Self::new_unchecked(q / norm), norm) } /// Builds the unit complex number from the corresponding 2D rotation matrix. #[inline] pub fn from_rotation_matrix(rotmat: &Rotation) -> Self where DefaultAllocator: Allocator, { Self::new_unchecked(Complex::new(rotmat[(0, 0)], rotmat[(1, 0)])) } /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Self where SB: Storage, SC: Storage, { Self::scaled_rotation_between(a, b, N::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. #[inline] pub fn scaled_rotation_between( a: &Vector, b: &Vector, s: N, ) -> Self where SB: Storage, SC: Storage, { // FIXME: code duplication with Rotation. if let (Some(na), Some(nb)) = ( Unit::try_new(a.clone_owned(), N::zero()), Unit::try_new(b.clone_owned(), N::zero()), ) { Self::scaled_rotation_between_axis(&na, &nb, s) } else { Self::identity() } } /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. #[inline] pub fn rotation_between_axis( a: &Unit>, b: &Unit>, ) -> Self where SB: Storage, SC: Storage, { Self::scaled_rotation_between_axis(a, b, N::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. #[inline] pub fn scaled_rotation_between_axis( na: &Unit>, nb: &Unit>, s: N, ) -> Self where SB: Storage, SC: Storage, { let sang = na.perp(&nb); let cang = na.dot(&nb); Self::from_angle(sang.atan2(cang) * s) } } impl One for UnitComplex { #[inline] fn one() -> Self { Self::identity() } } impl Distribution> for Standard where OpenClosed01: Distribution, { /// Generate a uniformly distributed random `UnitComplex`. #[inline] fn sample<'a, R: Rng + ?Sized>(&self, rng: &mut R) -> UnitComplex { UnitComplex::from_angle(rng.sample(OpenClosed01) * N::two_pi()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for UnitComplex { #[inline] fn arbitrary(g: &mut G) -> Self { UnitComplex::from_angle(N::arbitrary(g)) } }