glam/
euler.rs

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