// Based on Ken Shoemake. 1994. Euler angle conversion. Graphics gems IV. Academic Press // Professional, Inc., USA, 222–229. use crate::{DMat3, DMat4, DQuat, DVec3, Mat3, Mat3A, Mat4, Quat, Vec3, Vec3A, Vec3Swizzles}; /// Euler rotation sequences. /// /// The three elemental rotations may be extrinsic (rotations about the axes xyz of the original /// coordinate system, which is assumed to remain motionless), or intrinsic(rotations about the /// axes of the rotating coordinate system XYZ, solidary with the moving body, which changes its /// orientation after each elemental rotation). /// /// ``` /// # use glam::{EulerRot, Mat3}; /// # let i = core::f32::consts::FRAC_PI_2; /// # let j = core::f32::consts::FRAC_PI_4; /// # let k = core::f32::consts::FRAC_PI_8; /// let m_intrinsic = Mat3::from_rotation_x(i) * Mat3::from_rotation_y(j) * Mat3::from_rotation_z(k); /// let n_intrinsic = Mat3::from_euler(EulerRot::XYZ, i, j, k); /// assert!(m_intrinsic.abs_diff_eq(n_intrinsic, 2e-6)); /// /// let m_extrinsic = Mat3::from_rotation_z(k) * Mat3::from_rotation_y(j) * Mat3::from_rotation_x(i); /// let n_extrinsic = Mat3::from_euler(EulerRot::XYZEx, i, j, k); /// assert!(m_extrinsic.abs_diff_eq(n_extrinsic, 2e-6)); /// ``` /// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum EulerRot { /// Intrinsic three-axis rotation ZYX ZYX, /// Intrinsic three-axis rotation ZXY ZXY, /// Intrinsic three-axis rotation YXZ YXZ, /// Intrinsic three-axis rotation YZX YZX, /// Intrinsic three-axis rotation XYZ XYZ, /// Intrinsic three-axis rotation XZY XZY, /// Intrinsic two-axis rotation ZYZ ZYZ, /// Intrinsic two-axis rotation ZXZ ZXZ, /// Intrinsic two-axis rotation YXY YXY, /// Intrinsic two-axis rotation YZY YZY, /// Intrinsic two-axis rotation XYX XYX, /// Intrinsic two-axis rotation XZX XZX, /// Extrinsic three-axis rotation ZYX ZYXEx, /// Extrinsic three-axis rotation ZXY ZXYEx, /// Extrinsic three-axis rotation YXZ YXZEx, /// Extrinsic three-axis rotation YZX YZXEx, /// Extrinsic three-axis rotation XYZ XYZEx, /// Extrinsic three-axis rotation XZY XZYEx, /// Extrinsic two-axis rotation ZYZ ZYZEx, /// Extrinsic two-axis rotation ZXZ ZXZEx, /// Extrinsic two-axis rotation YXY YXYEx, /// Extrinsic two-axis rotation YZY YZYEx, /// Extrinsic two-axis rotation XYX XYXEx, /// Extrinsic two-axis rotation XZX XZXEx, } impl Default for EulerRot { /// Default `YXZ` as yaw (y-axis), pitch (x-axis), roll (z-axis). fn default() -> Self { Self::YXZ } } pub(crate) trait ToEuler { type Scalar; fn to_euler_angles(self, order: EulerRot) -> (Self::Scalar, Self::Scalar, Self::Scalar); } pub(crate) trait FromEuler { type Scalar; fn from_euler_angles( order: EulerRot, i: Self::Scalar, j: Self::Scalar, k: Self::Scalar, ) -> Self; } #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum Axis { X = 0, Y = 1, Z = 2, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum Parity { Odd = 0, Even = 1, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum Repeated { No = 0, Yes = 1, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum Frame { Relative = 0, Static = 1, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] struct Order { initial_axis: Axis, parity_even: bool, initial_repeated: bool, frame_static: bool, } impl Order { const fn new( initial_axis: Axis, parity: Parity, initial_repeated: Repeated, frame: Frame, ) -> Self { Self { initial_axis, parity_even: parity as u32 == Parity::Even as u32, initial_repeated: initial_repeated as u32 == Repeated::Yes as u32, frame_static: frame as u32 == Frame::Static as u32, } } const fn from_euler(euler: EulerRot) -> Self { match euler { EulerRot::XYZ => Self::new(Axis::X, Parity::Even, Repeated::No, Frame::Static), EulerRot::XYX => Self::new(Axis::X, Parity::Even, Repeated::Yes, Frame::Static), EulerRot::XZY => Self::new(Axis::X, Parity::Odd, Repeated::No, Frame::Static), EulerRot::XZX => Self::new(Axis::X, Parity::Odd, Repeated::Yes, Frame::Static), EulerRot::YZX => Self::new(Axis::Y, Parity::Even, Repeated::No, Frame::Static), EulerRot::YZY => Self::new(Axis::Y, Parity::Even, Repeated::Yes, Frame::Static), EulerRot::YXZ => Self::new(Axis::Y, Parity::Odd, Repeated::No, Frame::Static), EulerRot::YXY => Self::new(Axis::Y, Parity::Odd, Repeated::Yes, Frame::Static), EulerRot::ZXY => Self::new(Axis::Z, Parity::Even, Repeated::No, Frame::Static), EulerRot::ZXZ => Self::new(Axis::Z, Parity::Even, Repeated::Yes, Frame::Static), EulerRot::ZYX => Self::new(Axis::Z, Parity::Odd, Repeated::No, Frame::Static), EulerRot::ZYZ => Self::new(Axis::Z, Parity::Odd, Repeated::Yes, Frame::Static), EulerRot::ZYXEx => Self::new(Axis::X, Parity::Even, Repeated::No, Frame::Relative), EulerRot::XYXEx => Self::new(Axis::X, Parity::Even, Repeated::Yes, Frame::Relative), EulerRot::YZXEx => Self::new(Axis::X, Parity::Odd, Repeated::No, Frame::Relative), EulerRot::XZXEx => Self::new(Axis::X, Parity::Odd, Repeated::Yes, Frame::Relative), EulerRot::XZYEx => Self::new(Axis::Y, Parity::Even, Repeated::No, Frame::Relative), EulerRot::YZYEx => Self::new(Axis::Y, Parity::Even, Repeated::Yes, Frame::Relative), EulerRot::ZXYEx => Self::new(Axis::Y, Parity::Odd, Repeated::No, Frame::Relative), EulerRot::YXYEx => Self::new(Axis::Y, Parity::Odd, Repeated::Yes, Frame::Relative), EulerRot::YXZEx => Self::new(Axis::Z, Parity::Even, Repeated::No, Frame::Relative), EulerRot::ZXZEx => Self::new(Axis::Z, Parity::Even, Repeated::Yes, Frame::Relative), EulerRot::XYZEx => Self::new(Axis::Z, Parity::Odd, Repeated::No, Frame::Relative), EulerRot::ZYZEx => Self::new(Axis::Z, Parity::Odd, Repeated::Yes, Frame::Relative), } } const fn next_axis(i: usize) -> usize { (i + 1) % 3 } const fn prev_axis(i: usize) -> usize { if i > 0 { i - 1 } else { 2 } } const fn angle_order(self) -> (usize, usize, usize) { let i = self.initial_axis as usize; let j = if self.parity_even { Order::next_axis(i) } else { Order::prev_axis(i) }; let k = if self.parity_even { Order::prev_axis(i) } else { Order::next_axis(i) }; (i, j, k) } } macro_rules! impl_mat3_from_euler { ($scalar:ident, $mat3:ident, $vec3:ident) => { impl FromEuler for $mat3 { type Scalar = $scalar; fn from_euler_angles( euler: EulerRot, x: Self::Scalar, y: Self::Scalar, z: Self::Scalar, ) -> Self { use crate::$scalar::math; let order = Order::from_euler(euler); let (i, j, k) = order.angle_order(); let mut angles = if order.frame_static { $vec3::new(x, y, z) } else { $vec3::new(z, y, x) }; // rotation direction is reverse from original paper if order.parity_even { angles = -angles; } let (si, ci) = math::sin_cos(angles.x); let (sj, cj) = math::sin_cos(angles.y); let (sh, ch) = math::sin_cos(angles.z); let cc = ci * ch; let cs = ci * sh; let sc = si * ch; let ss = si * sh; let mut m = [[0.0; 3]; 3]; if order.initial_repeated { m[i][i] = cj; m[i][j] = sj * si; m[i][k] = sj * ci; m[j][i] = sj * sh; m[j][j] = -cj * ss + cc; m[j][k] = -cj * cs - sc; m[k][i] = -sj * ch; m[k][j] = cj * sc + cs; m[k][k] = cj * cc - ss; } else { m[i][i] = cj * ch; m[i][j] = sj * sc - cs; m[i][k] = sj * cc + ss; m[j][i] = cj * sh; m[j][j] = sj * ss + cc; m[j][k] = sj * cs - sc; m[k][i] = -sj; m[k][j] = cj * si; m[k][k] = cj * ci; } $mat3::from_cols_array_2d(&m) } } }; } macro_rules! impl_mat4_from_euler { ($scalar:ident, $mat4:ident, $mat3:ident) => { impl FromEuler for $mat4 { type Scalar = $scalar; fn from_euler_angles( euler: EulerRot, x: Self::Scalar, y: Self::Scalar, z: Self::Scalar, ) -> Self { $mat4::from_mat3($mat3::from_euler_angles(euler, x, y, z)) } } }; } macro_rules! impl_quat_from_euler { ($scalar:ident, $quat:ident, $vec3:ident) => { impl FromEuler for $quat { type Scalar = $scalar; fn from_euler_angles( euler: EulerRot, x: Self::Scalar, y: Self::Scalar, z: Self::Scalar, ) -> Self { use crate::$scalar::math; let order = Order::from_euler(euler); let (i, j, k) = order.angle_order(); let mut angles = if order.frame_static { $vec3::new(x, y, z) } else { $vec3::new(z, y, x) }; if order.parity_even { angles.y = -angles.y; } let ti = angles.x * 0.5; let tj = angles.y * 0.5; let th = angles.z * 0.5; let (si, ci) = math::sin_cos(ti); let (sj, cj) = math::sin_cos(tj); let (sh, ch) = math::sin_cos(th); let cc = ci * ch; let cs = ci * sh; let sc = si * ch; let ss = si * sh; let parity = if !order.parity_even { 1.0 } else { -1.0 }; let mut a = [0.0; 4]; if order.initial_repeated { a[i] = cj * (cs + sc); a[j] = sj * (cc + ss) * parity; a[k] = sj * (cs - sc); a[3] = cj * (cc - ss); } else { a[i] = cj * sc - sj * cs; a[j] = (cj * ss + sj * cc) * parity; a[k] = cj * cs - sj * sc; a[3] = cj * cc + sj * ss; } $quat::from_array(a) } } }; } macro_rules! impl_mat3_to_euler { ($scalar:ident, $mat3:ident, $vec3:ident) => { impl ToEuler for $mat3 { type Scalar = $scalar; fn to_euler_angles( self, euler: EulerRot, ) -> (Self::Scalar, Self::Scalar, Self::Scalar) { use crate::$scalar::math; let order = Order::from_euler(euler); let (i, j, k) = order.angle_order(); let mut ea = $vec3::ZERO; if order.initial_repeated { let sy = math::sqrt( self.col(i)[j] * self.col(i)[j] + self.col(i)[k] * self.col(i)[k], ); if (sy > 16.0 * $scalar::EPSILON) { ea.x = math::atan2(self.col(i)[j], self.col(i)[k]); ea.y = math::atan2(sy, self.col(i)[i]); ea.z = math::atan2(self.col(j)[i], -self.col(k)[i]); } else { ea.x = math::atan2(-self.col(j)[k], self.col(j)[j]); ea.y = math::atan2(sy, self.col(i)[i]); } } else { let cy = math::sqrt( self.col(i)[i] * self.col(i)[i] + self.col(j)[i] * self.col(j)[i], ); if (cy > 16.0 * $scalar::EPSILON) { ea.x = math::atan2(self.col(k)[j], self.col(k)[k]); ea.y = math::atan2(-self.col(k)[i], cy); ea.z = math::atan2(self.col(j)[i], self.col(i)[i]); } else { ea.x = math::atan2(-self.col(j)[k], self.col(j)[j]); ea.y = math::atan2(-self.col(k)[i], cy); } } // reverse rotation angle of original code if order.parity_even { ea = -ea; } if !order.frame_static { ea = ea.zyx(); } (ea.x, ea.y, ea.z) } } }; } macro_rules! impl_mat4_to_euler { ($scalar:ident, $mat4:ident, $mat3:ident) => { impl ToEuler for $mat4 { type Scalar = $scalar; fn to_euler_angles( self, order: EulerRot, ) -> (Self::Scalar, Self::Scalar, Self::Scalar) { $mat3::from_mat4(self).to_euler_angles(order) } } }; } macro_rules! impl_quat_to_euler { ($scalar:ident, $quat:ident, $mat3:ident) => { impl ToEuler for $quat { type Scalar = $scalar; fn to_euler_angles( self, order: EulerRot, ) -> (Self::Scalar, Self::Scalar, Self::Scalar) { $mat3::from_quat(self).to_euler_angles(order) } } }; } impl_mat3_to_euler!(f32, Mat3, Vec3); impl_mat3_from_euler!(f32, Mat3, Vec3); impl_mat3_to_euler!(f32, Mat3A, Vec3A); impl_mat3_from_euler!(f32, Mat3A, Vec3A); impl_mat4_from_euler!(f32, Mat4, Mat3); impl_mat4_to_euler!(f32, Mat4, Mat3); impl_quat_to_euler!(f32, Quat, Mat3); impl_quat_from_euler!(f32, Quat, Vec3); impl_mat3_to_euler!(f64, DMat3, DVec3); impl_mat3_from_euler!(f64, DMat3, DVec3); impl_mat4_to_euler!(f64, DMat4, DMat3); impl_mat4_from_euler!(f64, DMat4, DMat3); impl_quat_to_euler!(f64, DQuat, DMat3); impl_quat_from_euler!(f64, DQuat, DVec3);