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