1use crate::{
2 primitives::{Primitive2d, Primitive3d},
3 Quat, Rot2, Vec2, Vec3, Vec3A,
4};
5
6use core::f32::consts::FRAC_1_SQRT_2;
7
8#[cfg(feature = "bevy_reflect")]
9use bevy_reflect::Reflect;
10#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
11use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
12
13#[derive(Debug, PartialEq)]
15pub enum InvalidDirectionError {
16 Zero,
18 Infinite,
20 NaN,
22}
23
24impl InvalidDirectionError {
25 pub fn from_length(length: f32) -> Self {
27 if length.is_nan() {
28 InvalidDirectionError::NaN
29 } else if !length.is_finite() {
30 InvalidDirectionError::Infinite
32 } else {
33 InvalidDirectionError::Zero
35 }
36 }
37}
38
39impl std::fmt::Display for InvalidDirectionError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(
42 f,
43 "Direction can not be zero (or very close to zero), or non-finite."
44 )
45 }
46}
47
48#[cfg(debug_assertions)]
56fn assert_is_normalized(message: &str, length_squared: f32) {
57 let length_error_squared = (length_squared - 1.0).abs();
58
59 if length_error_squared > 2e-2 || length_error_squared.is_nan() {
61 panic!("Error: {message} The length is {}.", length_squared.sqrt());
63 } else if length_error_squared > 2e-4 {
64 eprintln!(
66 "Warning: {message} The length is {}.",
67 length_squared.sqrt()
68 );
69 }
70}
71
72#[deprecated(
74 since = "0.14.0",
75 note = "`Direction2d` has been renamed. Please use `Dir2` instead."
76)]
77pub type Direction2d = Dir2;
78
79#[deprecated(
81 since = "0.14.0",
82 note = "`Direction3d` has been renamed. Please use `Dir3` instead."
83)]
84pub type Direction3d = Dir3;
85
86#[derive(Clone, Copy, Debug, PartialEq)]
88#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
89#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
90#[cfg_attr(
91 all(feature = "serialize", feature = "bevy_reflect"),
92 reflect(Serialize, Deserialize)
93)]
94#[doc(alias = "Direction2d")]
95pub struct Dir2(Vec2);
96impl Primitive2d for Dir2 {}
97
98impl Dir2 {
99 pub const X: Self = Self(Vec2::X);
101 pub const Y: Self = Self(Vec2::Y);
103 pub const NEG_X: Self = Self(Vec2::NEG_X);
105 pub const NEG_Y: Self = Self(Vec2::NEG_Y);
107 pub const AXES: [Self; 2] = [Self::X, Self::Y];
109
110 pub const NORTH: Self = Self(Vec2::Y);
112 pub const SOUTH: Self = Self(Vec2::NEG_Y);
114 pub const EAST: Self = Self(Vec2::X);
116 pub const WEST: Self = Self(Vec2::NEG_X);
118 pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
120 pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
122 pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
124 pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
126
127 pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
132 Self::new_and_length(value).map(|(dir, _)| dir)
133 }
134
135 pub fn new_unchecked(value: Vec2) -> Self {
141 #[cfg(debug_assertions)]
142 assert_is_normalized(
143 "The vector given to `Dir2::new_unchecked` is not normalized.",
144 value.length_squared(),
145 );
146
147 Self(value)
148 }
149
150 pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
156 let length = value.length();
157 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
158
159 direction
160 .map(|dir| (Self(dir), length))
161 .ok_or(InvalidDirectionError::from_length(length))
162 }
163
164 pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
169 Self::new(Vec2::new(x, y))
170 }
171
172 pub const fn as_vec2(&self) -> Vec2 {
174 self.0
175 }
176
177 #[inline]
201 pub fn slerp(self, rhs: Self, s: f32) -> Self {
202 let angle = self.angle_between(rhs.0);
203 Rot2::radians(angle * s) * self
204 }
205
206 #[inline]
208 pub fn rotation_to(self, other: Self) -> Rot2 {
209 other.rotation_from_x() * self.rotation_to_x()
211 }
212
213 #[inline]
215 pub fn rotation_from(self, other: Self) -> Rot2 {
216 other.rotation_to(self)
217 }
218
219 #[inline]
221 pub fn rotation_from_x(self) -> Rot2 {
222 Rot2::from_sin_cos(self.0.y, self.0.x)
223 }
224
225 #[inline]
227 pub fn rotation_to_x(self) -> Rot2 {
228 self.rotation_from_x().inverse()
230 }
231
232 #[inline]
234 pub fn rotation_from_y(self) -> Rot2 {
235 Rot2::from_sin_cos(-self.0.x, self.0.y)
239 }
240
241 #[inline]
243 pub fn rotation_to_y(self) -> Rot2 {
244 self.rotation_from_y().inverse()
245 }
246}
247
248impl TryFrom<Vec2> for Dir2 {
249 type Error = InvalidDirectionError;
250
251 fn try_from(value: Vec2) -> Result<Self, Self::Error> {
252 Self::new(value)
253 }
254}
255
256impl From<Dir2> for Vec2 {
257 fn from(value: Dir2) -> Self {
258 value.as_vec2()
259 }
260}
261
262impl std::ops::Deref for Dir2 {
263 type Target = Vec2;
264 fn deref(&self) -> &Self::Target {
265 &self.0
266 }
267}
268
269impl std::ops::Neg for Dir2 {
270 type Output = Self;
271 fn neg(self) -> Self::Output {
272 Self(-self.0)
273 }
274}
275
276impl std::ops::Mul<f32> for Dir2 {
277 type Output = Vec2;
278 fn mul(self, rhs: f32) -> Self::Output {
279 self.0 * rhs
280 }
281}
282
283impl std::ops::Mul<Dir2> for f32 {
284 type Output = Vec2;
285 fn mul(self, rhs: Dir2) -> Self::Output {
286 self * rhs.0
287 }
288}
289
290impl std::ops::Mul<Dir2> for Rot2 {
291 type Output = Dir2;
292
293 fn mul(self, direction: Dir2) -> Self::Output {
295 let rotated = self * *direction;
296
297 #[cfg(debug_assertions)]
298 assert_is_normalized(
299 "`Dir2` is denormalized after rotation.",
300 rotated.length_squared(),
301 );
302
303 Dir2(rotated)
304 }
305}
306
307#[cfg(any(feature = "approx", test))]
308impl approx::AbsDiffEq for Dir2 {
309 type Epsilon = f32;
310 fn default_epsilon() -> f32 {
311 f32::EPSILON
312 }
313 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
314 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
315 }
316}
317
318#[cfg(any(feature = "approx", test))]
319impl approx::RelativeEq for Dir2 {
320 fn default_max_relative() -> f32 {
321 f32::EPSILON
322 }
323 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
324 self.as_ref()
325 .relative_eq(other.as_ref(), epsilon, max_relative)
326 }
327}
328
329#[cfg(any(feature = "approx", test))]
330impl approx::UlpsEq for Dir2 {
331 fn default_max_ulps() -> u32 {
332 4
333 }
334 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
335 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
336 }
337}
338
339#[derive(Clone, Copy, Debug, PartialEq)]
341#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
342#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
343#[cfg_attr(
344 all(feature = "serialize", feature = "bevy_reflect"),
345 reflect(Serialize, Deserialize)
346)]
347#[doc(alias = "Direction3d")]
348pub struct Dir3(Vec3);
349impl Primitive3d for Dir3 {}
350
351impl Dir3 {
352 pub const X: Self = Self(Vec3::X);
354 pub const Y: Self = Self(Vec3::Y);
356 pub const Z: Self = Self(Vec3::Z);
358 pub const NEG_X: Self = Self(Vec3::NEG_X);
360 pub const NEG_Y: Self = Self(Vec3::NEG_Y);
362 pub const NEG_Z: Self = Self(Vec3::NEG_Z);
364 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
366
367 pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
372 Self::new_and_length(value).map(|(dir, _)| dir)
373 }
374
375 pub fn new_unchecked(value: Vec3) -> Self {
381 #[cfg(debug_assertions)]
382 assert_is_normalized(
383 "The vector given to `Dir3::new_unchecked` is not normalized.",
384 value.length_squared(),
385 );
386
387 Self(value)
388 }
389
390 pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
396 let length = value.length();
397 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
398
399 direction
400 .map(|dir| (Self(dir), length))
401 .ok_or(InvalidDirectionError::from_length(length))
402 }
403
404 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
409 Self::new(Vec3::new(x, y, z))
410 }
411
412 pub const fn as_vec3(&self) -> Vec3 {
414 self.0
415 }
416
417 #[inline]
445 pub fn slerp(self, rhs: Self, s: f32) -> Self {
446 let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
447 Dir3(quat.mul_vec3(self.0))
448 }
449}
450
451impl TryFrom<Vec3> for Dir3 {
452 type Error = InvalidDirectionError;
453
454 fn try_from(value: Vec3) -> Result<Self, Self::Error> {
455 Self::new(value)
456 }
457}
458
459impl From<Dir3> for Vec3 {
460 fn from(value: Dir3) -> Self {
461 value.0
462 }
463}
464
465impl std::ops::Deref for Dir3 {
466 type Target = Vec3;
467 fn deref(&self) -> &Self::Target {
468 &self.0
469 }
470}
471
472impl std::ops::Neg for Dir3 {
473 type Output = Self;
474 fn neg(self) -> Self::Output {
475 Self(-self.0)
476 }
477}
478
479impl std::ops::Mul<f32> for Dir3 {
480 type Output = Vec3;
481 fn mul(self, rhs: f32) -> Self::Output {
482 self.0 * rhs
483 }
484}
485
486impl std::ops::Mul<Dir3> for f32 {
487 type Output = Vec3;
488 fn mul(self, rhs: Dir3) -> Self::Output {
489 self * rhs.0
490 }
491}
492
493impl std::ops::Mul<Dir3> for Quat {
494 type Output = Dir3;
495
496 fn mul(self, direction: Dir3) -> Self::Output {
498 let rotated = self * *direction;
499
500 #[cfg(debug_assertions)]
501 assert_is_normalized(
502 "`Dir3` is denormalized after rotation.",
503 rotated.length_squared(),
504 );
505
506 Dir3(rotated)
507 }
508}
509
510#[cfg(feature = "approx")]
511impl approx::AbsDiffEq for Dir3 {
512 type Epsilon = f32;
513 fn default_epsilon() -> f32 {
514 f32::EPSILON
515 }
516 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
517 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
518 }
519}
520
521#[cfg(feature = "approx")]
522impl approx::RelativeEq for Dir3 {
523 fn default_max_relative() -> f32 {
524 f32::EPSILON
525 }
526 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
527 self.as_ref()
528 .relative_eq(other.as_ref(), epsilon, max_relative)
529 }
530}
531
532#[cfg(feature = "approx")]
533impl approx::UlpsEq for Dir3 {
534 fn default_max_ulps() -> u32 {
535 4
536 }
537 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
538 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
539 }
540}
541
542#[derive(Clone, Copy, Debug, PartialEq)]
547#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
548#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
549#[cfg_attr(
550 all(feature = "serialize", feature = "bevy_reflect"),
551 reflect(Serialize, Deserialize)
552)]
553#[doc(alias = "Direction3dA")]
554pub struct Dir3A(Vec3A);
555impl Primitive3d for Dir3A {}
556
557impl Dir3A {
558 pub const X: Self = Self(Vec3A::X);
560 pub const Y: Self = Self(Vec3A::Y);
562 pub const Z: Self = Self(Vec3A::Z);
564 pub const NEG_X: Self = Self(Vec3A::NEG_X);
566 pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
568 pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
570 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
572
573 pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
578 Self::new_and_length(value).map(|(dir, _)| dir)
579 }
580
581 pub fn new_unchecked(value: Vec3A) -> Self {
587 #[cfg(debug_assertions)]
588 assert_is_normalized(
589 "The vector given to `Dir3A::new_unchecked` is not normalized.",
590 value.length_squared(),
591 );
592
593 Self(value)
594 }
595
596 pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> {
602 let length = value.length();
603 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
604
605 direction
606 .map(|dir| (Self(dir), length))
607 .ok_or(InvalidDirectionError::from_length(length))
608 }
609
610 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
615 Self::new(Vec3A::new(x, y, z))
616 }
617
618 pub const fn as_vec3a(&self) -> Vec3A {
620 self.0
621 }
622
623 #[inline]
651 pub fn slerp(self, rhs: Self, s: f32) -> Self {
652 let quat = Quat::IDENTITY.slerp(
653 Quat::from_rotation_arc(Vec3::from(self.0), Vec3::from(rhs.0)),
654 s,
655 );
656 Dir3A(quat.mul_vec3a(self.0))
657 }
658}
659
660impl From<Dir3> for Dir3A {
661 fn from(value: Dir3) -> Self {
662 Self(value.0.into())
663 }
664}
665
666impl From<Dir3A> for Dir3 {
667 fn from(value: Dir3A) -> Self {
668 Self(value.0.into())
669 }
670}
671
672impl TryFrom<Vec3A> for Dir3A {
673 type Error = InvalidDirectionError;
674
675 fn try_from(value: Vec3A) -> Result<Self, Self::Error> {
676 Self::new(value)
677 }
678}
679
680impl From<Dir3A> for Vec3A {
681 fn from(value: Dir3A) -> Self {
682 value.0
683 }
684}
685
686impl std::ops::Deref for Dir3A {
687 type Target = Vec3A;
688 fn deref(&self) -> &Self::Target {
689 &self.0
690 }
691}
692
693impl std::ops::Neg for Dir3A {
694 type Output = Self;
695 fn neg(self) -> Self::Output {
696 Self(-self.0)
697 }
698}
699
700impl std::ops::Mul<f32> for Dir3A {
701 type Output = Vec3A;
702 fn mul(self, rhs: f32) -> Self::Output {
703 self.0 * rhs
704 }
705}
706
707impl std::ops::Mul<Dir3A> for f32 {
708 type Output = Vec3A;
709 fn mul(self, rhs: Dir3A) -> Self::Output {
710 self * rhs.0
711 }
712}
713
714impl std::ops::Mul<Dir3A> for Quat {
715 type Output = Dir3A;
716
717 fn mul(self, direction: Dir3A) -> Self::Output {
719 let rotated = self * *direction;
720
721 #[cfg(debug_assertions)]
722 assert_is_normalized(
723 "`Dir3A` is denormalized after rotation.",
724 rotated.length_squared(),
725 );
726
727 Dir3A(rotated)
728 }
729}
730
731#[cfg(feature = "approx")]
732impl approx::AbsDiffEq for Dir3A {
733 type Epsilon = f32;
734 fn default_epsilon() -> f32 {
735 f32::EPSILON
736 }
737 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
738 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
739 }
740}
741
742#[cfg(feature = "approx")]
743impl approx::RelativeEq for Dir3A {
744 fn default_max_relative() -> f32 {
745 f32::EPSILON
746 }
747 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
748 self.as_ref()
749 .relative_eq(other.as_ref(), epsilon, max_relative)
750 }
751}
752
753#[cfg(feature = "approx")]
754impl approx::UlpsEq for Dir3A {
755 fn default_max_ulps() -> u32 {
756 4
757 }
758 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
759 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
760 }
761}
762
763#[cfg(test)]
764mod tests {
765 use super::*;
766 use approx::assert_relative_eq;
767
768 #[test]
769 fn dir2_creation() {
770 assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X));
771 assert_eq!(
772 Dir2::new(Vec2::new(0.0, 0.0)),
773 Err(InvalidDirectionError::Zero)
774 );
775 assert_eq!(
776 Dir2::new(Vec2::new(f32::INFINITY, 0.0)),
777 Err(InvalidDirectionError::Infinite)
778 );
779 assert_eq!(
780 Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)),
781 Err(InvalidDirectionError::Infinite)
782 );
783 assert_eq!(
784 Dir2::new(Vec2::new(f32::NAN, 0.0)),
785 Err(InvalidDirectionError::NaN)
786 );
787 assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5)));
788 }
789
790 #[test]
791 fn dir2_slerp() {
792 assert_relative_eq!(
793 Dir2::X.slerp(Dir2::Y, 0.5),
794 Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap()
795 );
796 assert_eq!(Dir2::Y.slerp(Dir2::X, 0.0), Dir2::Y);
797 assert_relative_eq!(Dir2::X.slerp(Dir2::Y, 1.0), Dir2::Y);
798 assert_relative_eq!(
799 Dir2::Y.slerp(Dir2::X, 1.0 / 3.0),
800 Dir2::from_xy(0.5, 0.75_f32.sqrt()).unwrap()
801 );
802 assert_relative_eq!(
803 Dir2::X.slerp(Dir2::Y, 2.0 / 3.0),
804 Dir2::from_xy(0.5, 0.75_f32.sqrt()).unwrap()
805 );
806 }
807
808 #[test]
809 fn dir2_to_rotation2d() {
810 assert_relative_eq!(Dir2::EAST.rotation_to(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
811 assert_relative_eq!(Dir2::NORTH.rotation_from(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
812 assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rot2::FRAC_PI_2);
813 assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rot2::PI);
814 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_x(), Rot2::degrees(135.0));
815 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rot2::FRAC_PI_4);
816 }
817
818 #[test]
819 fn dir3_creation() {
820 assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
821 assert_eq!(
822 Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
823 Err(InvalidDirectionError::Zero)
824 );
825 assert_eq!(
826 Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
827 Err(InvalidDirectionError::Infinite)
828 );
829 assert_eq!(
830 Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
831 Err(InvalidDirectionError::Infinite)
832 );
833 assert_eq!(
834 Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
835 Err(InvalidDirectionError::NaN)
836 );
837 assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
838
839 assert!(
841 (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Dir3::X)
842 .abs_diff_eq(Vec3::Y, 10e-6)
843 );
844 }
845
846 #[test]
847 fn dir3_slerp() {
848 assert_relative_eq!(
849 Dir3::X.slerp(Dir3::Y, 0.5),
850 Dir3::from_xyz(0.5f32.sqrt(), 0.5f32.sqrt(), 0.0).unwrap()
851 );
852 assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
853 assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
854 assert_relative_eq!(
855 Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
856 Dir3::from_xyz(0.75f32.sqrt(), 0.0, 0.5).unwrap(),
857 epsilon = 0.000001
858 );
859 assert_relative_eq!(
860 Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
861 Dir3::from_xyz(0.0, 0.75f32.sqrt(), 0.5).unwrap()
862 );
863 }
864
865 #[test]
866 fn dir3a_creation() {
867 assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
868 assert_eq!(
869 Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
870 Err(InvalidDirectionError::Zero)
871 );
872 assert_eq!(
873 Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
874 Err(InvalidDirectionError::Infinite)
875 );
876 assert_eq!(
877 Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
878 Err(InvalidDirectionError::Infinite)
879 );
880 assert_eq!(
881 Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
882 Err(InvalidDirectionError::NaN)
883 );
884 assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
885
886 assert!(
888 (Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Dir3A::X)
889 .abs_diff_eq(Vec3A::Y, 10e-6)
890 );
891 }
892
893 #[test]
894 fn dir3a_slerp() {
895 assert_relative_eq!(
896 Dir3A::X.slerp(Dir3A::Y, 0.5),
897 Dir3A::from_xyz(0.5f32.sqrt(), 0.5f32.sqrt(), 0.0).unwrap()
898 );
899 assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
900 assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
901 assert_relative_eq!(
902 Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
903 Dir3A::from_xyz(0.75f32.sqrt(), 0.0, 0.5).unwrap(),
904 epsilon = 0.000001
905 );
906 assert_relative_eq!(
907 Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
908 Dir3A::from_xyz(0.0, 0.75f32.sqrt(), 0.5).unwrap()
909 );
910 }
911}