1use crate::matrix::{Matrix3, Matrix4};
31use crate::scalar::*;
32use crate::vector::{FloatVector, Vector, Vector3};
33use core::ops::*;
34use num_traits::{One, Zero};
35
36#[repr(C)]
37#[derive(Copy, Clone, Debug, Default)]
38pub struct Quat<T: Scalar> {
40 pub x: T,
42 pub y: T,
44 pub z: T,
46 pub w: T,
48}
49
50impl<T: FloatScalar> Quat<T> {
51 #[allow(clippy::too_many_arguments)]
52 fn from_rotation_matrix(
53 m00: T,
54 m01: T,
55 m02: T,
56 m10: T,
57 m11: T,
58 m12: T,
59 m20: T,
60 m21: T,
61 m22: T,
62 ) -> Self {
63 let t = <T as One>::one() + m00 + m11 + m22;
64
65 if t > <T as Zero>::zero() {
66 let s = T::tsqrt(t) * T::two();
67
68 let x = (m21 - m12) / s;
69 let y = (m02 - m20) / s;
70 let z = (m10 - m01) / s;
71 let w = T::quarter() * s;
72 Self::new(x, y, z, w)
73 } else if m00 > m11 && m00 > m22 {
74 let s = T::tsqrt(<T as One>::one() + m00 - m11 - m22) * T::two();
75 let x = T::quarter() * s;
76 let y = (m10 + m01) / s;
77 let z = (m02 + m20) / s;
78 let w = (m21 - m12) / s;
79 Self::new(x, y, z, w)
80 } else if m11 > m22 {
81 let s = T::tsqrt(<T as One>::one() + m11 - m00 - m22) * T::two();
82 let x = (m10 + m01) / s;
83 let y = T::quarter() * s;
84 let z = (m21 + m12) / s;
85 let w = (m02 - m20) / s;
86 Self::new(x, y, z, w)
87 } else {
88 let s = T::tsqrt(<T as One>::one() + m22 - m00 - m11) * T::two();
89 let x = (m02 + m20) / s;
90 let y = (m21 + m12) / s;
91 let z = T::quarter() * s;
92 let w = (m10 - m01) / s;
93 Self::new(x, y, z, w)
94 }
95 }
96
97 pub fn identity() -> Self {
99 Self {
100 x: <T as Zero>::zero(),
101 y: <T as Zero>::zero(),
102 z: <T as Zero>::zero(),
103 w: <T as One>::one(),
104 }
105 }
106 pub fn new(x: T, y: T, z: T, w: T) -> Self {
108 Self { x, y, z, w }
109 }
110
111 pub fn dot(l: &Self, r: &Self) -> T {
117 l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w
118 }
119
120 pub fn length(&self) -> T {
126 T::tsqrt(Self::dot(self, self))
127 }
128
129 pub fn conjugate(q: &Self) -> Self {
136 Self::new(-q.x, -q.y, -q.z, q.w)
137 }
138
139 pub fn normalize(q: &Self) -> Self {
146 let ql = q.length();
147 if ql > <T as Zero>::zero() {
148 *q / ql
149 } else {
150 *q
151 }
152 }
153
154 pub fn neg(q: &Self) -> Self {
158 Self::new(-q.x, -q.y, -q.z, -q.w)
159 }
160 pub fn add(l: &Self, r: &Self) -> Self {
162 Self::new(l.x + r.x, l.y + r.y, l.z + r.z, l.w + r.w)
163 }
164 pub fn sub(l: &Self, r: &Self) -> Self {
166 Self::new(l.x - r.x, l.y - r.y, l.z - r.z, l.w - r.w)
167 }
168 pub fn mul(l: &Self, r: &Self) -> Self {
170 let x = l.w * r.x + l.x * r.w + l.y * r.z - l.z * r.y;
171 let y = l.w * r.y + l.y * r.w + l.z * r.x - l.x * r.z;
172 let z = l.w * r.z + l.z * r.w + l.x * r.y - l.y * r.x;
173 let w = l.w * r.w - l.x * r.x - l.y * r.y - l.z * r.z;
174 Self::new(x, y, z, w)
175 }
176
177 pub fn mulf(l: &Self, r: T) -> Self {
179 Self::new(l.x * r, l.y * r, l.z * r, l.w * r)
180 }
181 pub fn fmul(l: T, r: &Self) -> Self {
183 Self::new(l * r.x, l * r.y, l * r.z, l * r.w)
184 }
185 pub fn divf(l: &Self, r: T) -> Self {
187 Self::new(l.x / r, l.y / r, l.z / r, l.w / r)
188 }
189 pub fn fdiv(l: T, r: &Self) -> Self {
191 Self::new(l / r.x, l / r.y, l / r.z, l / r.w)
192 }
193 pub fn inverse(q: &Self) -> Self {
198 let denom = Self::dot(q, q);
199 if denom > T::epsilon() {
200 Self::conjugate(q) / denom
201 } else {
202 *q
203 }
204 }
205
206 pub fn mat3(&self) -> Matrix3<T> {
214 let xx = self.x * self.x;
215 let xy = self.x * self.y;
216 let xz = self.x * self.z;
217 let xw = self.x * self.w;
218 let yy = self.y * self.y;
219 let yz = self.y * self.z;
220 let yw = self.y * self.w;
221 let zz = self.z * self.z;
222 let zw = self.z * self.w;
223
224 let m00 = <T as One>::one() - T::two() * (yy + zz);
225 let m10 = T::two() * (xy + zw);
226 let m20 = T::two() * (xz - yw);
227 let m01 = T::two() * (xy - zw);
228 let m11 = <T as One>::one() - T::two() * (xx + zz);
229 let m21 = T::two() * (yz + xw);
230 let m02 = T::two() * (xz + yw);
231 let m12 = T::two() * (yz - xw);
232 let m22 = <T as One>::one() - T::two() * (xx + yy);
233
234 Matrix3::new(m00, m10, m20, m01, m11, m21, m02, m12, m22)
235 }
236
237 pub fn mat4(&self) -> Matrix4<T> {
242 let xx = self.x * self.x;
243 let xy = self.x * self.y;
244 let xz = self.x * self.z;
245 let xw = self.x * self.w;
246 let yy = self.y * self.y;
247 let yz = self.y * self.z;
248 let yw = self.y * self.w;
249 let zz = self.z * self.z;
250 let zw = self.z * self.w;
251
252 let m00 = <T as One>::one() - T::two() * (yy + zz);
253 let m10 = T::two() * (xy + zw);
254 let m20 = T::two() * (xz - yw);
255 let m01 = T::two() * (xy - zw);
256 let m11 = <T as One>::one() - T::two() * (xx + zz);
257 let m21 = T::two() * (yz + xw);
258 let m02 = T::two() * (xz + yw);
259 let m12 = T::two() * (yz - xw);
260 let m22 = <T as One>::one() - T::two() * (xx + yy);
261
262 Matrix4::new(
263 m00,
264 m10,
265 m20,
266 <T as Zero>::zero(),
267 m01,
268 m11,
269 m21,
270 <T as Zero>::zero(),
271 m02,
272 m12,
273 m22,
274 <T as Zero>::zero(),
275 <T as Zero>::zero(),
276 <T as Zero>::zero(),
277 <T as Zero>::zero(),
278 <T as One>::one(),
279 )
280 }
281
282 pub fn to_axis_angle(&self) -> (Vector3<T>, T) {
294 let nq = Self::normalize(self);
295 let one = <T as One>::one();
296 let mut cos_a = nq.w;
297 cos_a = T::min(one, T::max(-one, cos_a));
298 let angle = T::tacos(cos_a) * T::two();
299 let axis_vec = Vector3::new(nq.x, nq.y, nq.z);
300 let axis_len = axis_vec.length();
301 if axis_len <= T::epsilon() {
302 (
303 Vector3::new(one, <T as Zero>::zero(), <T as Zero>::zero()),
304 angle,
305 )
306 } else {
307 (axis_vec / axis_len, angle)
308 }
309 }
310
311 pub fn of_matrix3(m: &Matrix3<T>) -> Self {
313 Self::from_rotation_matrix(
314 m.col[0].x, m.col[1].x, m.col[2].x, m.col[0].y, m.col[1].y, m.col[2].y, m.col[0].z,
315 m.col[1].z, m.col[2].z,
316 )
317 }
318
319 pub fn of_matrix4(m: &Matrix4<T>) -> Self {
321 Self::from_rotation_matrix(
322 m.col[0].x, m.col[1].x, m.col[2].x, m.col[0].y, m.col[1].y, m.col[2].y, m.col[0].z,
323 m.col[1].z, m.col[2].z,
324 )
325 }
326
327 pub fn of_axis_angle(axis: &Vector3<T>, angle: T, epsilon: T) -> Option<Self> {
343 let len_sq = Vector3::dot(axis, axis);
344 if len_sq <= epsilon * epsilon {
345 return None;
346 }
347 let half_angle = angle * T::half();
348 let sin_a = T::tsin(half_angle);
349 let cos_a = T::tcos(half_angle);
350 let inv_len = <T as One>::one() / len_sq.tsqrt();
351 let n = *axis * inv_len * sin_a;
352
353 let x = n.x;
354 let y = n.y;
355 let z = n.z;
356 let w = cos_a;
357 Some(Self::new(x, y, z, w))
358 }
359}
360
361impl<T: FloatScalar> Add for Quat<T> {
362 type Output = Quat<T>;
363 fn add(self, rhs: Self) -> Self {
364 Self::add(&self, &rhs)
365 }
366}
367
368impl<T: FloatScalar> Sub for Quat<T> {
369 type Output = Quat<T>;
370 fn sub(self, rhs: Self) -> Self {
371 Self::sub(&self, &rhs)
372 }
373}
374
375impl<T: FloatScalar> Mul for Quat<T> {
376 type Output = Quat<T>;
377 fn mul(self, rhs: Self) -> Self {
378 Self::mul(&self, &rhs)
379 }
380}
381
382impl<T: FloatScalar> Div<T> for Quat<T> {
383 type Output = Quat<T>;
384 fn div(self, t: T) -> Self {
385 Self::divf(&self, t)
386 }
387}
388
389impl<T: FloatScalar> Mul<T> for Quat<T> {
390 type Output = Quat<T>;
391 fn mul(self, t: T) -> Self {
392 Self::mulf(&self, t)
393 }
394}
395
396macro_rules! impl_scalar {
397 ($Op:ident, $op:ident, $Opop:ident, $t:ident) => {
398 impl $Op<Quat<$t>> for $t {
399 type Output = Quat<$t>;
400 fn $op(self, q: Quat<$t>) -> Quat<$t> {
401 Quat::$Opop(self, &q)
402 }
403 }
404 };
405}
406
407impl_scalar!(Div, div, fdiv, f32);
408impl_scalar!(Mul, mul, fmul, f32);
409impl_scalar!(Div, div, fdiv, f64);
410impl_scalar!(Mul, mul, fmul, f64);
411
412#[cfg(test)]
413mod tests {
414 use core::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
415
416 use crate::matrix::*;
417 use crate::scalar::*;
418 use crate::vector::*;
419 use crate::Quat;
420
421 fn quat_axis_angle_f32(axis: &Vector3<f32>, angle: f32) -> Quat<f32> {
422 Quat::of_axis_angle(axis, angle, EPS_F32).expect("axis length too small")
423 }
424
425 fn quat_axis_angle_f64(axis: &Vector3<f64>, angle: f64) -> Quat<f64> {
426 Quat::of_axis_angle(axis, angle, EPS_F64).expect("axis length too small")
427 }
428
429 fn mat3_axis_angle_f32(axis: &Vector3<f32>, angle: f32) -> Matrix3<f32> {
430 Matrix3::of_axis_angle(axis, angle, EPS_F32).expect("axis length too small")
431 }
432
433 fn assert_vec3_close_f32(actual: Vector3<f32>, expected: Vector3<f32>) {
434 assert!((actual - expected).length() < f32::epsilon());
435 }
436
437 fn assert_vec3_close_f64(actual: Vector3<f64>, expected: Vector3<f64>) {
438 assert!((actual - expected).length() < f64::epsilon());
439 }
440
441 fn assert_scalar_close_f32(actual: f32, expected: f32) {
442 assert!((actual - expected).abs() < f32::epsilon());
443 }
444
445 fn assert_scalar_close_f64(actual: f64, expected: f64) {
446 assert!((actual - expected).abs() < f64::epsilon());
447 }
448
449 #[test]
450 fn test_identity() {
451 let q = Quat::<f32>::identity();
452 let m = q.mat3();
453
454 let x_axis = m.col[0];
455 let y_axis = m.col[1];
456 let z_axis = m.col[2];
457
458 assert_vec3_close_f32(x_axis, Vector3::new(1.0, 0.0, 0.0));
459 assert_vec3_close_f32(y_axis, Vector3::new(0.0, 1.0, 0.0));
460 assert_vec3_close_f32(z_axis, Vector3::new(0.0, 0.0, 1.0));
461 }
462
463 #[test]
464 fn test_identity_conjugate() {
465 let q = Quat::conjugate(&Quat::<f32>::identity());
466 let m = q.mat3();
467
468 let x_axis = m.col[0];
469 let y_axis = m.col[1];
470 let z_axis = m.col[2];
471
472 assert_vec3_close_f32(x_axis, Vector3::new(1.0, 0.0, 0.0));
473 assert_vec3_close_f32(y_axis, Vector3::new(0.0, 1.0, 0.0));
474 assert_vec3_close_f32(z_axis, Vector3::new(0.0, 0.0, 1.0));
475 }
476
477 #[test]
478 fn test_rot_180() {
479 let v = Vector3::<f32>::new(1.0, 0.0, 0.0);
480 let a = PI;
481
482 let q = quat_axis_angle_f32(&v, a);
483 let m = q.mat3();
484
485 let x_axis = m.col[0];
486 let y_axis = m.col[1];
487 let z_axis = m.col[2];
488
489 assert_vec3_close_f32(x_axis, Vector3::new(1.0, 0.0, 0.0));
490 assert_vec3_close_f32(y_axis, Vector3::new(0.0, -1.0, 0.0));
491 assert_vec3_close_f32(z_axis, Vector3::new(0.0, 0.0, -1.0));
492
493 let m2 = mat3_axis_angle_f32(&v, a);
494
495 let x2_axis = m2.col[0];
496 let y2_axis = m2.col[1];
497 let z2_axis = m2.col[2];
498
499 assert_vec3_close_f32(x_axis, x2_axis);
500 assert_vec3_close_f32(y_axis, y2_axis);
501 assert_vec3_close_f32(z_axis, z2_axis);
502 }
503
504 #[test]
505 fn test_rot_90() {
506 let v = Vector3::<f32>::new(1.0, 0.0, 0.0);
507 let a = FRAC_PI_2;
508
509 let q = quat_axis_angle_f32(&v, a);
510 let m = q.mat3();
511
512 let x_axis = m.col[0];
513 let y_axis = m.col[1];
514 let z_axis = m.col[2];
515
516 assert_vec3_close_f32(x_axis, Vector3::new(1.0, 0.0, 0.0));
517 assert_vec3_close_f32(y_axis, Vector3::new(0.0, 0.0, 1.0));
518 assert_vec3_close_f32(z_axis, Vector3::new(0.0, -1.0, 0.0));
519
520 let m2 = mat3_axis_angle_f32(&v, a);
521
522 let x2_axis = m2.col[0];
523 let y2_axis = m2.col[1];
524 let z2_axis = m2.col[2];
525
526 assert_vec3_close_f32(x_axis, x2_axis);
527 assert_vec3_close_f32(y_axis, y2_axis);
528 assert_vec3_close_f32(z_axis, z2_axis);
529 }
530
531 #[test]
532 fn test_rot_45() {
533 let v = Vector3::<f32>::new(1.0, 0.0, 0.0);
534 let a = FRAC_PI_4;
535
536 let q = quat_axis_angle_f32(&v, a);
537 let m = q.mat3();
538
539 let x_axis = m.col[0];
540 let y_axis = m.col[1];
541 let z_axis = m.col[2];
542
543 assert_vec3_close_f32(x_axis, Vector3::new(1.0, 0.0, 0.0));
544 assert_vec3_close_f32(
545 y_axis,
546 Vector3::new(0.0, f32::sqrt(2.0) / 2.0, f32::sqrt(2.0) / 2.0),
547 );
548 assert_vec3_close_f32(
549 z_axis,
550 Vector3::new(0.0, -f32::sqrt(2.0) / 2.0, f32::sqrt(2.0) / 2.0),
551 );
552
553 let m2 = mat3_axis_angle_f32(&v, a);
554
555 let x2_axis = m2.col[0];
556 let y2_axis = m2.col[1];
557 let z2_axis = m2.col[2];
558
559 assert_vec3_close_f32(x_axis, x2_axis);
560 assert_vec3_close_f32(y_axis, y2_axis);
561 assert_vec3_close_f32(z_axis, z2_axis);
562 }
563
564 #[test]
565 fn test_rot_composed_45() {
566 let v = Vector3::<f32>::new(1.0, 0.0, 0.0);
567 let a_u = FRAC_PI_4;
568 let a_f = PI;
569
570 let q_u = quat_axis_angle_f32(&v, a_u);
571 let q_f = quat_axis_angle_f32(&v, a_f);
572
573 let m = q_f.mat3();
574
575 let x_axis = m.col[0];
576 let y_axis = m.col[1];
577 let z_axis = m.col[2];
578
579 let q_t = q_u * q_u * q_u * q_u;
580 let m2 = q_t.mat3();
581
582 let x2_axis = m2.col[0];
583 let y2_axis = m2.col[1];
584 let z2_axis = m2.col[2];
585
586 assert_vec3_close_f32(x_axis, x2_axis);
587 assert_vec3_close_f32(y_axis, y2_axis);
588 assert_vec3_close_f32(z_axis, z2_axis);
589 }
590
591 #[test]
592 fn test_rot_composed_120() {
593 let v = Vector3::<f32>::new(1.0, 0.0, 0.0);
594 let a_u1 = FRAC_PI_2;
595 let a_u2 = PI / 6.0;
596 let a_f = FRAC_PI_2 + PI / 6.0;
597
598 let q_u1 = quat_axis_angle_f32(&v, a_u1);
599 let q_u2 = quat_axis_angle_f32(&v, a_u2);
600 let q_f = quat_axis_angle_f32(&v, a_f);
601
602 let m = q_f.mat3();
603
604 let x_axis = m.col[0];
605 let y_axis = m.col[1];
606 let z_axis = m.col[2];
607
608 let q_t = q_u2 * q_u1;
609 let m2 = q_t.mat3();
610
611 let x2_axis = m2.col[0];
612 let y2_axis = m2.col[1];
613 let z2_axis = m2.col[2];
614
615 assert_vec3_close_f32(x_axis, x2_axis);
616 assert_vec3_close_f32(y_axis, y2_axis);
617 assert_vec3_close_f32(z_axis, z2_axis);
618 }
619
620 #[test]
621 fn test_rot_x_rot_y() {
622 let vx = Vector3::<f32>::new(1.0, 0.0, 0.0);
623 let vy = Vector3::<f32>::new(0.0, 1.0, 0.0);
624 let vz = Vector3::<f32>::new(0.0, 0.0, 1.0);
625
626 let v4x = Vector4::<f32>::new(1.0, 0.0, 0.0, 1.0);
627 let v4y = Vector4::<f32>::new(0.0, 1.0, 0.0, 1.0);
628 let v4z = Vector4::<f32>::new(0.0, 0.0, 1.0, 1.0);
629
630 let a = FRAC_PI_2;
631
632 let q_ux = quat_axis_angle_f32(&vx, a);
633 let q_uy = quat_axis_angle_f32(&vy, a);
634 let q_uz = quat_axis_angle_f32(&vz, a);
635
636 let mx = q_ux.mat3();
637 let m4x = q_ux.mat4();
638
639 let px = (m4x * v4x).xyz();
640 let py = (m4x * v4y).xyz();
641 let pz = (m4x * v4z).xyz();
642
643 let x_axis = mx.col[0];
644 let y_axis = mx.col[1];
645 let z_axis = mx.col[2];
646
647 assert_vec3_close_f32(x_axis, vx);
648 assert_vec3_close_f32(y_axis, vz);
649 assert_vec3_close_f32(z_axis, -vy);
650
651 assert_vec3_close_f32(px, vx);
652 assert_vec3_close_f32(py, vz);
653 assert_vec3_close_f32(pz, -vy);
654
655 let q_yx = q_uy * q_ux;
657 let myx = q_yx.mat3();
658 let m4yx = q_yx.mat4();
659
660 let px = (m4yx * v4x).xyz();
661 let py = (m4yx * v4y).xyz();
662 let pz = (m4yx * v4z).xyz();
663
664 let x_axis = myx.col[0];
665 let y_axis = myx.col[1];
666 let z_axis = myx.col[2];
667
668 assert_vec3_close_f32(x_axis, -vz);
669 assert_vec3_close_f32(y_axis, vx);
670 assert_vec3_close_f32(z_axis, -vy);
671
672 assert_vec3_close_f32(px, -vz);
673 assert_vec3_close_f32(py, vx);
674 assert_vec3_close_f32(pz, -vy);
675
676 let q_zyx = q_uz * q_uy * q_ux;
678 let mzyx = q_zyx.mat3();
679
680 let m4zyx = q_zyx.mat4();
681
682 let px = (m4zyx * v4x).xyz();
683 let py = (m4zyx * v4y).xyz();
684 let pz = (m4zyx * v4z).xyz();
685
686 let x_axis = mzyx.col[0];
687 let y_axis = mzyx.col[1];
688 let z_axis = mzyx.col[2];
689
690 assert_vec3_close_f32(x_axis, -vz);
691 assert_vec3_close_f32(y_axis, vy);
692 assert_vec3_close_f32(z_axis, vx);
693
694 assert_vec3_close_f32(px, -vz);
695 assert_vec3_close_f32(py, vy);
696 assert_vec3_close_f32(pz, vx);
697 }
698
699 #[test]
700 fn test_axis_angle_f32() {
701 let v = Vector3::normalize(&Vector3::<f32>::new(1.0, 2.0, 3.0));
702 let q0 = quat_axis_angle_f32(&v, 3.0);
703 let (v2, a) = q0.to_axis_angle();
704 assert_vec3_close_f32(v, v2);
705 assert_scalar_close_f32(a, 3.0);
706 }
707
708 #[test]
709 fn test_axis_angle_f64() {
710 let v = Vector3::normalize(&Vector3::<f64>::new(1.0, 2.0, 3.0));
711 let q0 = quat_axis_angle_f64(&v, 3.0);
712 let (v2, a) = q0.to_axis_angle();
713 assert_vec3_close_f64(v, v2);
714 assert_scalar_close_f64(a, 3.0);
715 }
716
717 #[test]
718 fn test_axis_angle_zero_axis_none() {
719 let axis = Vector3::<f32>::new(0.0, 0.0, 0.0);
720 assert!(Quat::of_axis_angle(&axis, 1.0, EPS_F32).is_none());
721 }
722
723 #[test]
724 fn test_quaternion_normalization() {
725 let q = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
727 let nq = Quat::normalize(&q);
728 let len = nq.length();
729 assert!((len - 1.0).abs() < f32::epsilon());
730
731 let q_zero = Quat::<f32>::new(0.0, 0.0, 0.0, 0.0);
733 let nq_zero = Quat::normalize(&q_zero);
734 assert_eq!(nq_zero.x, 0.0);
735 assert_eq!(nq_zero.y, 0.0);
736 assert_eq!(nq_zero.z, 0.0);
737 assert_eq!(nq_zero.w, 0.0);
738
739 let q_unit = Quat::<f32>::identity();
741 let nq_unit = Quat::normalize(&q_unit);
742 assert!((nq_unit.length() - 1.0).abs() < f32::epsilon());
743 }
744
745 #[test]
746 fn test_quaternion_inverse() {
747 let q = quat_axis_angle_f32(&Vector3::new(0.0, 1.0, 0.0), FRAC_PI_2);
748 let q_inv = Quat::inverse(&q);
749 let product = q * q_inv;
750
751 assert!((product.x).abs() < 0.001);
753 assert!((product.y).abs() < 0.001);
754 assert!((product.z).abs() < 0.001);
755 assert!((product.w - 1.0).abs() < 0.001);
756 }
757
758 #[test]
759 fn test_quaternion_inverse_non_unit() {
760 let q = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
761 let q_inv = Quat::inverse(&q);
762 let product = q * q_inv;
763
764 assert!(product.x.abs() < 0.001);
765 assert!(product.y.abs() < 0.001);
766 assert!(product.z.abs() < 0.001);
767 assert!((product.w - 1.0).abs() < 0.001);
768 }
769
770 #[test]
771 fn test_quaternion_multiplication() {
772 let q1 = quat_axis_angle_f32(&Vector3::new(1.0, 0.0, 0.0), 0.5);
774 let q2 = quat_axis_angle_f32(&Vector3::new(0.0, 1.0, 0.0), 0.5);
775 let q3 = quat_axis_angle_f32(&Vector3::new(0.0, 0.0, 1.0), 0.5);
776
777 let left = (q1 * q2) * q3;
779 let right = q1 * (q2 * q3);
780 assert!((left.x - right.x).abs() < f32::epsilon());
781 assert!((left.y - right.y).abs() < f32::epsilon());
782 assert!((left.z - right.z).abs() < f32::epsilon());
783 assert!((left.w - right.w).abs() < f32::epsilon());
784
785 let identity = Quat::<f32>::identity();
787 let result = q1 * identity;
788 assert!((result.x - q1.x).abs() < f32::epsilon());
789 assert!((result.y - q1.y).abs() < f32::epsilon());
790 assert!((result.z - q1.z).abs() < f32::epsilon());
791 assert!((result.w - q1.w).abs() < f32::epsilon());
792 }
793
794 #[test]
795 fn test_quaternion_addition_subtraction() {
796 let q1 = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
797 let q2 = Quat::<f32>::new(5.0, 6.0, 7.0, 8.0);
798
799 let sum = q1 + q2;
800 assert_eq!(sum.x, 6.0);
801 assert_eq!(sum.y, 8.0);
802 assert_eq!(sum.z, 10.0);
803 assert_eq!(sum.w, 12.0);
804
805 let diff = q2 - q1;
806 assert_eq!(diff.x, 4.0);
807 assert_eq!(diff.y, 4.0);
808 assert_eq!(diff.z, 4.0);
809 assert_eq!(diff.w, 4.0);
810 }
811
812 #[test]
813 fn test_quaternion_scalar_multiplication() {
814 let q = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
815 let scalar = 2.0;
816
817 let result1 = q * scalar;
818 assert_eq!(result1.x, 2.0);
819 assert_eq!(result1.y, 4.0);
820 assert_eq!(result1.z, 6.0);
821 assert_eq!(result1.w, 8.0);
822
823 let result2 = scalar * q;
824 assert_eq!(result2.x, 2.0);
825 assert_eq!(result2.y, 4.0);
826 assert_eq!(result2.z, 6.0);
827 assert_eq!(result2.w, 8.0);
828
829 let result3 = q / scalar;
830 assert_eq!(result3.x, 0.5);
831 assert_eq!(result3.y, 1.0);
832 assert_eq!(result3.z, 1.5);
833 assert_eq!(result3.w, 2.0);
834 }
835
836 #[test]
837 fn test_to_axis_angle_edge_cases() {
838 let q_identity = Quat::<f32>::identity();
840 let (axis, angle) = q_identity.to_axis_angle();
841 assert!((angle).abs() < 0.001);
842 assert!((axis.x - 1.0).abs() < 0.001);
843 assert!(axis.y.abs() < 0.001);
844 assert!(axis.z.abs() < 0.001);
845
846 let q_clamped = Quat::<f32>::new(0.0, 0.0, 0.0, 1.00001);
848 let (axis_clamped, angle_clamped) = q_clamped.to_axis_angle();
849 assert!(!angle_clamped.is_nan());
850 assert!((axis_clamped.length() - 1.0).abs() < 0.01);
851 assert!(Vector3::dot(&axis_clamped, &Vector3::new(1.0, 0.0, 0.0)) > 0.99);
852
853 let q_180 = quat_axis_angle_f32(&Vector3::new(0.0, 1.0, 0.0), PI);
855 let (axis_180, angle_180) = q_180.to_axis_angle();
856 assert!((angle_180 - PI).abs() < 0.001);
857 assert!((axis_180.y - 1.0).abs() < 0.001);
858
859 let small_angle = 0.001;
861 let q_small = quat_axis_angle_f32(&Vector3::new(1.0, 0.0, 0.0), small_angle);
862 let (axis_small, angle_small) = q_small.to_axis_angle();
863 assert!((angle_small - small_angle).abs() < 0.0001);
864 assert!((axis_small.length() - 1.0).abs() < 0.01);
865 assert!(Vector3::dot(&axis_small, &Vector3::new(1.0, 0.0, 0.0)) > 0.99);
866 }
867
868 #[test]
869 fn test_matrix_quaternion_conversion() {
870 let angles = [0.0, 0.5, 1.0, FRAC_PI_2, PI];
872 let axes = [
873 Vector3::<f32>::new(1.0, 0.0, 0.0),
874 Vector3::<f32>::new(0.0, 1.0, 0.0),
875 Vector3::<f32>::new(0.0, 0.0, 1.0),
876 Vector3::<f32>::normalize(&Vector3::new(1.0, 1.0, 1.0)),
877 ];
878
879 for angle in &angles {
880 for axis in &axes {
881 let q1 = quat_axis_angle_f32(axis, *angle);
882 let mat = q1.mat3();
883 let q2 = Quat::of_matrix3(&mat);
884
885 let dot_product = Quat::dot(&q1, &q2);
887 let q2_adjusted = if dot_product < 0.0 {
888 Quat::neg(&q2)
889 } else {
890 q2
891 };
892
893 assert!((q1.x - q2_adjusted.x).abs() < 0.001);
894 assert!((q1.y - q2_adjusted.y).abs() < 0.001);
895 assert!((q1.z - q2_adjusted.z).abs() < 0.001);
896 assert!((q1.w - q2_adjusted.w).abs() < 0.001);
897 }
898 }
899 }
900
901 #[test]
902 fn test_quaternion_matrix_roundtrip() {
903 let q = quat_axis_angle_f32(&Vector3::new(0.0, 1.0, 0.0), 1.234);
904 let mat = q.mat3();
905 let q2 = Quat::of_matrix3(&mat);
906 let dot = Quat::dot(&q, &q2);
907 let q2 = if dot < 0.0 { Quat::neg(&q2) } else { q2 };
908
909 assert!((q.x - q2.x).abs() < 0.001);
910 assert!((q.y - q2.y).abs() < 0.001);
911 assert!((q.z - q2.z).abs() < 0.001);
912 assert!((q.w - q2.w).abs() < 0.001);
913 }
914
915 #[test]
916 fn test_matrix4_quaternion_roundtrip() {
917 let q = quat_axis_angle_f32(&Vector3::new(0.0, 0.0, 1.0), 0.75);
918 let mat4 = q.mat4();
919 let q2 = Quat::of_matrix4(&mat4);
920 let dot = Quat::dot(&q, &q2);
921 let q2 = if dot < 0.0 { Quat::neg(&q2) } else { q2 };
922
923 assert!((q.x - q2.x).abs() < 0.001);
924 assert!((q.y - q2.y).abs() < 0.001);
925 assert!((q.z - q2.z).abs() < 0.001);
926 assert!((q.w - q2.w).abs() < 0.001);
927 }
928
929 #[test]
930 fn test_quaternion_conjugate() {
931 let q = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
932 let conj = Quat::conjugate(&q);
933
934 assert_eq!(conj.x, -1.0);
935 assert_eq!(conj.y, -2.0);
936 assert_eq!(conj.z, -3.0);
937 assert_eq!(conj.w, 4.0);
938
939 let conj_conj = Quat::conjugate(&conj);
941 assert_eq!(conj_conj.x, q.x);
942 assert_eq!(conj_conj.y, q.y);
943 assert_eq!(conj_conj.z, q.z);
944 assert_eq!(conj_conj.w, q.w);
945 }
946
947 #[test]
948 fn test_quaternion_dot_product() {
949 let q1 = Quat::<f32>::new(1.0, 2.0, 3.0, 4.0);
950 let q2 = Quat::<f32>::new(5.0, 6.0, 7.0, 8.0);
951
952 let dot = Quat::dot(&q1, &q2);
953 let expected = 1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0;
954 assert_eq!(dot, expected);
955
956 let self_dot = Quat::dot(&q1, &q1);
958 let length_squared = q1.length() * q1.length();
959 assert!((self_dot - length_squared).abs() < 0.0001);
960 }
961}