#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use rand::{Rand, Rng}; use num::{Zero, One}; use alga::general::Real; use core::{Unit, ColumnVector, Vector3}; use core::storage::{Storage, OwnedStorage}; use core::allocator::{Allocator, OwnedAllocator}; use core::dimension::{U1, U3, U4}; use geometry::{QuaternionBase, UnitQuaternionBase, RotationBase, OwnedRotation}; impl QuaternionBase where N: Real, S: Storage { /// Creates a quaternion from a 4D vector. The quaternion scalar part corresponds to the `w` /// vector component. #[inline] pub fn from_vector(vector: ColumnVector) -> Self { QuaternionBase { coords: vector } } } impl QuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator { /// Creates a new quaternion from its individual components. Note that the arguments order does /// **not** follow the storage order. /// /// The storage order is [ x, y, z, w ]. #[inline] pub fn new(w: N, x: N, y: N, z: N) -> Self { let v = ColumnVector::::new(x, y, z, w); Self::from_vector(v) } /// Creates a new quaternion from its scalar and vector parts. Note that the arguments order does /// **not** follow the storage order. /// /// The storage order is [ vector, scalar ]. #[inline] // FIXME: take a reference to `vector`? pub fn from_parts(scalar: N, vector: ColumnVector) -> Self where SB: Storage { Self::new(scalar, vector[0], vector[1], vector[2]) } /// Creates a new quaternion from its polar decomposition. /// /// Note that `axis` is assumed to be a unit vector. // FIXME: take a reference to `axis`? pub fn from_polar_decomposition(scale: N, theta: N, axis: Unit>) -> Self where SB: Storage { let rot = UnitQuaternionBase::::from_axis_angle(&axis, theta * ::convert(2.0f64)); rot.unwrap() * scale } /// The quaternion multiplicative identity. #[inline] pub fn identity() -> Self { Self::new(N::one(), N::zero(), N::zero(), N::zero()) } } impl One for QuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator { #[inline] fn one() -> Self { Self::identity() } } impl Zero for QuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator { #[inline] fn zero() -> Self { Self::new(N::zero(), N::zero(), N::zero(), N::zero()) } #[inline] fn is_zero(&self) -> bool { self.coords.is_zero() } } impl Rand for QuaternionBase where N: Real + Rand, S: OwnedStorage, S::Alloc: OwnedAllocator { #[inline] fn rand(rng: &mut R) -> Self { QuaternionBase::new(rng.gen(), rng.gen(), rng.gen(), rng.gen()) } } #[cfg(feature="arbitrary")] impl Arbitrary for QuaternionBase where N: Real + Arbitrary, S: OwnedStorage + Send, S::Alloc: OwnedAllocator { #[inline] fn arbitrary(g: &mut G) -> Self { QuaternionBase::new(N::arbitrary(g), N::arbitrary(g), N::arbitrary(g), N::arbitrary(g)) } } impl UnitQuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator { /// The quaternion multiplicative identity. #[inline] pub fn identity() -> Self { Self::new_unchecked(QuaternionBase::identity()) } /// Creates a new quaternion from a unit vector (the rotation axis) and an angle /// (the rotation angle). #[inline] pub fn from_axis_angle(axis: &Unit>, angle: N) -> Self where SB: Storage { let (sang, cang) = (angle / ::convert(2.0f64)).sin_cos(); let q = QuaternionBase::from_parts(cang, axis.as_ref() * sang); Self::new_unchecked(q) } /// Creates a new unit quaternion from a quaternion. /// /// The input quaternion will be normalized. #[inline] pub fn from_quaternion(q: QuaternionBase) -> Self { Self::new_normalize(q) } /// Creates a new unit quaternion from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. #[inline] pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self { let (sr, cr) = (roll * ::convert(0.5f64)).sin_cos(); let (sp, cp) = (pitch * ::convert(0.5f64)).sin_cos(); let (sy, cy) = (yaw * ::convert(0.5f64)).sin_cos(); let q = QuaternionBase::new( cr * cp * cy + sr * sp * sy, sr * cp * cy - cr * sp * sy, cr * sp * cy + sr * cp * sy, cr * cp * sy - sr * sp * cy); Self::new_unchecked(q) } /// Builds an unit quaternion from a rotation matrix. #[inline] pub fn from_rotation_matrix(rotmat: &RotationBase) -> Self where SB: Storage, SB::Alloc: Allocator { let angle = rotmat.angle(); if let Some(axis) = rotmat.axis() { Self::from_axis_angle(&axis, angle) } else if angle > ::convert(1.0f64) { // The angle is 3.14. -Self::identity() } else { // The angle is 0. Self::identity() } } /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. #[inline] pub fn rotation_between(a: &ColumnVector, b: &ColumnVector) -> Option 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: &ColumnVector, b: &ColumnVector, s: N) -> Option where SB: Storage, SC: Storage { // FIXME: code duplication with RotationBase. if let (Some(na), Some(nb)) = (a.try_normalize(N::zero()), b.try_normalize(N::zero())) { let c = na.cross(&nb); if let Some(axis) = Unit::try_new(c, N::default_epsilon()) { return Some(Self::from_axis_angle(&axis, na.dot(&nb).acos() * s)) } // Zero or PI. if na.dot(&nb) < N::zero() { // PI // // The rotation axis is undefined but the angle not zero. This is not a // simple rotation. return None; } } Some(Self::identity()) } /// Creates an unit quaternion that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// /// It maps the view direction `dir` to the positive `z` axis. /// /// # Arguments /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with. /// * up - The vertical direction. The only requirement of this parameter is to not be /// collinear /// to `dir`. Non-collinearity is not checked. #[inline] pub fn new_observer_frame(dir: &ColumnVector, up: &ColumnVector) -> Self where SB: Storage, SC: Storage, S::Alloc: Allocator + Allocator { Self::from_rotation_matrix(&OwnedRotation::::new_observer_frame(dir, up)) } /// Builds a right-handed look-at view matrix without translation. /// /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. #[inline] pub fn look_at_rh(dir: &ColumnVector, up: &ColumnVector) -> Self where SB: Storage, SC: Storage, S::Alloc: Allocator + Allocator { Self::new_observer_frame(&-dir, up).inverse() } /// Builds a left-handed look-at view matrix without translation. /// /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. #[inline] pub fn look_at_lh(dir: &ColumnVector, up: &ColumnVector) -> Self where SB: Storage, SC: Storage, S::Alloc: Allocator + Allocator { Self::new_observer_frame(dir, up).inverse() } } impl UnitQuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator + Allocator { /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` is zero, this returns the indentity rotation. #[inline] pub fn new(axisangle: ColumnVector) -> Self where SB: Storage { let two: N = ::convert(2.0f64); let q = QuaternionBase::::from_parts(N::zero(), axisangle / two).exp(); Self::new_unchecked(q) } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` is zero, this returns the indentity rotation. /// Same as `Self::new(axisangle)`. #[inline] pub fn from_scaled_axis(axisangle: ColumnVector) -> Self where SB: Storage { Self::new(axisangle) } } impl One for UnitQuaternionBase where N: Real, S: OwnedStorage, S::Alloc: OwnedAllocator { #[inline] fn one() -> Self { Self::identity() } } impl Rand for UnitQuaternionBase where N: Real + Rand, S: OwnedStorage, S::Alloc: OwnedAllocator + Allocator { #[inline] fn rand(rng: &mut R) -> Self { let axisangle = Vector3::rand(rng); UnitQuaternionBase::from_scaled_axis(axisangle) } } #[cfg(feature="arbitrary")] impl Arbitrary for UnitQuaternionBase where N: Real + Arbitrary, S: OwnedStorage + Send, S::Alloc: OwnedAllocator + Allocator { #[inline] fn arbitrary(g: &mut G) -> Self { let axisangle = Vector3::arbitrary(g); UnitQuaternionBase::from_scaled_axis(axisangle) } }