1use crate::{DQuat, Quat};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum EulerRot {
17 ZYX,
19 ZXY,
21 YXZ,
23 YZX,
25 XYZ,
27 XZY,
29}
30
31impl Default for EulerRot {
32 fn default() -> Self {
34 Self::YXZ
35 }
36}
37
38pub(crate) trait EulerFromQuaternion<Q: Copy>: Sized + Copy {
40 type Output;
41 fn first(self, q: Q) -> Self::Output;
43 fn second(self, q: Q) -> Self::Output;
45 fn third(self, q: Q) -> Self::Output;
47
48 fn convert_quat(self, q: Q) -> (Self::Output, Self::Output, Self::Output);
50
51 #[doc(hidden)]
52 fn sine_theta(self, q: Q) -> Self::Output;
53}
54
55pub(crate) trait EulerToQuaternion<T>: Copy {
57 type Output;
58 fn new_quat(self, u: T, v: T, w: T) -> Self::Output;
60}
61
62macro_rules! impl_from_quat {
63 ($t:ident, $quat:ident) => {
64 impl EulerFromQuaternion<$quat> for EulerRot {
65 type Output = $t;
66
67 fn sine_theta(self, q: $quat) -> $t {
68 use EulerRot::*;
69 match self {
70 ZYX => -2.0 * (q.x * q.z - q.w * q.y),
71 ZXY => 2.0 * (q.y * q.z + q.w * q.x),
72 YXZ => -2.0 * (q.y * q.z - q.w * q.x),
73 YZX => 2.0 * (q.x * q.y + q.w * q.z),
74 XYZ => 2.0 * (q.x * q.z + q.w * q.y),
75 XZY => -2.0 * (q.x * q.y - q.w * q.z),
76 }
77 .clamp(-1.0, 1.0)
78 }
79
80 fn first(self, q: $quat) -> $t {
81 use crate::$t::math;
82 use EulerRot::*;
83
84 let sine_theta = self.sine_theta(q);
85 if math::abs(sine_theta) > 0.99999 {
86 let scale = 2.0 * math::signum(sine_theta);
87
88 match self {
89 ZYX => scale * math::atan2(-q.x, q.w),
90 ZXY => scale * math::atan2(q.y, q.w),
91 YXZ => scale * math::atan2(-q.z, q.w),
92 YZX => scale * math::atan2(q.x, q.w),
93 XYZ => scale * math::atan2(q.z, q.w),
94 XZY => scale * math::atan2(-q.y, q.w),
95 }
96 } else {
97 match self {
98 ZYX => math::atan2(
99 2.0 * (q.x * q.y + q.w * q.z),
100 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
101 ),
102 ZXY => math::atan2(
103 -2.0 * (q.x * q.y - q.w * q.z),
104 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
105 ),
106 YXZ => math::atan2(
107 2.0 * (q.x * q.z + q.w * q.y),
108 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
109 ),
110 YZX => math::atan2(
111 -2.0 * (q.x * q.z - q.w * q.y),
112 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
113 ),
114 XYZ => math::atan2(
115 -2.0 * (q.y * q.z - q.w * q.x),
116 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
117 ),
118 XZY => math::atan2(
119 2.0 * (q.y * q.z + q.w * q.x),
120 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
121 ),
122 }
123 }
124 }
125
126 fn second(self, q: $quat) -> $t {
127 use crate::$t::math;
128 math::asin(self.sine_theta(q))
129 }
130
131 fn third(self, q: $quat) -> $t {
132 use crate::$t::math;
133 use EulerRot::*;
134 if math::abs(self.sine_theta(q)) > 0.99999 {
135 0.0
136 } else {
137 match self {
138 ZYX => math::atan2(
139 2.0 * (q.y * q.z + q.w * q.x),
140 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
141 ),
142 ZXY => math::atan2(
143 -2.0 * (q.x * q.z - q.w * q.y),
144 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
145 ),
146 YXZ => math::atan2(
147 2.0 * (q.x * q.y + q.w * q.z),
148 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
149 ),
150 YZX => math::atan2(
151 -2.0 * (q.y * q.z - q.w * q.x),
152 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
153 ),
154 XYZ => math::atan2(
155 -2.0 * (q.x * q.y - q.w * q.z),
156 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
157 ),
158 XZY => math::atan2(
159 2.0 * (q.x * q.z + q.w * q.y),
160 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
161 ),
162 }
163 }
164 }
165
166 fn convert_quat(self, q: $quat) -> ($t, $t, $t) {
167 use crate::$t::math;
168 use EulerRot::*;
169
170 let sine_theta = self.sine_theta(q);
171 let second = math::asin(sine_theta);
172
173 if math::abs(sine_theta) > 0.99999 {
174 let scale = 2.0 * math::signum(sine_theta);
175
176 return match self {
177 ZYX => (scale * math::atan2(-q.x, q.w), second, 0.0),
178 ZXY => (scale * math::atan2(q.y, q.w), second, 0.0),
179 YXZ => (scale * math::atan2(-q.z, q.w), second, 0.0),
180 YZX => (scale * math::atan2(q.x, q.w), second, 0.0),
181 XYZ => (scale * math::atan2(q.z, q.w), second, 0.0),
182 XZY => (scale * math::atan2(-q.y, q.w), second, 0.0),
183 };
184 }
185
186 let first = match self {
187 ZYX => math::atan2(
188 2.0 * (q.x * q.y + q.w * q.z),
189 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
190 ),
191 ZXY => math::atan2(
192 -2.0 * (q.x * q.y - q.w * q.z),
193 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
194 ),
195 YXZ => math::atan2(
196 2.0 * (q.x * q.z + q.w * q.y),
197 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
198 ),
199 YZX => math::atan2(
200 -2.0 * (q.x * q.z - q.w * q.y),
201 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
202 ),
203 XYZ => math::atan2(
204 -2.0 * (q.y * q.z - q.w * q.x),
205 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
206 ),
207 XZY => math::atan2(
208 2.0 * (q.y * q.z + q.w * q.x),
209 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
210 ),
211 };
212
213 let third = match self {
214 ZYX => math::atan2(
215 2.0 * (q.y * q.z + q.w * q.x),
216 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
217 ),
218 ZXY => math::atan2(
219 -2.0 * (q.x * q.z - q.w * q.y),
220 q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z,
221 ),
222 YXZ => math::atan2(
223 2.0 * (q.x * q.y + q.w * q.z),
224 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
225 ),
226 YZX => math::atan2(
227 -2.0 * (q.y * q.z - q.w * q.x),
228 q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z,
229 ),
230 XYZ => math::atan2(
231 -2.0 * (q.x * q.y - q.w * q.z),
232 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
233 ),
234 XZY => math::atan2(
235 2.0 * (q.x * q.z + q.w * q.y),
236 q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z,
237 ),
238 };
239
240 (first, second, third)
241 }
242 }
243 };
245}
246
247macro_rules! impl_to_quat {
248 ($t:ty, $quat:ident) => {
249 impl EulerToQuaternion<$t> for EulerRot {
250 type Output = $quat;
251 #[inline(always)]
252 fn new_quat(self, u: $t, v: $t, w: $t) -> $quat {
253 use EulerRot::*;
254 #[inline(always)]
255 fn rot_x(a: $t) -> $quat {
256 $quat::from_rotation_x(a)
257 }
258 #[inline(always)]
259 fn rot_y(a: $t) -> $quat {
260 $quat::from_rotation_y(a)
261 }
262 #[inline(always)]
263 fn rot_z(a: $t) -> $quat {
264 $quat::from_rotation_z(a)
265 }
266 match self {
267 ZYX => rot_z(u) * rot_y(v) * rot_x(w),
268 ZXY => rot_z(u) * rot_x(v) * rot_y(w),
269 YXZ => rot_y(u) * rot_x(v) * rot_z(w),
270 YZX => rot_y(u) * rot_z(v) * rot_x(w),
271 XYZ => rot_x(u) * rot_y(v) * rot_z(w),
272 XZY => rot_x(u) * rot_z(v) * rot_y(w),
273 }
274 .normalize()
275 }
276 }
277 };
279}
280
281impl_from_quat!(f32, Quat);
282impl_from_quat!(f64, DQuat);
283impl_to_quat!(f32, Quat);
284impl_to_quat!(f64, DQuat);