Skip to main content

glam/
euler.rs

1// Based on Ken Shoemake. 1994. Euler angle conversion. Graphics gems IV.  Academic Press
2// Professional, Inc., USA, 222–229.
3#[cfg(feature = "f64")]
4use crate::{DMat3, DMat4, DQuat, DVec3};
5use crate::{Mat3, Mat3A, Mat4, Quat, Vec3, Vec3A, Vec3Swizzles};
6
7/// Euler rotation sequences.
8///
9/// The three elemental rotations may be extrinsic (rotations about the axes xyz of the original
10/// coordinate system, which is assumed to remain motionless), or intrinsic (rotations about the
11/// axes of the rotating coordinate system XYZ, solidary with the moving body, which changes its
12/// orientation after each elemental rotation).
13///
14/// ```
15/// # use glam::{EulerRot, Mat3};
16/// # let i = core::f32::consts::FRAC_PI_2;
17/// # let j = core::f32::consts::FRAC_PI_4;
18/// # let k = core::f32::consts::FRAC_PI_8;
19/// let m_intrinsic = Mat3::from_rotation_x(i) * Mat3::from_rotation_y(j) * Mat3::from_rotation_z(k);
20/// let n_intrinsic = Mat3::from_euler(EulerRot::XYZ, i, j, k);
21/// assert!(m_intrinsic.abs_diff_eq(n_intrinsic, 2e-6));
22///
23/// let m_extrinsic = Mat3::from_rotation_z(k) * Mat3::from_rotation_y(j) * Mat3::from_rotation_x(i);
24/// let n_extrinsic = Mat3::from_euler(EulerRot::XYZEx, i, j, k);
25/// assert!(m_extrinsic.abs_diff_eq(n_extrinsic, 2e-6));
26/// ```
27///
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub enum EulerRot {
30    /// Intrinsic three-axis rotation ZYX
31    ZYX,
32    /// Intrinsic three-axis rotation ZXY
33    ZXY,
34    /// Intrinsic three-axis rotation YXZ
35    YXZ,
36    /// Intrinsic three-axis rotation YZX
37    YZX,
38    /// Intrinsic three-axis rotation XYZ
39    XYZ,
40    /// Intrinsic three-axis rotation XZY
41    XZY,
42
43    /// Intrinsic two-axis rotation ZYZ
44    ZYZ,
45    /// Intrinsic two-axis rotation ZXZ
46    ZXZ,
47    /// Intrinsic two-axis rotation YXY
48    YXY,
49    /// Intrinsic two-axis rotation YZY
50    YZY,
51    /// Intrinsic two-axis rotation XYX
52    XYX,
53    /// Intrinsic two-axis rotation XZX
54    XZX,
55
56    /// Extrinsic three-axis rotation ZYX
57    ZYXEx,
58    /// Extrinsic three-axis rotation ZXY
59    ZXYEx,
60    /// Extrinsic three-axis rotation YXZ
61    YXZEx,
62    /// Extrinsic three-axis rotation YZX
63    YZXEx,
64    /// Extrinsic three-axis rotation XYZ
65    XYZEx,
66    /// Extrinsic three-axis rotation XZY
67    XZYEx,
68
69    /// Extrinsic two-axis rotation ZYZ
70    ZYZEx,
71    /// Extrinsic two-axis rotation ZXZ
72    ZXZEx,
73    /// Extrinsic two-axis rotation YXY
74    YXYEx,
75    /// Extrinsic two-axis rotation YZY
76    YZYEx,
77    /// Extrinsic two-axis rotation XYX
78    XYXEx,
79    /// Extrinsic two-axis rotation XZX
80    XZXEx,
81}
82
83impl Default for EulerRot {
84    /// Default `YXZ` as yaw (y-axis), pitch (x-axis), roll (z-axis).
85    fn default() -> Self {
86        Self::YXZ
87    }
88}
89
90pub(crate) trait ToEuler {
91    type Scalar;
92    fn to_euler_angles(self, order: EulerRot) -> (Self::Scalar, Self::Scalar, Self::Scalar);
93}
94
95pub(crate) trait FromEuler {
96    type Scalar;
97    fn from_euler_angles(
98        order: EulerRot,
99        i: Self::Scalar,
100        j: Self::Scalar,
101        k: Self::Scalar,
102    ) -> Self;
103}
104
105#[derive(Copy, Clone, Debug, PartialEq, Eq)]
106enum Axis {
107    X = 0,
108    Y = 1,
109    Z = 2,
110}
111
112#[derive(Copy, Clone, Debug, PartialEq, Eq)]
113enum Parity {
114    Odd = 0,
115    Even = 1,
116}
117
118#[derive(Copy, Clone, Debug, PartialEq, Eq)]
119enum Repeated {
120    No = 0,
121    Yes = 1,
122}
123
124#[derive(Copy, Clone, Debug, PartialEq, Eq)]
125enum Frame {
126    Relative = 0,
127    Static = 1,
128}
129
130#[derive(Copy, Clone, Debug, PartialEq, Eq)]
131struct Order {
132    initial_axis: Axis,
133    parity_even: bool,
134    initial_repeated: bool,
135    frame_static: bool,
136}
137
138impl Order {
139    const fn new(
140        initial_axis: Axis,
141        parity: Parity,
142        initial_repeated: Repeated,
143        frame: Frame,
144    ) -> Self {
145        Self {
146            initial_axis,
147            parity_even: parity as u32 == Parity::Even as u32,
148            initial_repeated: initial_repeated as u32 == Repeated::Yes as u32,
149            frame_static: frame as u32 == Frame::Static as u32,
150        }
151    }
152
153    const fn from_euler(euler: EulerRot) -> Self {
154        match euler {
155            EulerRot::XYZ => Self::new(Axis::X, Parity::Even, Repeated::No, Frame::Static),
156            EulerRot::XYX => Self::new(Axis::X, Parity::Even, Repeated::Yes, Frame::Static),
157            EulerRot::XZY => Self::new(Axis::X, Parity::Odd, Repeated::No, Frame::Static),
158            EulerRot::XZX => Self::new(Axis::X, Parity::Odd, Repeated::Yes, Frame::Static),
159            EulerRot::YZX => Self::new(Axis::Y, Parity::Even, Repeated::No, Frame::Static),
160            EulerRot::YZY => Self::new(Axis::Y, Parity::Even, Repeated::Yes, Frame::Static),
161            EulerRot::YXZ => Self::new(Axis::Y, Parity::Odd, Repeated::No, Frame::Static),
162            EulerRot::YXY => Self::new(Axis::Y, Parity::Odd, Repeated::Yes, Frame::Static),
163            EulerRot::ZXY => Self::new(Axis::Z, Parity::Even, Repeated::No, Frame::Static),
164            EulerRot::ZXZ => Self::new(Axis::Z, Parity::Even, Repeated::Yes, Frame::Static),
165            EulerRot::ZYX => Self::new(Axis::Z, Parity::Odd, Repeated::No, Frame::Static),
166            EulerRot::ZYZ => Self::new(Axis::Z, Parity::Odd, Repeated::Yes, Frame::Static),
167            EulerRot::ZYXEx => Self::new(Axis::X, Parity::Even, Repeated::No, Frame::Relative),
168            EulerRot::XYXEx => Self::new(Axis::X, Parity::Even, Repeated::Yes, Frame::Relative),
169            EulerRot::YZXEx => Self::new(Axis::X, Parity::Odd, Repeated::No, Frame::Relative),
170            EulerRot::XZXEx => Self::new(Axis::X, Parity::Odd, Repeated::Yes, Frame::Relative),
171            EulerRot::XZYEx => Self::new(Axis::Y, Parity::Even, Repeated::No, Frame::Relative),
172            EulerRot::YZYEx => Self::new(Axis::Y, Parity::Even, Repeated::Yes, Frame::Relative),
173            EulerRot::ZXYEx => Self::new(Axis::Y, Parity::Odd, Repeated::No, Frame::Relative),
174            EulerRot::YXYEx => Self::new(Axis::Y, Parity::Odd, Repeated::Yes, Frame::Relative),
175            EulerRot::YXZEx => Self::new(Axis::Z, Parity::Even, Repeated::No, Frame::Relative),
176            EulerRot::ZXZEx => Self::new(Axis::Z, Parity::Even, Repeated::Yes, Frame::Relative),
177            EulerRot::XYZEx => Self::new(Axis::Z, Parity::Odd, Repeated::No, Frame::Relative),
178            EulerRot::ZYZEx => Self::new(Axis::Z, Parity::Odd, Repeated::Yes, Frame::Relative),
179        }
180    }
181
182    const fn next_axis(i: usize) -> usize {
183        (i + 1) % 3
184    }
185
186    const fn prev_axis(i: usize) -> usize {
187        if i > 0 {
188            i - 1
189        } else {
190            2
191        }
192    }
193
194    const fn angle_order(self) -> (usize, usize, usize) {
195        let i = self.initial_axis as usize;
196        let j = if self.parity_even {
197            Self::next_axis(i)
198        } else {
199            Self::prev_axis(i)
200        };
201        let k = if self.parity_even {
202            Self::prev_axis(i)
203        } else {
204            Self::next_axis(i)
205        };
206        (i, j, k)
207    }
208}
209
210macro_rules! impl_mat3_from_euler {
211    ($scalar:ident, $mat3:ident, $vec3:ident) => {
212        impl FromEuler for $mat3 {
213            type Scalar = $scalar;
214            fn from_euler_angles(
215                euler: EulerRot,
216                x: Self::Scalar,
217                y: Self::Scalar,
218                z: Self::Scalar,
219            ) -> Self {
220                use crate::$scalar::math;
221
222                let order = Order::from_euler(euler);
223                let (i, j, k) = order.angle_order();
224
225                let mut angles = if order.frame_static {
226                    $vec3::new(x, y, z)
227                } else {
228                    $vec3::new(z, y, x)
229                };
230
231                // rotation direction is reverse from original paper
232                if order.parity_even {
233                    angles = -angles;
234                }
235
236                let (si, ci) = math::sin_cos(angles.x);
237                let (sj, cj) = math::sin_cos(angles.y);
238                let (sh, ch) = math::sin_cos(angles.z);
239
240                let cc = ci * ch;
241                let cs = ci * sh;
242                let sc = si * ch;
243                let ss = si * sh;
244
245                let mut m = [[0.0; 3]; 3];
246
247                if order.initial_repeated {
248                    m[i][i] = cj;
249                    m[i][j] = sj * si;
250                    m[i][k] = sj * ci;
251                    m[j][i] = sj * sh;
252                    m[j][j] = -cj * ss + cc;
253                    m[j][k] = -cj * cs - sc;
254                    m[k][i] = -sj * ch;
255                    m[k][j] = cj * sc + cs;
256                    m[k][k] = cj * cc - ss;
257                } else {
258                    m[i][i] = cj * ch;
259                    m[i][j] = sj * sc - cs;
260                    m[i][k] = sj * cc + ss;
261                    m[j][i] = cj * sh;
262                    m[j][j] = sj * ss + cc;
263                    m[j][k] = sj * cs - sc;
264                    m[k][i] = -sj;
265                    m[k][j] = cj * si;
266                    m[k][k] = cj * ci;
267                }
268
269                $mat3::from_cols_array_2d(&m)
270            }
271        }
272    };
273}
274
275macro_rules! impl_mat4_from_euler {
276    ($scalar:ident, $mat4:ident, $mat3:ident) => {
277        impl FromEuler for $mat4 {
278            type Scalar = $scalar;
279            fn from_euler_angles(
280                euler: EulerRot,
281                x: Self::Scalar,
282                y: Self::Scalar,
283                z: Self::Scalar,
284            ) -> Self {
285                $mat4::from_mat3($mat3::from_euler_angles(euler, x, y, z))
286            }
287        }
288    };
289}
290
291macro_rules! impl_quat_from_euler {
292    ($scalar:ident, $quat:ident, $vec3:ident) => {
293        impl FromEuler for $quat {
294            type Scalar = $scalar;
295            fn from_euler_angles(
296                euler: EulerRot,
297                x: Self::Scalar,
298                y: Self::Scalar,
299                z: Self::Scalar,
300            ) -> Self {
301                use crate::$scalar::math;
302
303                let order = Order::from_euler(euler);
304                let (i, j, k) = order.angle_order();
305
306                let mut angles = if order.frame_static {
307                    $vec3::new(x, y, z)
308                } else {
309                    $vec3::new(z, y, x)
310                };
311
312                if order.parity_even {
313                    angles.y = -angles.y;
314                }
315
316                let ti = angles.x * 0.5;
317                let tj = angles.y * 0.5;
318                let th = angles.z * 0.5;
319                let (si, ci) = math::sin_cos(ti);
320                let (sj, cj) = math::sin_cos(tj);
321                let (sh, ch) = math::sin_cos(th);
322                let cc = ci * ch;
323                let cs = ci * sh;
324                let sc = si * ch;
325                let ss = si * sh;
326
327                let parity = if !order.parity_even { 1.0 } else { -1.0 };
328
329                let mut a = [0.0; 4];
330
331                if order.initial_repeated {
332                    a[i] = cj * (cs + sc);
333                    a[j] = sj * (cc + ss) * parity;
334                    a[k] = sj * (cs - sc);
335                    a[3] = cj * (cc - ss);
336                } else {
337                    a[i] = cj * sc - sj * cs;
338                    a[j] = (cj * ss + sj * cc) * parity;
339                    a[k] = cj * cs - sj * sc;
340                    a[3] = cj * cc + sj * ss;
341                }
342
343                $quat::from_array(a)
344            }
345        }
346    };
347}
348
349macro_rules! impl_mat3_to_euler {
350    ($scalar:ident, $mat3:ident, $vec3:ident) => {
351        impl ToEuler for $mat3 {
352            type Scalar = $scalar;
353            fn to_euler_angles(
354                self,
355                euler: EulerRot,
356            ) -> (Self::Scalar, Self::Scalar, Self::Scalar) {
357                use crate::$scalar::math;
358
359                let order = Order::from_euler(euler);
360                let (i, j, k) = order.angle_order();
361
362                let mut ea = $vec3::ZERO;
363                if order.initial_repeated {
364                    let sy = math::sqrt(
365                        self.col(i)[j] * self.col(i)[j] + self.col(i)[k] * self.col(i)[k],
366                    );
367                    if (sy > 16.0 * $scalar::EPSILON) {
368                        ea.x = math::atan2(self.col(i)[j], self.col(i)[k]);
369                        ea.y = math::atan2(sy, self.col(i)[i]);
370                        ea.z = math::atan2(self.col(j)[i], -self.col(k)[i]);
371                    } else {
372                        ea.x = math::atan2(-self.col(j)[k], self.col(j)[j]);
373                        ea.y = math::atan2(sy, self.col(i)[i]);
374                    }
375                } else {
376                    let cy = math::sqrt(
377                        self.col(i)[i] * self.col(i)[i] + self.col(j)[i] * self.col(j)[i],
378                    );
379                    if (cy > 16.0 * $scalar::EPSILON) {
380                        ea.x = math::atan2(self.col(k)[j], self.col(k)[k]);
381                        ea.y = math::atan2(-self.col(k)[i], cy);
382                        ea.z = math::atan2(self.col(j)[i], self.col(i)[i]);
383                    } else {
384                        ea.x = math::atan2(-self.col(j)[k], self.col(j)[j]);
385                        ea.y = math::atan2(-self.col(k)[i], cy);
386                    }
387                }
388
389                // reverse rotation angle of original code
390                if order.parity_even {
391                    ea = -ea;
392                }
393
394                if !order.frame_static {
395                    ea = ea.zyx();
396                }
397
398                (ea.x, ea.y, ea.z)
399            }
400        }
401    };
402}
403
404macro_rules! impl_mat4_to_euler {
405    ($scalar:ident, $mat4:ident, $mat3:ident) => {
406        impl ToEuler for $mat4 {
407            type Scalar = $scalar;
408            fn to_euler_angles(
409                self,
410                order: EulerRot,
411            ) -> (Self::Scalar, Self::Scalar, Self::Scalar) {
412                $mat3::from_mat4(self).to_euler_angles(order)
413            }
414        }
415    };
416}
417
418macro_rules! impl_quat_to_euler {
419    ($scalar:ident, $quat:ident, $mat3:ident) => {
420        impl ToEuler for $quat {
421            type Scalar = $scalar;
422            fn to_euler_angles(
423                self,
424                order: EulerRot,
425            ) -> (Self::Scalar, Self::Scalar, Self::Scalar) {
426                $mat3::from_quat(self).to_euler_angles(order)
427            }
428        }
429    };
430}
431
432impl_mat3_to_euler!(f32, Mat3, Vec3);
433impl_mat3_from_euler!(f32, Mat3, Vec3);
434impl_mat3_to_euler!(f32, Mat3A, Vec3A);
435impl_mat3_from_euler!(f32, Mat3A, Vec3A);
436impl_mat4_from_euler!(f32, Mat4, Mat3);
437impl_mat4_to_euler!(f32, Mat4, Mat3);
438impl_quat_to_euler!(f32, Quat, Mat3);
439impl_quat_from_euler!(f32, Quat, Vec3);
440
441#[cfg(feature = "f64")]
442impl_mat3_to_euler!(f64, DMat3, DVec3);
443#[cfg(feature = "f64")]
444impl_mat3_from_euler!(f64, DMat3, DVec3);
445#[cfg(feature = "f64")]
446impl_mat4_to_euler!(f64, DMat4, DMat3);
447#[cfg(feature = "f64")]
448impl_mat4_from_euler!(f64, DMat4, DMat3);
449#[cfg(feature = "f64")]
450impl_quat_to_euler!(f64, DQuat, DMat3);
451#[cfg(feature = "f64")]
452impl_quat_from_euler!(f64, DQuat, DVec3);