use num_traits::cast;
#[cfg(feature = "rand")]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
use structure::*;
use angle::Rad;
use approx;
#[cfg(feature = "mint")]
use mint;
use num::BaseFloat;
use quaternion::Quaternion;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Euler<A> {
pub x: A,
pub y: A,
pub z: A,
}
impl<A> Euler<A> {
pub const fn new(x: A, y: A, z: A) -> Euler<A> {
Euler { x: x, y: y, z: z }
}
}
impl<S: BaseFloat> From<Quaternion<S>> for Euler<Rad<S>> {
fn from(src: Quaternion<S>) -> Euler<Rad<S>> {
let sig: S = cast(0.499).unwrap();
let two: S = cast(2).unwrap();
let one: S = cast(1).unwrap();
let (qw, qx, qy, qz) = (src.s, src.v.x, src.v.y, src.v.z);
let (sqw, sqx, sqy, sqz) = (qw * qw, qx * qx, qy * qy, qz * qz);
let unit = sqx + sqz + sqy + sqw;
let test = qx * qz + qy * qw;
if test > sig * unit {
Euler {
x: Rad::zero(),
y: Rad::turn_div_4(),
z: Rad::atan2(qx, qw) * two,
}
} else if test < -sig * unit {
Euler {
x: Rad::zero(),
y: -Rad::turn_div_4(),
z: -Rad::atan2(qx, qw) * two,
}
} else {
Euler {
x: Rad::atan2(two * (-qy * qz + qx * qw), one - two * (sqx + sqy)),
y: Rad::asin(two * (qx * qz + qy * qw)),
z: Rad::atan2(two * (-qx * qy + qz * qw), one - two * (sqy + sqz)),
}
}
}
}
impl<A: Angle> approx::AbsDiffEq for Euler<A> {
type Epsilon = A::Epsilon;
#[inline]
fn default_epsilon() -> A::Epsilon {
A::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: A::Epsilon) -> bool {
A::abs_diff_eq(&self.x, &other.x, epsilon)
&& A::abs_diff_eq(&self.y, &other.y, epsilon)
&& A::abs_diff_eq(&self.z, &other.z, epsilon)
}
}
impl<A: Angle> approx::RelativeEq for Euler<A> {
#[inline]
fn default_max_relative() -> A::Epsilon {
A::default_max_relative()
}
#[inline]
fn relative_eq(&self, other: &Self, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
A::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& A::relative_eq(&self.y, &other.y, epsilon, max_relative)
&& A::relative_eq(&self.z, &other.z, epsilon, max_relative)
}
}
impl<A: Angle> approx::UlpsEq for Euler<A> {
#[inline]
fn default_max_ulps() -> u32 {
A::default_max_ulps()
}
#[inline]
fn ulps_eq(&self, other: &Self, epsilon: A::Epsilon, max_ulps: u32) -> bool {
A::ulps_eq(&self.x, &other.x, epsilon, max_ulps)
&& A::ulps_eq(&self.y, &other.y, epsilon, max_ulps)
&& A::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
}
}
#[cfg(feature = "rand")]
impl<A> Distribution<Euler<A>> for Standard
where
Standard: Distribution<A>,
A: Angle,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Euler<A> {
Euler {
x: rng.gen(),
y: rng.gen(),
z: rng.gen(),
}
}
}
#[cfg(feature = "mint")]
type MintEuler<S> = mint::EulerAngles<S, mint::IntraXYZ>;
#[cfg(feature = "mint")]
impl<S, A: Angle + From<S>> From<MintEuler<S>> for Euler<A> {
fn from(mint: MintEuler<S>) -> Self {
Euler {
x: mint.a.into(),
y: mint.b.into(),
z: mint.c.into(),
}
}
}
#[cfg(feature = "mint")]
impl<S: Clone, A: Angle + Into<S>> Into<MintEuler<S>> for Euler<A> {
fn into(self) -> MintEuler<S> {
MintEuler::from([self.x.into(), self.y.into(), self.z.into()])
}
}