1#[cfg(feature = "f64")]
4use crate::{DMat3, DMat4, DQuat, DVec3};
5use crate::{Mat3, Mat3A, Mat4, Quat, Vec3, Vec3A, Vec3Swizzles};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub enum EulerRot {
30 ZYX,
32 ZXY,
34 YXZ,
36 YZX,
38 XYZ,
40 XZY,
42
43 ZYZ,
45 ZXZ,
47 YXY,
49 YZY,
51 XYX,
53 XZX,
55
56 ZYXEx,
58 ZXYEx,
60 YXZEx,
62 YZXEx,
64 XYZEx,
66 XZYEx,
68
69 ZYZEx,
71 ZXZEx,
73 YXYEx,
75 YZYEx,
77 XYXEx,
79 XZXEx,
81}
82
83impl Default for EulerRot {
84 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 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 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);