1use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
9
10use godot_ffi as sys;
11use sys::{ffi_methods, ExtVariantType, GodotFfi};
12
13use crate::builtin::math::{ApproxEq, FloatExt, GlamConv, GlamType};
14use crate::builtin::{inner, real, Basis, EulerOrder, RQuat, RealConv, Vector3};
15
16#[derive(Copy, Clone, PartialEq, Debug)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[repr(C)]
26pub struct Quaternion {
27 pub x: real,
28 pub y: real,
29 pub z: real,
30 pub w: real,
31}
32
33impl Quaternion {
34 pub const IDENTITY: Self = Self {
38 x: 0.0,
39 y: 0.0,
40 z: 0.0,
41 w: 1.0,
42 };
43
44 pub fn new(x: real, y: real, z: real, w: real) -> Self {
45 Self { x, y, z, w }
46 }
47
48 pub fn from_axis_angle(axis: Vector3, angle: real) -> Self {
53 assert!(
54 axis.is_normalized(),
55 "Quaternion axis {axis:?} is not normalized."
56 );
57 let d = axis.length();
58 let sin_angle = (angle * 0.5).sin();
59 let cos_angle = (angle * 0.5).cos();
60 let s = sin_angle / d;
61 let x = axis.x * s;
62 let y = axis.y * s;
63 let z = axis.z * s;
64 let w = cos_angle;
65 Self::new(x, y, z, w)
66 }
67
68 pub fn from_rotation_arc(arc_from: Vector3, arc_to: Vector3) -> Self {
80 debug_assert!(
81 arc_from.is_normalized(),
82 "input 1 (`arc_from`) in `Quaternion::from_rotation_arc` must be a unit vector"
83 );
84 debug_assert!(
85 arc_to.is_normalized(),
86 "input 2 (`arc_to`) in `Quaternion::from_rotation_arc` must be a unit vector"
87 );
88 <Self as GlamConv>::Glam::from_rotation_arc(arc_from.to_glam(), arc_to.to_glam()).to_front()
89 }
90
91 pub fn angle_to(self, to: Self) -> real {
92 self.glam2(&to, RQuat::angle_between)
93 }
94
95 pub fn dot(self, with: Self) -> real {
96 self.glam2(&with, RQuat::dot)
97 }
98
99 pub fn exp(self) -> Self {
100 let mut v = Vector3::new(self.x, self.y, self.z);
101 let theta = v.length();
102 v = v.normalized();
103
104 if theta < real::CMP_EPSILON || !v.is_normalized() {
105 Self::default()
106 } else {
107 Self::from_axis_angle(v, theta)
108 }
109 }
110
111 pub fn from_euler(euler: Vector3) -> Self {
112 let half_a1 = euler.y * 0.5;
113 let half_a2 = euler.x * 0.5;
114 let half_a3 = euler.z * 0.5;
115 let cos_a1 = half_a1.cos();
116 let sin_a1 = half_a1.sin();
117 let cos_a2 = half_a2.cos();
118 let sin_a2 = half_a2.sin();
119 let cos_a3 = half_a3.cos();
120 let sin_a3 = half_a3.sin();
121
122 Self::new(
123 sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
124 sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
125 -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
126 sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3,
127 )
128 }
129
130 pub fn get_angle(self) -> real {
131 2.0 * self.w.acos()
132 }
133
134 pub fn get_axis(self) -> Vector3 {
135 let Self { x, y, z, w } = self;
136 let axis = Vector3::new(x, y, z);
137
138 if self.w.abs() > 1.0 - real::CMP_EPSILON {
139 axis
140 } else {
141 let r = 1.0 / (1.0 - w * w).sqrt();
142 r * axis
143 }
144 }
145
146 pub fn get_euler(self) -> Vector3 {
150 self.get_euler_with(EulerOrder::YXZ)
151 }
152
153 pub fn get_euler_with(self, order: EulerOrder) -> Vector3 {
159 Basis::from_quaternion(self).get_euler_with(order)
160 }
161
162 pub fn inverse(self) -> Self {
163 Self::new(-self.x, -self.y, -self.z, self.w)
164 }
165
166 pub fn is_finite(self) -> bool {
167 self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
168 }
169
170 pub fn is_normalized(self) -> bool {
171 self.length_squared().approx_eq(&1.0)
172 }
173
174 pub fn length(self) -> real {
175 self.length_squared().sqrt()
176 }
177
178 pub fn length_squared(self) -> real {
179 self.dot(self)
180 }
181
182 pub fn log(self) -> Self {
183 let v = self.get_axis() * self.get_angle();
184 Quaternion::new(v.x, v.y, v.z, 0.0)
185 }
186
187 pub fn normalized(self) -> Self {
190 let length = self.length();
191 assert!(!length.approx_eq(&0.0), "Quaternion has length 0");
192 self / length
193 }
194
195 pub fn slerp(self, to: Self, weight: real) -> Self {
198 let normalized_inputs = self.ensure_normalized(&[&to]);
199 assert!(normalized_inputs, "Slerp requires normalized quaternions");
200
201 self.as_inner().slerp(to, weight.as_f64())
202 }
203
204 pub fn slerpni(self, to: Self, weight: real) -> Self {
207 let normalized_inputs = self.ensure_normalized(&[&to]);
208 assert!(normalized_inputs, "Slerpni requires normalized quaternions");
209
210 self.as_inner().slerpni(to, weight.as_f64())
211 }
212
213 pub fn spherical_cubic_interpolate(
216 self,
217 b: Self,
218 pre_a: Self,
219 post_b: Self,
220 weight: real,
221 ) -> Self {
222 let normalized_inputs = self.ensure_normalized(&[&b, &pre_a, &post_b]);
223 assert!(
224 normalized_inputs,
225 "Spherical cubic interpolation requires normalized quaternions"
226 );
227
228 self.as_inner()
229 .spherical_cubic_interpolate(b, pre_a, post_b, weight.as_f64())
230 }
231
232 #[allow(clippy::too_many_arguments)]
235 pub fn spherical_cubic_interpolate_in_time(
236 self,
237 b: Self,
238 pre_a: Self,
239 post_b: Self,
240 weight: real,
241 b_t: real,
242 pre_a_t: real,
243 post_b_t: real,
244 ) -> Self {
245 let normalized_inputs = self.ensure_normalized(&[&b, &pre_a, &post_b]);
246 assert!(
247 normalized_inputs,
248 "Spherical cubic interpolation in time requires normalized quaternions"
249 );
250
251 self.as_inner().spherical_cubic_interpolate_in_time(
252 b,
253 pre_a,
254 post_b,
255 weight.as_f64(),
256 b_t.as_f64(),
257 pre_a_t.as_f64(),
258 post_b_t.as_f64(),
259 )
260 }
261
262 #[doc(hidden)]
263 pub fn as_inner(&self) -> inner::InnerQuaternion<'_> {
264 inner::InnerQuaternion::from_outer(self)
265 }
266
267 #[doc(hidden)]
268 fn ensure_normalized(&self, quats: &[&Quaternion]) -> bool {
269 quats.iter().all(|v| v.is_normalized()) && self.is_normalized()
270 }
271}
272
273impl Add for Quaternion {
274 type Output = Self;
275
276 fn add(self, other: Self) -> Self {
277 Self::new(
278 self.x + other.x,
279 self.y + other.y,
280 self.z + other.z,
281 self.w + other.w,
282 )
283 }
284}
285
286impl AddAssign for Quaternion {
287 fn add_assign(&mut self, other: Self) {
288 *self = *self + other
289 }
290}
291
292impl Sub for Quaternion {
293 type Output = Self;
294
295 fn sub(self, other: Self) -> Self {
296 Self::new(
297 self.x - other.x,
298 self.y - other.y,
299 self.z - other.z,
300 self.w - other.w,
301 )
302 }
303}
304
305impl SubAssign for Quaternion {
306 fn sub_assign(&mut self, other: Self) {
307 *self = *self - other
308 }
309}
310
311impl Mul<Quaternion> for Quaternion {
312 type Output = Self;
313
314 fn mul(self, other: Quaternion) -> Self {
315 let x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y;
318 let y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z;
319 let z = self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x;
320 let w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z;
321
322 Self::new(x, y, z, w)
323 }
324}
325
326impl Mul<Vector3> for Quaternion {
327 type Output = Vector3;
328
329 fn mul(self, rhs: Vector3) -> Self::Output {
334 Vector3::from_glam(self.to_glam().mul_vec3(rhs.to_glam()))
335 }
336}
337
338unsafe impl GodotFfi for Quaternion {
341 const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::QUATERNION);
342
343 ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
344}
345
346crate::meta::impl_godot_as_self!(Quaternion: ByValue);
347
348impl std::fmt::Display for Quaternion {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 self.to_glam().fmt(f)
351 }
352}
353
354impl Default for Quaternion {
355 fn default() -> Self {
356 Self::new(0.0, 0.0, 0.0, 1.0)
357 }
358}
359
360impl ApproxEq for Quaternion {
361 fn approx_eq(&self, other: &Self) -> bool {
362 self.x.approx_eq(&other.x)
363 && self.y.approx_eq(&other.y)
364 && self.z.approx_eq(&other.z)
365 && self.w.approx_eq(&other.w)
366 }
367}
368
369impl GlamConv for Quaternion {
370 type Glam = RQuat;
371}
372
373impl GlamType for RQuat {
374 type Mapped = Quaternion;
375
376 fn to_front(&self) -> Self::Mapped {
377 Quaternion::new(self.x, self.y, self.z, self.w)
378 }
379
380 fn from_front(mapped: &Self::Mapped) -> Self {
381 RQuat::from_xyzw(mapped.x, mapped.y, mapped.z, mapped.w)
382 }
383}
384
385impl MulAssign<Quaternion> for Quaternion {
386 fn mul_assign(&mut self, other: Quaternion) {
387 *self = *self * other
388 }
389}
390
391impl Mul<real> for Quaternion {
392 type Output = Self;
393
394 fn mul(self, other: real) -> Self {
395 Quaternion::new(
396 self.x * other,
397 self.y * other,
398 self.z * other,
399 self.w * other,
400 )
401 }
402}
403
404impl Mul<Quaternion> for real {
405 type Output = Quaternion;
406
407 fn mul(self, other: Quaternion) -> Quaternion {
408 other * self
409 }
410}
411
412impl MulAssign<real> for Quaternion {
413 fn mul_assign(&mut self, other: real) {
414 *self = *self * other
415 }
416}
417
418impl Div<real> for Quaternion {
419 type Output = Self;
420
421 fn div(self, other: real) -> Self {
422 Self::new(
423 self.x / other,
424 self.y / other,
425 self.z / other,
426 self.w / other,
427 )
428 }
429}
430
431impl DivAssign<real> for Quaternion {
432 fn div_assign(&mut self, other: real) {
433 *self = *self / other
434 }
435}
436
437impl Neg for Quaternion {
438 type Output = Self;
439
440 fn neg(self) -> Self {
441 Self::new(-self.x, -self.y, -self.z, -self.w)
442 }
443}
444
445#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
446mod test {
447 #[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
448 #[test]
449 fn serde_roundtrip() {
450 let quaternion = super::Quaternion::new(1.0, 1.0, 1.0, 1.0);
451 let expected_json = "{\"x\":1.0,\"y\":1.0,\"z\":1.0,\"w\":1.0}";
452
453 crate::builtin::test_utils::roundtrip(&quaternion, expected_json);
454 }
455}