Antkeeper  0.0.1
euler-angles.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef ANTKEEPER_MATH_EULER_ANGLES_HPP
21 #define ANTKEEPER_MATH_EULER_ANGLES_HPP
22 
23 #include <engine/math/numbers.hpp>
25 #include <engine/math/vector.hpp>
26 #include <cmath>
27 #include <cstdint>
28 #include <concepts>
29 #include <utility>
30 
31 namespace math {
32 
38 enum class rotation_sequence: std::uint8_t
39 {
41  zxz = 0b100010,
42 
44  xyx = 0b000100,
45 
47  yzy = 0b011001,
48 
50  zyz = 0b100110,
51 
53  xzx = 0b001000,
54 
56  yxy = 0b010001,
57 
59  xyz = 0b100100,
60 
62  yzx = 0b001001,
63 
65  zxy = 0b010010,
66 
68  xzy = 0b011000,
69 
71  zyx = 0b000110,
72 
74  yxz = 0b100001
75 };
76 
86 template <std::integral T>
87 [[nodiscard]] inline constexpr vec3<T> rotation_axes(rotation_sequence sequence) noexcept
88 {
89  const auto x = static_cast<T>(sequence);
90  return {x & 3, (x >> 2) & 3, x >> 4};
91 }
92 
108 template <rotation_sequence Sequence, class T>
109 [[nodiscard]] vec3<T> euler_from_quat(const quat<T>& q, T tolerance = T{1e-6})
110 {
111  constexpr auto axes = rotation_axes<int>(Sequence);
112  constexpr auto proper = axes[0] == axes[2];
113  constexpr auto i = axes[0];
114  constexpr auto j = axes[1];
115  constexpr auto k = proper ? 3 - i - j : axes[2];
116  constexpr auto sign = static_cast<T>(((i - j) * (j - k) * (k - i)) >> 1);
117 
118  auto a = q.r;
119  auto b = q.i[i];
120  auto c = q.i[j];
121  auto d = q.i[k] * sign;
122  if constexpr (!proper)
123  {
124  a -= q.i[j];
125  b += d;
126  c += q.r;
127  d -= q.i[i];
128  }
129  const auto aa_bb = a * a + b * b;
130 
131  vec3<T> angles;
132  angles[1] = std::acos(T{2} * aa_bb / (aa_bb + c * c + d * d) - T{1});
133 
134  if (std::abs(angles[1]) <= tolerance)
135  {
136  angles[0] = T{0};
137  angles[2] = std::atan2(b, a) * T{2};
138  }
139  else if (std::abs(angles[1] - pi<T>) <= tolerance)
140  {
141  angles[0] = T{0};
142  angles[2] = std::atan2(-d, c) * T{-2};
143  }
144  else
145  {
146  const auto half_sum = std::atan2(b, a);
147  const auto half_diff = std::atan2(-d, c);
148 
149  angles[0] = half_sum + half_diff;
150  angles[2] = half_sum - half_diff;
151  }
152 
153  if constexpr (!proper)
154  {
155  angles[2] *= sign;
156  angles[1] -= half_pi<T>;
157  }
158 
159  return angles;
160 }
161 
162 template <class T>
163 [[nodiscard]] inline vec3<T> euler_zxz_from_quat(const quat<T>& q, T tolerance = T{1e-6})
164 {
165  return euler_from_quat<rotation_sequence::zxz, T>(q, tolerance);
166 }
167 
168 template <class T>
169 [[nodiscard]] inline vec3<T> euler_xyx_from_quat(const quat<T>& q, T tolerance = T{1e-6})
170 {
171  return euler_from_quat<rotation_sequence::xyx, T>(q, tolerance);
172 }
173 
174 template <class T>
175 [[nodiscard]] inline vec3<T> euler_yzy_from_quat(const quat<T>& q, T tolerance = T{1e-6})
176 {
177  return euler_from_quat<rotation_sequence::yzy, T>(q, tolerance);
178 }
179 
180 template <class T>
181 [[nodiscard]] inline vec3<T> euler_zyz_from_quat(const quat<T>& q, T tolerance = T{1e-6})
182 {
183  return euler_from_quat<rotation_sequence::zyz, T>(q, tolerance);
184 }
185 
186 template <class T>
187 [[nodiscard]] inline vec3<T> euler_xzx_from_quat(const quat<T>& q, T tolerance = T{1e-6})
188 {
189  return euler_from_quat<rotation_sequence::xzx, T>(q, tolerance);
190 }
191 
192 template <class T>
193 [[nodiscard]] inline vec3<T> euler_yxy_from_quat(const quat<T>& q, T tolerance = T{1e-6})
194 {
195  return euler_from_quat<rotation_sequence::yxy, T>(q, tolerance);
196 }
197 
198 template <class T>
199 [[nodiscard]] inline vec3<T> euler_xyz_from_quat(const quat<T>& q, T tolerance = T{1e-6})
200 {
201  return euler_from_quat<rotation_sequence::xyz, T>(q, tolerance);
202 }
203 
204 template <class T>
205 [[nodiscard]] inline vec3<T> euler_yzx_from_quat(const quat<T>& q, T tolerance = T{1e-6})
206 {
207  return euler_from_quat<rotation_sequence::yzx, T>(q, tolerance);
208 }
209 
210 template <class T>
211 [[nodiscard]] inline vec3<T> euler_zxy_from_quat(const quat<T>& q, T tolerance = T{1e-6})
212 {
213  return euler_from_quat<rotation_sequence::zxy, T>(q, tolerance);
214 }
215 
216 template <class T>
217 [[nodiscard]] inline vec3<T> euler_xzy_from_quat(const quat<T>& q, T tolerance = T{1e-6})
218 {
219  return euler_from_quat<rotation_sequence::xzy, T>(q, tolerance);
220 }
221 
222 template <class T>
223 [[nodiscard]] inline vec3<T> euler_zyx_from_quat(const quat<T>& q, T tolerance = T{1e-6})
224 {
225  return euler_from_quat<rotation_sequence::zyx, T>(q, tolerance);
226 }
227 
228 template <class T>
229 [[nodiscard]] inline vec3<T> euler_yxz_from_quat(const quat<T>& q, T tolerance = T{1e-6})
230 {
231  return euler_from_quat<rotation_sequence::yxz, T>(q, tolerance);
232 }
233 
234 template <class T>
235 [[nodiscard]] inline vec3<T> euler_from_quat(rotation_sequence sequence, const quat<T>& q, T tolerance = T{1e-6})
236 {
237  switch (sequence)
238  {
240  return euler_zxz_from_quat<T>(q, tolerance);
242  return euler_xyx_from_quat<T>(q, tolerance);
244  return euler_yzy_from_quat<T>(q, tolerance);
246  return euler_zyz_from_quat<T>(q, tolerance);
248  return euler_xzx_from_quat<T>(q, tolerance);
250  return euler_yxy_from_quat<T>(q, tolerance);
252  return euler_xyz_from_quat<T>(q, tolerance);
254  return euler_yzx_from_quat<T>(q, tolerance);
256  return euler_zxy_from_quat<T>(q, tolerance);
258  return euler_xzy_from_quat<T>(q, tolerance);
260  return euler_zyx_from_quat<T>(q, tolerance);
262  default:
263  return euler_yxz_from_quat<T>(q, tolerance);
264  }
265 }
266 
268 
282 template <rotation_sequence Sequence, class T>
283 [[nodiscard]] quat<T> euler_to_quat(const vec3<T>& angles)
284 {
285  const auto half_angles = angles * T{0.5};
286  const T cx = std::cos(half_angles.x());
287  const T sx = std::sin(half_angles.x());
288  const T cy = std::cos(half_angles.y());
289  const T sy = std::sin(half_angles.y());
290  const T cz = std::cos(half_angles.z());
291  const T sz = std::sin(half_angles.z());
292 
293  if constexpr (Sequence == rotation_sequence::zxz)
294  {
295  return
296  {
297  cx * cy * cz - sx * cy * sz,
298  cx * cz * sy + sx * sy * sz,
299  cx * sy * sz - sx * cz * sy,
300  cx * cy * sz + cy * cz * sx
301  };
302  }
303  else if constexpr (Sequence == rotation_sequence::xyx)
304  {
305  return
306  {
307  cx * cy * cz - sx * cy * sz,
308  cx * cy * sz + cy * cz * sx,
309  cx * cz * sy + sx * sy * sz,
310  cx * sy * sz - sx * cz * sy
311  };
312  }
313  else if constexpr (Sequence == rotation_sequence::yzy)
314  {
315  return
316  {
317  cx * cy * cz - sx * cy * sz,
318  cx * sy * sz - sx * cz * sy,
319  cx * cy * sz + cy * cz * sx,
320  cx * cz * sy + sx * sy * sz
321  };
322  }
323  else if constexpr (Sequence == rotation_sequence::zyz)
324  {
325  return
326  {
327  cx * cy * cz - sx * cy * sz,
328  -cx * sy * sz + sx * cz * sy,
329  cx * cz * sy + sx * sy * sz,
330  cx * cy * sz + cy * cz * sx
331  };
332  }
333  else if constexpr (Sequence == rotation_sequence::xzx)
334  {
335  return
336  {
337  cx * cy * cz - sx * cy * sz,
338  cx * cy * sz + cy * cz * sx,
339  -cx * sy * sz + sx * cz * sy,
340  cx * cz * sy + sx * sy * sz
341  };
342  }
343  else if constexpr (Sequence == rotation_sequence::yxy)
344  {
345  return
346  {
347  cx * cy * cz - sx * cy * sz,
348  cx * cz * sy + sx * sy * sz,
349  cx * cy * sz + cy * cz * sx,
350  -cx * sy * sz + sx * cz * sy
351  };
352  }
353  else if constexpr (Sequence == rotation_sequence::xyz)
354  {
355  return
356  {
357  cx * cy * cz + sx * sy * sz,
358  -cx * sy * sz + cy * cz * sx,
359  cx * cz * sy + sx * cy * sz,
360  cx * cy * sz - sx * cz * sy
361  };
362  }
363  else if constexpr (Sequence == rotation_sequence::yzx)
364  {
365  return
366  {
367  cx * cy * cz + sx * sy * sz,
368  cx * cy * sz - sx * cz * sy,
369  -cx * sy * sz + cy * cz * sx,
370  cx * cz * sy + sx * cy * sz
371  };
372  }
373  else if constexpr (Sequence == rotation_sequence::zxy)
374  {
375  return
376  {
377  cx * cy * cz + sx * sy * sz,
378  cx * cz * sy + sx * cy * sz,
379  cx * cy * sz - sx * cz * sy,
380  -cx * sy * sz + cy * cz * sx
381  };
382  }
383  else if constexpr (Sequence == rotation_sequence::xzy)
384  {
385  return
386  {
387  cx * cy * cz - sx * sy * sz,
388  cx * sy * sz + cy * cz * sx,
389  cx * cy * sz + sx * cz * sy,
390  cx * cz * sy - sx * cy * sz
391  };
392  }
393  else if constexpr (Sequence == rotation_sequence::zyx)
394  {
395  return
396  {
397  cx * cy * cz - sx * sy * sz,
398  cx * cy * sz + sx * cz * sy,
399  cx * cz * sy - sx * cy * sz,
400  cx * sy * sz + cy * cz * sx
401  };
402  }
403  else //if constexpr (Sequence == rotation_sequence::yxz)
404  {
405  return
406  {
407  cx * cy * cz - sx * sy * sz,
408  cx * cz * sy - sx * cy * sz,
409  cx * sy * sz + cy * cz * sx,
410  cx * cy * sz + sx * cz * sy
411  };
412  }
413 }
414 
415 template <class T>
416 [[nodiscard]] inline quat<T> euler_zxz_to_quat(const vec3<T>& angles)
417 {
418  return euler_to_quat<rotation_sequence::zxz, T>(angles);
419 }
420 
421 template <class T>
422 [[nodiscard]] inline quat<T> euler_xyx_to_quat(const vec3<T>& angles)
423 {
424  return euler_to_quat<rotation_sequence::xyx, T>(angles);
425 }
426 
427 template <class T>
428 [[nodiscard]] inline quat<T> euler_yzy_to_quat(const vec3<T>& angles)
429 {
430  return euler_to_quat<rotation_sequence::yzy, T>(angles);
431 }
432 
433 template <class T>
434 [[nodiscard]] inline quat<T> euler_zyz_to_quat(const vec3<T>& angles)
435 {
436  return euler_to_quat<rotation_sequence::zyz, T>(angles);
437 }
438 
439 template <class T>
440 [[nodiscard]] inline quat<T> euler_xzx_to_quat(const vec3<T>& angles)
441 {
442  return euler_to_quat<rotation_sequence::xzx, T>(angles);
443 }
444 
445 template <class T>
446 [[nodiscard]] inline quat<T> euler_yxy_to_quat(const vec3<T>& angles)
447 {
448  return euler_to_quat<rotation_sequence::yxy, T>(angles);
449 }
450 
451 template <class T>
452 [[nodiscard]] inline quat<T> euler_xyz_to_quat(const vec3<T>& angles)
453 {
454  return euler_to_quat<rotation_sequence::xyz, T>(angles);
455 }
456 
457 template <class T>
458 [[nodiscard]] inline quat<T> euler_yzx_to_quat(const vec3<T>& angles)
459 {
460  return euler_to_quat<rotation_sequence::yzx, T>(angles);
461 }
462 
463 template <class T>
464 [[nodiscard]] inline quat<T> euler_zxy_to_quat(const vec3<T>& angles)
465 {
466  return euler_to_quat<rotation_sequence::zxy, T>(angles);
467 }
468 
469 template <class T>
470 [[nodiscard]] inline quat<T> euler_xzy_to_quat(const vec3<T>& angles)
471 {
472  return euler_to_quat<rotation_sequence::xzy, T>(angles);
473 }
474 
475 template <class T>
476 [[nodiscard]] inline quat<T> euler_zyx_to_quat(const vec3<T>& angles)
477 {
478  return euler_to_quat<rotation_sequence::zyx, T>(angles);
479 }
480 
481 template <class T>
482 [[nodiscard]] inline quat<T> euler_yxz_to_quat(const vec3<T>& angles)
483 {
484  return euler_to_quat<rotation_sequence::yxz, T>(angles);
485 }
486 
487 template <class T>
488 [[nodiscard]] inline quat<T> euler_to_quat(rotation_sequence sequence, const vec3<T>& angles)
489 {
490  switch (sequence)
491  {
493  return euler_zxz_to_quat<T>(angles);
495  return euler_xyx_to_quat<T>(angles);
497  return euler_yzy_to_quat<T>(angles);
499  return euler_zyz_to_quat<T>(angles);
501  return euler_xzx_to_quat<T>(angles);
503  return euler_yxy_to_quat<T>(angles);
505  return euler_xyz_to_quat<T>(angles);
507  return euler_yzx_to_quat<T>(angles);
509  return euler_zxy_to_quat<T>(angles);
511  return euler_xzy_to_quat<T>(angles);
513  return euler_zyx_to_quat<T>(angles);
515  default:
516  return euler_yxz_to_quat<T>(angles);
517  }
518 }
520 
521 } // namespace math
522 
523 #endif // ANTKEEPER_MATH_EULER_ANGLES_HPP
@ a
Vertex A region.
@ c
Vertex C region.
@ b
Vertex B region.
constexpr T e
e.
Definition: numbers.hpp:37
Mathematical functions and data types.
Definition: angles.hpp:26
quat< T > euler_yzy_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_yxy_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
constexpr vector< T, N > sign(const vector< T, N > &x)
Returns a vector containing the signs of each element.
Definition: vector.hpp:1502
quat< T > euler_xyz_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
constexpr vec3< T > rotation_axes(rotation_sequence sequence) noexcept
Returns the indices of the axes of a rotation sequence.
vec3< T > euler_xzx_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
quat< T > euler_xzx_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
quat< T > euler_yxy_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
vec3< T > euler_zyz_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
quat< T > euler_yxz_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_zxz_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
constexpr vector< T, N > abs(const vector< T, N > &x)
Returns the absolute values of each element.
Definition: vector.hpp:985
quat< T > euler_yzx_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
quat< T > euler_zyz_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
quat< T > euler_zxz_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_xyx_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
quat< T > euler_xzy_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_xyz_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
rotation_sequence
Rotation sequences of proper Euler and Tait-Bryan angles.
@ zxz
z-x-z rotation sequence (proper Euler angles).
@ zxy
z-x-y rotation sequence (Tait-Bryan angles).
@ yzx
y-z-x rotation sequence (Tait-Bryan angles).
@ yxz
y-x-z rotation sequence (Tait-Bryan angles).
@ zyz
z-y-z rotation sequence (proper Euler angles).
@ yzy
y-z-y rotation sequence (proper Euler angles).
@ xzx
x-z-x rotation sequence (proper Euler angles).
@ yxy
y-x-y rotation sequence (proper Euler angles).
@ xyz
x-y-z rotation sequence (Tait-Bryan angles).
@ xzy
x-z-y rotation sequence (Tait-Bryan angles).
@ xyx
x-y-x rotation sequence (proper Euler angles).
@ zyx
z-y-x rotation sequence (Tait-Bryan angles).
quat< T > euler_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
quat< T > euler_zxy_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_zyx_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
vec3< T > euler_yxz_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
vec3< T > euler_zxy_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
vec3< T > euler_yzy_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
quat< T > euler_xyx_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
quat< T > euler_zyx_to_quat(const vec3< T > &angles)
Constructs a quaternion from Euler angles.
vec3< T > euler_yzx_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
vec3< T > euler_xzy_from_quat(const quat< T > &q, T tolerance=T{1e-6})
Derives Euler angles from a unit quaternion.
Quaternion composed of a real scalar part and imaginary vector part.
Definition: quaternion.hpp:39
n-dimensional vector.
Definition: vector.hpp:44