1use super::{Mat4, Vec3, EPSILON};
18use std::ops::{Add, Mul, MulAssign, Neg, Sub};
19
20#[derive(Debug, Clone, Copy, PartialEq)]
30#[repr(C)]
31pub struct Quaternion {
32 pub x: f32,
34 pub y: f32,
36 pub z: f32,
38 pub w: f32,
40}
41
42impl Quaternion {
43 pub const IDENTITY: Quaternion = Quaternion {
45 x: 0.0,
46 y: 0.0,
47 z: 0.0,
48 w: 1.0,
49 };
50
51 #[inline]
56 pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
57 Self { x, y, z, w }
58 }
59
60 #[inline]
67 pub fn from_axis_angle(axis: Vec3, angle_radians: f32) -> Self {
68 let normalized_axis = axis.normalize();
69 let half_angle = angle_radians * 0.5;
70 let s = half_angle.sin();
71 let c = half_angle.cos();
72 Self {
73 x: normalized_axis.x * s,
74 y: normalized_axis.y * s,
75 z: normalized_axis.z * s,
76 w: c,
77 }
78 }
79
80 #[inline]
84 pub fn from_rotation_matrix(m: &Mat4) -> Self {
85 let m00 = m.cols[0].x;
86 let m10 = m.cols[0].y;
87 let m20 = m.cols[0].z;
88 let m01 = m.cols[1].x;
89 let m11 = m.cols[1].y;
90 let m21 = m.cols[1].z;
91 let m02 = m.cols[2].x;
92 let m12 = m.cols[2].y;
93 let m22 = m.cols[2].z;
94
95 let trace = m00 + m11 + m22;
97 let mut q = Self::IDENTITY;
98
99 if trace > 0.0 {
100 let s = 2.0 * (trace + 1.0).sqrt();
101 q.w = 0.25 * s;
102 q.x = (m21 - m12) / s;
103 q.y = (m02 - m20) / s;
104 q.z = (m10 - m01) / s;
105 } else if m00 > m11 && m00 > m22 {
106 let s = 2.0 * (1.0 + m00 - m11 - m22).sqrt();
107 q.w = (m21 - m12) / s;
108 q.x = 0.25 * s;
109 q.y = (m01 + m10) / s;
110 q.z = (m02 + m20) / s;
111 } else if m11 > m22 {
112 let s = 2.0 * (1.0 + m11 - m00 - m22).sqrt();
113 q.w = (m02 - m20) / s;
114 q.x = (m01 + m10) / s;
115 q.y = 0.25 * s;
116 q.z = (m12 + m21) / s;
117 } else {
118 let s = 2.0 * (1.0 + m22 - m00 - m11).sqrt();
119 q.w = (m10 - m01) / s;
120 q.x = (m02 + m20) / s;
121 q.y = (m12 + m21) / s;
122 q.z = 0.25 * s;
123 }
124 q.normalize()
125 }
126
127 #[inline]
129 pub fn magnitude_squared(&self) -> f32 {
130 self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
131 }
132
133 #[inline]
135 pub fn magnitude(&self) -> f32 {
136 self.magnitude_squared().sqrt()
137 }
138
139 pub fn normalize(&self) -> Self {
142 let mag_sqrt = self.magnitude_squared();
143 if mag_sqrt > EPSILON {
144 let inv_mag = 1.0 / mag_sqrt.sqrt();
145 Self {
146 x: self.x * inv_mag,
147 y: self.y * inv_mag,
148 z: self.z * inv_mag,
149 w: self.w * inv_mag,
150 }
151 } else {
152 Self::IDENTITY
153 }
154 }
155
156 #[inline]
158 pub fn conjugate(&self) -> Self {
159 Self {
160 x: -self.x,
161 y: -self.y,
162 z: -self.z,
163 w: self.w,
164 }
165 }
166
167 #[inline]
170 pub fn inverse(&self) -> Self {
171 let mag_squared = self.magnitude_squared();
172 if mag_squared > EPSILON {
173 self.conjugate() * (1.0 / mag_squared)
174 } else {
175 Self::IDENTITY
176 }
177 }
178
179 #[inline]
181 pub fn dot(&self, other: Self) -> f32 {
182 self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
183 }
184
185 pub fn rotate_vec3(&self, v: Vec3) -> Vec3 {
187 let u = Vec3::new(self.x, self.y, self.z);
188 let s: f32 = self.w;
189 2.0 * u.dot(v) * u + (s * s - u.dot(u)) * v + 2.0 * s * u.cross(v)
190 }
191
192 pub fn slerp(start: Self, end: Self, t: f32) -> Self {
199 let t = t.clamp(0.0, 1.0);
200 let mut cos_theta = start.dot(end);
201 let mut end_adjusted = end;
202
203 if cos_theta < 0.0 {
207 cos_theta = -cos_theta;
208 end_adjusted = -end;
209 }
210
211 if cos_theta > 1.0 - EPSILON {
212 let result = (start * (1.0 - t)) + (end_adjusted * t);
215 result.normalize()
216 } else {
217 let angle = cos_theta.acos();
218 let sin_theta_inv = 1.0 / angle.sin();
219 let scale_start = ((1.0 - t) * angle).sin() * sin_theta_inv;
220 let scale_end = (t * angle).sin() * sin_theta_inv;
221 (start * scale_start) + (end_adjusted * scale_end)
222 }
223 }
224}
225
226impl Default for Quaternion {
229 #[inline]
231 fn default() -> Self {
232 Self::IDENTITY
233 }
234}
235
236impl Mul<Quaternion> for Quaternion {
237 type Output = Self;
238 #[inline]
241 fn mul(self, rhs: Self) -> Self::Output {
242 Self {
243 x: self.w * rhs.x + self.x * rhs.w + self.y * rhs.z - self.z * rhs.y,
244 y: self.w * rhs.y - self.x * rhs.z + self.y * rhs.w + self.z * rhs.x,
245 z: self.w * rhs.z + self.x * rhs.y - self.y * rhs.x + self.z * rhs.w,
246 w: self.w * rhs.w - self.x * rhs.x - self.y * rhs.y - self.z * rhs.z,
247 }
248 }
249}
250
251impl MulAssign<Quaternion> for Quaternion {
252 #[inline]
254 fn mul_assign(&mut self, rhs: Self) {
255 *self = *self * rhs;
256 }
257}
258
259impl Mul<Vec3> for Quaternion {
260 type Output = Vec3;
261 #[inline]
263 fn mul(self, rhs: Vec3) -> Self::Output {
264 self.normalize().rotate_vec3(rhs)
265 }
266}
267
268impl Add<Quaternion> for Quaternion {
269 type Output = Self;
270 #[inline]
273 fn add(self, rhs: Self) -> Self::Output {
274 Self {
275 x: self.x + rhs.x,
276 y: self.y + rhs.y,
277 z: self.z + rhs.z,
278 w: self.w + rhs.w,
279 }
280 }
281}
282
283impl Sub<Quaternion> for Quaternion {
284 type Output = Self;
285 #[inline]
287 fn sub(self, rhs: Self) -> Self::Output {
288 Self {
289 x: self.x - rhs.x,
290 y: self.y - rhs.y,
291 z: self.z - rhs.z,
292 w: self.w - rhs.w,
293 }
294 }
295}
296
297impl Mul<f32> for Quaternion {
298 type Output = Self;
299 #[inline]
301 fn mul(self, scalar: f32) -> Self::Output {
302 Self {
303 x: self.x * scalar,
304 y: self.y * scalar,
305 z: self.z * scalar,
306 w: self.w * scalar,
307 }
308 }
309}
310
311impl Neg for Quaternion {
312 type Output = Self;
313 #[inline]
315 fn neg(self) -> Self::Output {
316 Self {
317 x: -self.x,
318 y: -self.y,
319 z: -self.z,
320 w: -self.w,
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*; use crate::math::vector::Vec4;
329 use approx::assert_relative_eq; fn quat_approx_eq(q1: Quaternion, q2: Quaternion) -> bool {
332 let dot = q1.dot(q2).abs();
333 approx::relative_eq!(dot, 1.0, epsilon = EPSILON * 10.0) }
335
336 #[test]
337 fn test_identity_and_default() {
338 let q_ident = Quaternion::IDENTITY;
339 let q_def = Quaternion::default();
340 assert_eq!(q_ident, q_def);
341 assert_relative_eq!(q_ident.x, 0.0);
342 assert_relative_eq!(q_ident.y, 0.0);
343 assert_relative_eq!(q_ident.z, 0.0);
344 assert_relative_eq!(q_ident.w, 1.0);
345 assert_relative_eq!(q_ident.magnitude(), 1.0, epsilon = EPSILON);
346 }
347
348 #[test]
349 fn test_from_axis_angle() {
350 let axis = Vec3::Y;
351 let angle = std::f32::consts::FRAC_PI_2; let q = Quaternion::from_axis_angle(axis, angle);
353
354 let half_angle = angle * 0.5;
355 let expected_s = half_angle.sin();
356 let expected_c = half_angle.cos();
357
358 assert_relative_eq!(q.x, 0.0 * expected_s, epsilon = EPSILON);
359 assert_relative_eq!(q.y, 1.0 * expected_s, epsilon = EPSILON);
360 assert_relative_eq!(q.z, 0.0 * expected_s, epsilon = EPSILON);
361 assert_relative_eq!(q.w, expected_c, epsilon = EPSILON);
362 assert_relative_eq!(q.magnitude(), 1.0, epsilon = EPSILON);
363 }
364
365 #[test]
366 fn test_from_axis_angle_normalizes_axis() {
367 let axis = Vec3::new(0.0, 5.0, 0.0); let angle = std::f32::consts::FRAC_PI_2;
369 let q = Quaternion::from_axis_angle(axis, angle);
370
371 let half_angle = angle * 0.5;
372 let expected_s = half_angle.sin();
373 let expected_c = half_angle.cos();
374
375 assert_relative_eq!(q.x, 0.0 * expected_s, epsilon = EPSILON);
376 assert_relative_eq!(q.y, 1.0 * expected_s, epsilon = EPSILON); assert_relative_eq!(q.z, 0.0 * expected_s, epsilon = EPSILON);
378 assert_relative_eq!(q.w, expected_c, epsilon = EPSILON);
379 assert_relative_eq!(q.magnitude(), 1.0, epsilon = EPSILON);
380 }
381
382 #[test]
383 fn test_from_rotation_matrix_identity() {
384 let m = Mat4::IDENTITY;
385 let q = Quaternion::from_rotation_matrix(&m);
386 assert!(quat_approx_eq(q, Quaternion::IDENTITY));
387 }
388
389 #[test]
390 fn test_from_rotation_matrix_simple_rotations() {
391 let angle = std::f32::consts::FRAC_PI_4; let mx = Mat4::from_rotation_x(angle);
395 let qx_expected = Quaternion::from_axis_angle(Vec3::X, angle);
396 let qx_from_m = Quaternion::from_rotation_matrix(&mx);
397 assert!(quat_approx_eq(qx_from_m, qx_expected));
398
399 let my = Mat4::from_rotation_y(angle);
401 let qy_expected = Quaternion::from_axis_angle(Vec3::Y, angle);
402 let qy_from_m = Quaternion::from_rotation_matrix(&my);
403 assert!(quat_approx_eq(qy_from_m, qy_expected));
404
405 let mz = Mat4::from_rotation_z(angle);
407 let qz_expected = Quaternion::from_axis_angle(Vec3::Z, angle);
408 let qz_from_m = Quaternion::from_rotation_matrix(&mz);
409 assert!(quat_approx_eq(qz_from_m, qz_expected));
410 }
411
412 #[test]
413 fn test_matrix_to_quat_and_back() {
414 let axis = Vec3::new(-1.0, 2.5, 0.7).normalize();
415 let angle = 1.85; let q_orig = Quaternion::from_axis_angle(axis, angle);
418 let m_from_q = Mat4::from_quat(q_orig);
419
420 let q_from_m = Quaternion::from_rotation_matrix(&m_from_q);
421 let m_from_q_again = Mat4::from_quat(q_from_m);
422
423 assert!(quat_approx_eq(q_orig, q_from_m));
425
426 let v = Vec3::new(1.0, 1.0, 1.0);
431 let v_rot_orig = m_from_q * Vec4::from_vec3(v, 1.0);
432 let v_rot_new = m_from_q_again * Vec4::from_vec3(v, 1.0);
433 assert_relative_eq!(v_rot_orig.x, v_rot_new.x, epsilon = EPSILON);
434 assert_relative_eq!(v_rot_orig.y, v_rot_new.y, epsilon = EPSILON);
435 assert_relative_eq!(v_rot_orig.z, v_rot_new.z, epsilon = EPSILON);
436 }
437
438 #[test]
439 fn test_conjugate_and_inverse_unit() {
440 let axis = Vec3::new(1.0, 2.0, 3.0).normalize();
441 let angle = 0.75;
442 let q = Quaternion::from_axis_angle(axis, angle);
443 let q_conj = q.conjugate();
444 let q_inv = q.inverse();
445
446 assert_relative_eq!(q_conj.x, q_inv.x, epsilon = EPSILON);
447 assert_relative_eq!(q_conj.y, q_inv.y, epsilon = EPSILON);
448 assert_relative_eq!(q_conj.z, q_inv.z, epsilon = EPSILON);
449 assert_relative_eq!(q_conj.w, q_inv.w, epsilon = EPSILON);
450
451 assert_relative_eq!(q_conj.x, -q.x, epsilon = EPSILON);
452 assert_relative_eq!(q_conj.y, -q.y, epsilon = EPSILON);
453 assert_relative_eq!(q_conj.z, -q.z, epsilon = EPSILON);
454 assert_relative_eq!(q_conj.w, q.w, epsilon = EPSILON);
455 }
456
457 #[test]
458 fn test_multiplication_identity() {
459 let axis = Vec3::Y;
460 let angle = std::f32::consts::FRAC_PI_2;
461 let q = Quaternion::from_axis_angle(axis, angle);
462
463 let res_qi = q * Quaternion::IDENTITY;
464 let res_iq = Quaternion::IDENTITY * q;
465
466 assert_relative_eq!(res_qi.x, q.x, epsilon = EPSILON);
467 assert_relative_eq!(res_qi.y, q.y, epsilon = EPSILON);
468 assert_relative_eq!(res_qi.z, q.z, epsilon = EPSILON);
469 assert_relative_eq!(res_qi.w, q.w, epsilon = EPSILON);
470
471 assert_relative_eq!(res_iq.x, q.x, epsilon = EPSILON);
472 assert_relative_eq!(res_iq.y, q.y, epsilon = EPSILON);
473 assert_relative_eq!(res_iq.z, q.z, epsilon = EPSILON);
474 assert_relative_eq!(res_iq.w, q.w, epsilon = EPSILON);
475 }
476
477 #[test]
478 fn test_multiplication_composition() {
479 let rot_y = Quaternion::from_axis_angle(Vec3::Y, std::f32::consts::FRAC_PI_2);
480 let rot_x = Quaternion::from_axis_angle(Vec3::X, std::f32::consts::FRAC_PI_2);
481 let combined_rot = rot_x * rot_y; let v_start = Vec3::Z;
484 let v_after_y = rot_y * v_start;
485 let v_after_x_then_y = rot_x * v_after_y;
486 let v_combined = combined_rot * v_start;
487
488 assert_relative_eq!(v_after_x_then_y.x, 1.0, epsilon = EPSILON);
489 assert_relative_eq!(v_after_x_then_y.y, 0.0, epsilon = EPSILON);
490 assert_relative_eq!(v_after_x_then_y.z, 0.0, epsilon = EPSILON);
491
492 assert_relative_eq!(v_combined.x, v_after_x_then_y.x, epsilon = EPSILON);
493 assert_relative_eq!(v_combined.y, v_after_x_then_y.y, epsilon = EPSILON);
494 assert_relative_eq!(v_combined.z, v_after_x_then_y.z, epsilon = EPSILON);
495 }
496
497 #[test]
498 fn test_multiplication_inverse() {
499 let axis = Vec3::new(1.0, -2.0, 0.5).normalize();
500 let angle = 1.2;
501 let q = Quaternion::from_axis_angle(axis, angle);
502 let q_inv = q.inverse();
503
504 let result_forward = q * q_inv;
505 let result_backward = q_inv * q;
506
507 assert_relative_eq!(result_forward.x, Quaternion::IDENTITY.x, epsilon = EPSILON);
508 assert_relative_eq!(result_forward.y, Quaternion::IDENTITY.y, epsilon = EPSILON);
509 assert_relative_eq!(result_forward.z, Quaternion::IDENTITY.z, epsilon = EPSILON);
510 assert_relative_eq!(result_forward.w, Quaternion::IDENTITY.w, epsilon = EPSILON);
511
512 assert_relative_eq!(result_backward.x, Quaternion::IDENTITY.x, epsilon = EPSILON);
513 assert_relative_eq!(result_backward.y, Quaternion::IDENTITY.y, epsilon = EPSILON);
514 assert_relative_eq!(result_backward.z, Quaternion::IDENTITY.z, epsilon = EPSILON);
515 assert_relative_eq!(result_backward.w, Quaternion::IDENTITY.w, epsilon = EPSILON);
516 }
517
518 #[test]
519 fn test_rotate_vec3_and_operator() {
520 let axis = Vec3::Y;
521 let angle = std::f32::consts::FRAC_PI_2;
522 let q = Quaternion::from_axis_angle(axis, angle);
523
524 let v_in = Vec3::X;
525 let v_out_method = q.rotate_vec3(v_in);
526 let v_out_operator = q * v_in;
527 let v_expected = Vec3::new(0.0, 0.0, -1.0);
528
529 assert_relative_eq!(v_out_method.x, v_expected.x, epsilon = EPSILON);
530 assert_relative_eq!(v_out_method.y, v_expected.y, epsilon = EPSILON);
531 assert_relative_eq!(v_out_method.z, v_expected.z, epsilon = EPSILON);
532
533 assert_relative_eq!(v_out_operator.x, v_expected.x, epsilon = EPSILON);
534 assert_relative_eq!(v_out_operator.y, v_expected.y, epsilon = EPSILON);
535 assert_relative_eq!(v_out_operator.z, v_expected.z, epsilon = EPSILON);
536 }
537
538 #[test]
539 fn test_normalization() {
540 let q_non_unit = Quaternion::new(1.0, 2.0, 3.0, 4.0);
541 let q_norm = q_non_unit.normalize();
542 assert_relative_eq!(q_norm.magnitude(), 1.0, epsilon = EPSILON);
543
544 let q_mut = q_non_unit;
545 let q_mut = q_mut.normalize();
546 assert_relative_eq!(q_mut.magnitude(), 1.0, epsilon = EPSILON);
547
548 assert_relative_eq!(q_mut.x, q_norm.x, epsilon = EPSILON);
549 assert_relative_eq!(q_mut.y, q_norm.y, epsilon = EPSILON);
550 assert_relative_eq!(q_mut.z, q_norm.z, epsilon = EPSILON);
551 assert_relative_eq!(q_mut.w, q_norm.w, epsilon = EPSILON);
552 }
553
554 #[test]
555 fn test_normalize_zero_quaternion() {
556 let q_zero = Quaternion::new(0.0, 0.0, 0.0, 0.0);
557 let q_norm = q_zero.normalize();
558 assert_eq!(q_norm, Quaternion::IDENTITY);
559 }
560
561 #[test]
562 fn test_dot_product() {
563 let angle = 0.5;
564 let q1 = Quaternion::from_axis_angle(Vec3::X, angle);
565 let q2 = Quaternion::from_axis_angle(Vec3::X, angle);
566 let q3 = Quaternion::from_axis_angle(Vec3::Y, angle);
567 let q4 = Quaternion::from_axis_angle(Vec3::X, -angle);
568
569 assert_relative_eq!(q1.dot(q1), 1.0, epsilon = EPSILON);
570 assert_relative_eq!(q1.dot(q2), 1.0, epsilon = EPSILON);
571 assert!(q1.dot(q3).abs() < 1.0 - EPSILON);
572 assert_relative_eq!(q1.dot(q4), angle.cos(), epsilon = EPSILON);
573 }
574
575 #[test]
576 fn test_slerp_endpoints() {
577 let q_start = Quaternion::IDENTITY;
578 let q_end = Quaternion::from_axis_angle(Vec3::Z, std::f32::consts::FRAC_PI_2);
579
580 let q_t0 = Quaternion::slerp(q_start, q_end, 0.0);
581 let q_t1 = Quaternion::slerp(q_start, q_end, 1.0);
582
583 assert_relative_eq!(q_t0.x, q_start.x, epsilon = EPSILON);
584 assert_relative_eq!(q_t0.y, q_start.y, epsilon = EPSILON);
585 assert_relative_eq!(q_t0.z, q_start.z, epsilon = EPSILON);
586 assert_relative_eq!(q_t0.w, q_start.w, epsilon = EPSILON);
587
588 assert_relative_eq!(q_t1.x, q_end.x, epsilon = EPSILON);
589 assert_relative_eq!(q_t1.y, q_end.y, epsilon = EPSILON);
590 assert_relative_eq!(q_t1.z, q_end.z, epsilon = EPSILON);
591 assert_relative_eq!(q_t1.w, q_end.w, epsilon = EPSILON);
592 }
593
594 #[test]
595 fn test_slerp_midpoint() {
596 let q_start = Quaternion::IDENTITY;
597 let q_end = Quaternion::from_axis_angle(Vec3::Z, std::f32::consts::FRAC_PI_2);
598 let q_half = Quaternion::slerp(q_start, q_end, 0.5);
599 let q_expected_half =
600 Quaternion::from_axis_angle(Vec3::Z, std::f32::consts::FRAC_PI_2 * 0.5);
601
602 assert_relative_eq!(q_half.x, q_expected_half.x, epsilon = EPSILON);
603 assert_relative_eq!(q_half.y, q_expected_half.y, epsilon = EPSILON);
604 assert_relative_eq!(q_half.z, q_expected_half.z, epsilon = EPSILON);
605 assert_relative_eq!(q_half.w, q_expected_half.w, epsilon = EPSILON);
606 assert_relative_eq!(q_half.magnitude(), 1.0, epsilon = EPSILON);
607 }
608
609 #[test]
610 fn test_slerp_short_path_handling() {
611 let q_start = Quaternion::from_axis_angle(Vec3::Y, -30.0f32.to_radians());
612 let q_end = Quaternion::from_axis_angle(Vec3::Y, 170.0f32.to_radians());
613 assert!(q_start.dot(q_end) < 0.0);
614
615 let q_mid = Quaternion::slerp(q_start, q_end, 0.5);
616 let q_expected_mid = Quaternion::from_axis_angle(Vec3::Y, -110.0f32.to_radians()); assert_relative_eq!(q_mid.dot(q_expected_mid).abs(), 1.0, epsilon = EPSILON);
619
620 let v = Vec3::X;
621 let v_rotated_mid = q_mid * v;
622 let v_rotated_expected = q_expected_mid * v;
623 assert_relative_eq!(v_rotated_mid.x, v_rotated_expected.x, epsilon = EPSILON);
624 assert_relative_eq!(v_rotated_mid.y, v_rotated_expected.y, epsilon = EPSILON);
625 assert_relative_eq!(v_rotated_mid.z, v_rotated_expected.z, epsilon = EPSILON);
626 }
627
628 #[test]
629 fn test_slerp_near_identical_quaternions() {
630 let angle1 = 0.00001;
631 let angle2 = 0.00002;
632 let q_close1 = Quaternion::from_axis_angle(Vec3::Y, angle1);
633 let q_close2 = Quaternion::from_axis_angle(Vec3::Y, angle2);
634 assert!(q_close1.dot(q_close2) > 1.0 - EPSILON);
635
636 let q_mid = Quaternion::slerp(q_close1, q_close2, 0.5);
637 let angle_mid = angle1 + (angle2 - angle1) * 0.5;
638 let q_expected = Quaternion::from_axis_angle(Vec3::Y, angle_mid);
639
640 assert_relative_eq!(q_mid.magnitude(), 1.0, epsilon = EPSILON * 10.0);
641
642 let v = Vec3::X;
643 let v_rotated = q_mid * v;
644 let v_expected_rotated = q_expected * v;
645 assert_relative_eq!(v_rotated.x, v_expected_rotated.x, epsilon = EPSILON * 10.0);
646 assert_relative_eq!(v_rotated.y, v_expected_rotated.y, epsilon = EPSILON * 10.0);
647 assert_relative_eq!(v_rotated.z, v_expected_rotated.z, epsilon = EPSILON * 10.0);
648 }
649
650 #[test]
651 fn test_slerp_clamps_t() {
652 let q_start = Quaternion::IDENTITY;
653 let q_end = Quaternion::from_axis_angle(Vec3::Z, std::f32::consts::FRAC_PI_2);
654
655 let q_t_neg = Quaternion::slerp(q_start, q_end, -0.5); let q_t_large = Quaternion::slerp(q_start, q_end, 1.5); assert_relative_eq!(q_t_neg.x, q_start.x, epsilon = EPSILON);
659 assert_relative_eq!(q_t_neg.y, q_start.y, epsilon = EPSILON);
660 assert_relative_eq!(q_t_neg.z, q_start.z, epsilon = EPSILON);
661 assert_relative_eq!(q_t_neg.w, q_start.w, epsilon = EPSILON);
662
663 assert_relative_eq!(q_t_large.x, q_end.x, epsilon = EPSILON);
664 assert_relative_eq!(q_t_large.y, q_end.y, epsilon = EPSILON);
665 assert_relative_eq!(q_t_large.z, q_end.z, epsilon = EPSILON);
666 assert_relative_eq!(q_t_large.w, q_end.w, epsilon = EPSILON);
667 }
668}