1use crate::{
2 primitives::{Primitive2d, Primitive3d},
3 Quat, Rot2, Vec2, Vec3, Vec3A, Vec4,
4};
5
6use core::f32::consts::FRAC_1_SQRT_2;
7use core::fmt;
8use derive_more::derive::Into;
9
10#[cfg(feature = "bevy_reflect")]
11use bevy_reflect::Reflect;
12
13#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
14use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
15
16#[cfg(all(debug_assertions, feature = "std"))]
17use std::eprintln;
18
19use thiserror::Error;
20
21#[derive(Debug, PartialEq, Error)]
23pub enum InvalidDirectionError {
24 #[error("The length of the direction vector is zero or very close to zero")]
26 Zero,
27 #[error("The length of the direction vector is `std::f32::INFINITY`")]
29 Infinite,
30 #[error("The length of the direction vector is `NaN`")]
32 NaN,
33}
34
35impl InvalidDirectionError {
36 pub fn from_length(length: f32) -> Self {
38 if length.is_nan() {
39 InvalidDirectionError::NaN
40 } else if !length.is_finite() {
41 InvalidDirectionError::Infinite
43 } else {
44 InvalidDirectionError::Zero
46 }
47 }
48}
49
50#[cfg(debug_assertions)]
58fn assert_is_normalized(message: &str, length_squared: f32) {
59 use crate::ops;
60
61 let length_error_squared = ops::abs(length_squared - 1.0);
62
63 if length_error_squared > 2e-2 || length_error_squared.is_nan() {
65 panic!(
67 "Error: {message} The length is {}.",
68 ops::sqrt(length_squared)
69 );
70 } else if length_error_squared > 2e-4 {
71 #[cfg(feature = "std")]
73 #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
74 {
75 eprintln!(
76 "Warning: {message} The length is {}.",
77 ops::sqrt(length_squared)
78 );
79 }
80 }
81}
82
83#[derive(Clone, Copy, Debug, PartialEq)]
85#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
86#[cfg_attr(
87 feature = "bevy_reflect",
88 derive(Reflect),
89 reflect(Debug, PartialEq, Clone)
90)]
91#[cfg_attr(
92 all(feature = "serialize", feature = "bevy_reflect"),
93 reflect(Serialize, Deserialize)
94)]
95#[doc(alias = "Direction2d")]
96pub struct Dir2(Vec2);
97impl Primitive2d for Dir2 {}
98
99impl Dir2 {
100 pub const X: Self = Self(Vec2::X);
102 pub const Y: Self = Self(Vec2::Y);
104 pub const NEG_X: Self = Self(Vec2::NEG_X);
106 pub const NEG_Y: Self = Self(Vec2::NEG_Y);
108 pub const AXES: [Self; 2] = [Self::X, Self::Y];
110
111 pub const NORTH: Self = Self(Vec2::Y);
113 pub const SOUTH: Self = Self(Vec2::NEG_Y);
115 pub const EAST: Self = Self(Vec2::X);
117 pub const WEST: Self = Self(Vec2::NEG_X);
119 pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
121 pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
123 pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
125 pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
127
128 pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
133 Self::new_and_length(value).map(|(dir, _)| dir)
134 }
135
136 pub fn new_unchecked(value: Vec2) -> Self {
142 #[cfg(debug_assertions)]
143 assert_is_normalized(
144 "The vector given to `Dir2::new_unchecked` is not normalized.",
145 value.length_squared(),
146 );
147
148 Self(value)
149 }
150
151 pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
157 let length = value.length();
158 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
159
160 direction
161 .map(|dir| (Self(dir), length))
162 .ok_or(InvalidDirectionError::from_length(length))
163 }
164
165 pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
170 Self::new(Vec2::new(x, y))
171 }
172
173 pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
179 Self::new_unchecked(Vec2::new(x, y))
180 }
181
182 pub const fn as_vec2(&self) -> Vec2 {
184 self.0
185 }
186
187 #[inline]
213 pub fn slerp(self, rhs: Self, s: f32) -> Self {
214 let angle = self.angle_to(rhs.0);
215 Rot2::radians(angle * s) * self
216 }
217
218 #[inline]
220 pub fn rotation_to(self, other: Self) -> Rot2 {
221 other.rotation_from_x() * self.rotation_to_x()
223 }
224
225 #[inline]
227 pub fn rotation_from(self, other: Self) -> Rot2 {
228 other.rotation_to(self)
229 }
230
231 #[inline]
233 pub fn rotation_from_x(self) -> Rot2 {
234 Rot2::from_sin_cos(self.0.y, self.0.x)
235 }
236
237 #[inline]
239 pub fn rotation_to_x(self) -> Rot2 {
240 self.rotation_from_x().inverse()
242 }
243
244 #[inline]
246 pub fn rotation_from_y(self) -> Rot2 {
247 Rot2::from_sin_cos(-self.0.x, self.0.y)
251 }
252
253 #[inline]
255 pub fn rotation_to_y(self) -> Rot2 {
256 self.rotation_from_y().inverse()
257 }
258
259 #[inline]
263 pub fn fast_renormalize(self) -> Self {
264 let length_squared = self.0.length_squared();
265 Self(self * (0.5 * (3.0 - length_squared)))
267 }
268}
269
270impl TryFrom<Vec2> for Dir2 {
271 type Error = InvalidDirectionError;
272
273 fn try_from(value: Vec2) -> Result<Self, Self::Error> {
274 Self::new(value)
275 }
276}
277
278impl From<Dir2> for Vec2 {
279 fn from(value: Dir2) -> Self {
280 value.as_vec2()
281 }
282}
283
284impl core::ops::Deref for Dir2 {
285 type Target = Vec2;
286 fn deref(&self) -> &Self::Target {
287 &self.0
288 }
289}
290
291impl core::ops::Neg for Dir2 {
292 type Output = Self;
293 fn neg(self) -> Self::Output {
294 Self(-self.0)
295 }
296}
297
298impl core::ops::Mul<f32> for Dir2 {
299 type Output = Vec2;
300 fn mul(self, rhs: f32) -> Self::Output {
301 self.0 * rhs
302 }
303}
304
305impl core::ops::Mul<Dir2> for f32 {
306 type Output = Vec2;
307 fn mul(self, rhs: Dir2) -> Self::Output {
308 self * rhs.0
309 }
310}
311
312impl core::ops::Mul<Dir2> for Rot2 {
313 type Output = Dir2;
314
315 fn mul(self, direction: Dir2) -> Self::Output {
317 let rotated = self * *direction;
318
319 #[cfg(debug_assertions)]
320 assert_is_normalized(
321 "`Dir2` is denormalized after rotation.",
322 rotated.length_squared(),
323 );
324
325 Dir2(rotated)
326 }
327}
328
329impl fmt::Display for Dir2 {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 write!(f, "{}", self.0)
332 }
333}
334
335#[cfg(any(feature = "approx", test))]
336impl approx::AbsDiffEq for Dir2 {
337 type Epsilon = f32;
338 fn default_epsilon() -> f32 {
339 f32::EPSILON
340 }
341 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
342 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
343 }
344}
345
346#[cfg(any(feature = "approx", test))]
347impl approx::RelativeEq for Dir2 {
348 fn default_max_relative() -> f32 {
349 f32::EPSILON
350 }
351 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
352 self.as_ref()
353 .relative_eq(other.as_ref(), epsilon, max_relative)
354 }
355}
356
357#[cfg(any(feature = "approx", test))]
358impl approx::UlpsEq for Dir2 {
359 fn default_max_ulps() -> u32 {
360 4
361 }
362 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
363 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
364 }
365}
366
367#[derive(Clone, Copy, Debug, PartialEq, Into)]
369#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
370#[cfg_attr(
371 feature = "bevy_reflect",
372 derive(Reflect),
373 reflect(Debug, PartialEq, Clone)
374)]
375#[cfg_attr(
376 all(feature = "serialize", feature = "bevy_reflect"),
377 reflect(Serialize, Deserialize)
378)]
379#[doc(alias = "Direction3d")]
380pub struct Dir3(Vec3);
381impl Primitive3d for Dir3 {}
382
383impl Dir3 {
384 pub const X: Self = Self(Vec3::X);
386 pub const Y: Self = Self(Vec3::Y);
388 pub const Z: Self = Self(Vec3::Z);
390 pub const NEG_X: Self = Self(Vec3::NEG_X);
392 pub const NEG_Y: Self = Self(Vec3::NEG_Y);
394 pub const NEG_Z: Self = Self(Vec3::NEG_Z);
396 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
398
399 pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
404 Self::new_and_length(value).map(|(dir, _)| dir)
405 }
406
407 pub fn new_unchecked(value: Vec3) -> Self {
413 #[cfg(debug_assertions)]
414 assert_is_normalized(
415 "The vector given to `Dir3::new_unchecked` is not normalized.",
416 value.length_squared(),
417 );
418
419 Self(value)
420 }
421
422 pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
428 let length = value.length();
429 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
430
431 direction
432 .map(|dir| (Self(dir), length))
433 .ok_or(InvalidDirectionError::from_length(length))
434 }
435
436 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
441 Self::new(Vec3::new(x, y, z))
442 }
443
444 pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
450 Self::new_unchecked(Vec3::new(x, y, z))
451 }
452
453 pub const fn as_vec3(&self) -> Vec3 {
455 self.0
456 }
457
458 #[inline]
488 pub fn slerp(self, rhs: Self, s: f32) -> Self {
489 let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
490 Dir3(quat.mul_vec3(self.0))
491 }
492
493 #[inline]
520 pub fn fast_renormalize(self) -> Self {
521 let length_squared = self.0.length_squared();
540 Self(self * (0.5 * (3.0 - length_squared)))
541 }
542}
543
544impl TryFrom<Vec3> for Dir3 {
545 type Error = InvalidDirectionError;
546
547 fn try_from(value: Vec3) -> Result<Self, Self::Error> {
548 Self::new(value)
549 }
550}
551
552impl core::ops::Deref for Dir3 {
553 type Target = Vec3;
554 fn deref(&self) -> &Self::Target {
555 &self.0
556 }
557}
558
559impl core::ops::Neg for Dir3 {
560 type Output = Self;
561 fn neg(self) -> Self::Output {
562 Self(-self.0)
563 }
564}
565
566impl core::ops::Mul<f32> for Dir3 {
567 type Output = Vec3;
568 fn mul(self, rhs: f32) -> Self::Output {
569 self.0 * rhs
570 }
571}
572
573impl core::ops::Mul<Dir3> for f32 {
574 type Output = Vec3;
575 fn mul(self, rhs: Dir3) -> Self::Output {
576 self * rhs.0
577 }
578}
579
580impl core::ops::Mul<Dir3> for Quat {
581 type Output = Dir3;
582
583 fn mul(self, direction: Dir3) -> Self::Output {
585 let rotated = self * *direction;
586
587 #[cfg(debug_assertions)]
588 assert_is_normalized(
589 "`Dir3` is denormalized after rotation.",
590 rotated.length_squared(),
591 );
592
593 Dir3(rotated)
594 }
595}
596
597impl fmt::Display for Dir3 {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 write!(f, "{}", self.0)
600 }
601}
602
603#[cfg(feature = "approx")]
604impl approx::AbsDiffEq for Dir3 {
605 type Epsilon = f32;
606 fn default_epsilon() -> f32 {
607 f32::EPSILON
608 }
609 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
610 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
611 }
612}
613
614#[cfg(feature = "approx")]
615impl approx::RelativeEq for Dir3 {
616 fn default_max_relative() -> f32 {
617 f32::EPSILON
618 }
619 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
620 self.as_ref()
621 .relative_eq(other.as_ref(), epsilon, max_relative)
622 }
623}
624
625#[cfg(feature = "approx")]
626impl approx::UlpsEq for Dir3 {
627 fn default_max_ulps() -> u32 {
628 4
629 }
630 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
631 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
632 }
633}
634
635#[derive(Clone, Copy, Debug, PartialEq)]
640#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
641#[cfg_attr(
642 feature = "bevy_reflect",
643 derive(Reflect),
644 reflect(Debug, PartialEq, Clone)
645)]
646#[cfg_attr(
647 all(feature = "serialize", feature = "bevy_reflect"),
648 reflect(Serialize, Deserialize)
649)]
650#[doc(alias = "Direction3dA")]
651pub struct Dir3A(Vec3A);
652impl Primitive3d for Dir3A {}
653
654impl Dir3A {
655 pub const X: Self = Self(Vec3A::X);
657 pub const Y: Self = Self(Vec3A::Y);
659 pub const Z: Self = Self(Vec3A::Z);
661 pub const NEG_X: Self = Self(Vec3A::NEG_X);
663 pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
665 pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
667 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
669
670 pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
675 Self::new_and_length(value).map(|(dir, _)| dir)
676 }
677
678 pub fn new_unchecked(value: Vec3A) -> Self {
684 #[cfg(debug_assertions)]
685 assert_is_normalized(
686 "The vector given to `Dir3A::new_unchecked` is not normalized.",
687 value.length_squared(),
688 );
689
690 Self(value)
691 }
692
693 pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> {
699 let length = value.length();
700 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
701
702 direction
703 .map(|dir| (Self(dir), length))
704 .ok_or(InvalidDirectionError::from_length(length))
705 }
706
707 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
712 Self::new(Vec3A::new(x, y, z))
713 }
714
715 pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
721 Self::new_unchecked(Vec3A::new(x, y, z))
722 }
723
724 pub const fn as_vec3a(&self) -> Vec3A {
726 self.0
727 }
728
729 #[inline]
759 pub fn slerp(self, rhs: Self, s: f32) -> Self {
760 let quat = Quat::IDENTITY.slerp(
761 Quat::from_rotation_arc(Vec3::from(self.0), Vec3::from(rhs.0)),
762 s,
763 );
764 Dir3A(quat.mul_vec3a(self.0))
765 }
766
767 #[inline]
772 pub fn fast_renormalize(self) -> Self {
773 let length_squared = self.0.length_squared();
774 Self(self * (0.5 * (3.0 - length_squared)))
776 }
777}
778
779impl From<Dir3> for Dir3A {
780 fn from(value: Dir3) -> Self {
781 Self(value.0.into())
782 }
783}
784
785impl From<Dir3A> for Dir3 {
786 fn from(value: Dir3A) -> Self {
787 Self(value.0.into())
788 }
789}
790
791impl TryFrom<Vec3A> for Dir3A {
792 type Error = InvalidDirectionError;
793
794 fn try_from(value: Vec3A) -> Result<Self, Self::Error> {
795 Self::new(value)
796 }
797}
798
799impl From<Dir3A> for Vec3A {
800 fn from(value: Dir3A) -> Self {
801 value.0
802 }
803}
804
805impl core::ops::Deref for Dir3A {
806 type Target = Vec3A;
807 fn deref(&self) -> &Self::Target {
808 &self.0
809 }
810}
811
812impl core::ops::Neg for Dir3A {
813 type Output = Self;
814 fn neg(self) -> Self::Output {
815 Self(-self.0)
816 }
817}
818
819impl core::ops::Mul<f32> for Dir3A {
820 type Output = Vec3A;
821 fn mul(self, rhs: f32) -> Self::Output {
822 self.0 * rhs
823 }
824}
825
826impl core::ops::Mul<Dir3A> for f32 {
827 type Output = Vec3A;
828 fn mul(self, rhs: Dir3A) -> Self::Output {
829 self * rhs.0
830 }
831}
832
833impl core::ops::Mul<Dir3A> for Quat {
834 type Output = Dir3A;
835
836 fn mul(self, direction: Dir3A) -> Self::Output {
838 let rotated = self * *direction;
839
840 #[cfg(debug_assertions)]
841 assert_is_normalized(
842 "`Dir3A` is denormalized after rotation.",
843 rotated.length_squared(),
844 );
845
846 Dir3A(rotated)
847 }
848}
849
850impl fmt::Display for Dir3A {
851 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852 write!(f, "{}", self.0)
853 }
854}
855
856#[cfg(feature = "approx")]
857impl approx::AbsDiffEq for Dir3A {
858 type Epsilon = f32;
859 fn default_epsilon() -> f32 {
860 f32::EPSILON
861 }
862 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
863 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
864 }
865}
866
867#[cfg(feature = "approx")]
868impl approx::RelativeEq for Dir3A {
869 fn default_max_relative() -> f32 {
870 f32::EPSILON
871 }
872 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
873 self.as_ref()
874 .relative_eq(other.as_ref(), epsilon, max_relative)
875 }
876}
877
878#[cfg(feature = "approx")]
879impl approx::UlpsEq for Dir3A {
880 fn default_max_ulps() -> u32 {
881 4
882 }
883 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
884 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
885 }
886}
887
888#[derive(Clone, Copy, Debug, PartialEq, Into)]
890#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
891#[cfg_attr(
892 feature = "bevy_reflect",
893 derive(Reflect),
894 reflect(Debug, PartialEq, Clone)
895)]
896#[cfg_attr(
897 all(feature = "serialize", feature = "bevy_reflect"),
898 reflect(Serialize, Deserialize)
899)]
900#[doc(alias = "Direction4d")]
901pub struct Dir4(Vec4);
902
903impl Dir4 {
904 pub const X: Self = Self(Vec4::X);
906 pub const Y: Self = Self(Vec4::Y);
908 pub const Z: Self = Self(Vec4::Z);
910 pub const W: Self = Self(Vec4::W);
912 pub const NEG_X: Self = Self(Vec4::NEG_X);
914 pub const NEG_Y: Self = Self(Vec4::NEG_Y);
916 pub const NEG_Z: Self = Self(Vec4::NEG_Z);
918 pub const NEG_W: Self = Self(Vec4::NEG_W);
920 pub const AXES: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
922
923 pub fn new(value: Vec4) -> Result<Self, InvalidDirectionError> {
928 Self::new_and_length(value).map(|(dir, _)| dir)
929 }
930
931 pub fn new_unchecked(value: Vec4) -> Self {
937 #[cfg(debug_assertions)]
938 assert_is_normalized(
939 "The vector given to `Dir4::new_unchecked` is not normalized.",
940 value.length_squared(),
941 );
942 Self(value)
943 }
944
945 pub fn new_and_length(value: Vec4) -> Result<(Self, f32), InvalidDirectionError> {
951 let length = value.length();
952 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
953
954 direction
955 .map(|dir| (Self(dir), length))
956 .ok_or(InvalidDirectionError::from_length(length))
957 }
958
959 pub fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Result<Self, InvalidDirectionError> {
964 Self::new(Vec4::new(x, y, z, w))
965 }
966
967 pub fn from_xyzw_unchecked(x: f32, y: f32, z: f32, w: f32) -> Self {
973 Self::new_unchecked(Vec4::new(x, y, z, w))
974 }
975
976 pub const fn as_vec4(&self) -> Vec4 {
978 self.0
979 }
980
981 #[inline]
984 pub fn fast_renormalize(self) -> Self {
985 let length_squared = self.0.length_squared();
1004 Self(self * (0.5 * (3.0 - length_squared)))
1005 }
1006}
1007
1008impl TryFrom<Vec4> for Dir4 {
1009 type Error = InvalidDirectionError;
1010
1011 fn try_from(value: Vec4) -> Result<Self, Self::Error> {
1012 Self::new(value)
1013 }
1014}
1015
1016impl core::ops::Deref for Dir4 {
1017 type Target = Vec4;
1018 fn deref(&self) -> &Self::Target {
1019 &self.0
1020 }
1021}
1022
1023impl core::ops::Neg for Dir4 {
1024 type Output = Self;
1025 fn neg(self) -> Self::Output {
1026 Self(-self.0)
1027 }
1028}
1029
1030impl core::ops::Mul<f32> for Dir4 {
1031 type Output = Vec4;
1032 fn mul(self, rhs: f32) -> Self::Output {
1033 self.0 * rhs
1034 }
1035}
1036
1037impl core::ops::Mul<Dir4> for f32 {
1038 type Output = Vec4;
1039 fn mul(self, rhs: Dir4) -> Self::Output {
1040 self * rhs.0
1041 }
1042}
1043
1044impl fmt::Display for Dir4 {
1045 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1046 write!(f, "{}", self.0)
1047 }
1048}
1049
1050#[cfg(feature = "approx")]
1051impl approx::AbsDiffEq for Dir4 {
1052 type Epsilon = f32;
1053 fn default_epsilon() -> f32 {
1054 f32::EPSILON
1055 }
1056 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
1057 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
1058 }
1059}
1060
1061#[cfg(feature = "approx")]
1062impl approx::RelativeEq for Dir4 {
1063 fn default_max_relative() -> f32 {
1064 f32::EPSILON
1065 }
1066 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
1067 self.as_ref()
1068 .relative_eq(other.as_ref(), epsilon, max_relative)
1069 }
1070}
1071
1072#[cfg(feature = "approx")]
1073impl approx::UlpsEq for Dir4 {
1074 fn default_max_ulps() -> u32 {
1075 4
1076 }
1077
1078 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
1079 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
1080 }
1081}
1082
1083#[cfg(test)]
1084#[cfg(feature = "approx")]
1085mod tests {
1086 use crate::ops;
1087
1088 use super::*;
1089 use approx::assert_relative_eq;
1090
1091 #[test]
1092 fn dir2_creation() {
1093 assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X));
1094 assert_eq!(
1095 Dir2::new(Vec2::new(0.0, 0.0)),
1096 Err(InvalidDirectionError::Zero)
1097 );
1098 assert_eq!(
1099 Dir2::new(Vec2::new(f32::INFINITY, 0.0)),
1100 Err(InvalidDirectionError::Infinite)
1101 );
1102 assert_eq!(
1103 Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)),
1104 Err(InvalidDirectionError::Infinite)
1105 );
1106 assert_eq!(
1107 Dir2::new(Vec2::new(f32::NAN, 0.0)),
1108 Err(InvalidDirectionError::NaN)
1109 );
1110 assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5)));
1111 }
1112
1113 #[test]
1114 fn dir2_slerp() {
1115 assert_relative_eq!(
1116 Dir2::X.slerp(Dir2::Y, 0.5),
1117 Dir2::from_xy(ops::sqrt(0.5_f32), ops::sqrt(0.5_f32)).unwrap()
1118 );
1119 assert_eq!(Dir2::Y.slerp(Dir2::X, 0.0), Dir2::Y);
1120 assert_relative_eq!(Dir2::X.slerp(Dir2::Y, 1.0), Dir2::Y);
1121 assert_relative_eq!(
1122 Dir2::Y.slerp(Dir2::X, 1.0 / 3.0),
1123 Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1124 );
1125 assert_relative_eq!(
1126 Dir2::X.slerp(Dir2::Y, 2.0 / 3.0),
1127 Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1128 );
1129 }
1130
1131 #[test]
1132 fn dir2_to_rotation2d() {
1133 assert_relative_eq!(Dir2::EAST.rotation_to(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1134 assert_relative_eq!(Dir2::NORTH.rotation_from(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1135 assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rot2::FRAC_PI_2);
1136 assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rot2::PI);
1137 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_x(), Rot2::degrees(135.0));
1138 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rot2::FRAC_PI_4);
1139 }
1140
1141 #[test]
1142 fn dir2_renorm() {
1143 let (sin, cos) = ops::sin_cos(1.0_f32);
1145 let rot2 = Rot2::from_sin_cos(sin * (1.0 + 1e-5), cos * (1.0 + 1e-5));
1146 let mut dir_a = Dir2::X;
1147 let mut dir_b = Dir2::X;
1148
1149 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1151
1152 for _ in 0..50 {
1153 dir_a = rot2 * dir_a;
1154 dir_b = rot2 * dir_b;
1155 dir_b = dir_b.fast_renormalize();
1156 }
1157
1158 assert!(
1160 !dir_a.is_normalized(),
1161 "Denormalization doesn't work, test is faulty"
1162 );
1163 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1164 }
1165
1166 #[test]
1167 fn dir3_creation() {
1168 assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
1169 assert_eq!(
1170 Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
1171 Err(InvalidDirectionError::Zero)
1172 );
1173 assert_eq!(
1174 Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
1175 Err(InvalidDirectionError::Infinite)
1176 );
1177 assert_eq!(
1178 Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
1179 Err(InvalidDirectionError::Infinite)
1180 );
1181 assert_eq!(
1182 Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
1183 Err(InvalidDirectionError::NaN)
1184 );
1185 assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
1186
1187 assert!(
1189 (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3::X)
1190 .abs_diff_eq(Vec3::Y, 10e-6)
1191 );
1192 }
1193
1194 #[test]
1195 fn dir3_slerp() {
1196 assert_relative_eq!(
1197 Dir3::X.slerp(Dir3::Y, 0.5),
1198 Dir3::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1199 );
1200 assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
1201 assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
1202 assert_relative_eq!(
1203 Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
1204 Dir3::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1205 epsilon = 0.000001
1206 );
1207 assert_relative_eq!(
1208 Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
1209 Dir3::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1210 );
1211 }
1212
1213 #[test]
1214 fn dir3_renorm() {
1215 let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1217 let mut dir_a = Dir3::X;
1218 let mut dir_b = Dir3::X;
1219
1220 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1222
1223 for _ in 0..50 {
1224 dir_a = rot3 * dir_a;
1225 dir_b = rot3 * dir_b;
1226 dir_b = dir_b.fast_renormalize();
1227 }
1228
1229 assert!(
1231 !dir_a.is_normalized(),
1232 "Denormalization doesn't work, test is faulty"
1233 );
1234 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1235 }
1236
1237 #[test]
1238 fn dir3a_creation() {
1239 assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
1240 assert_eq!(
1241 Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
1242 Err(InvalidDirectionError::Zero)
1243 );
1244 assert_eq!(
1245 Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
1246 Err(InvalidDirectionError::Infinite)
1247 );
1248 assert_eq!(
1249 Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
1250 Err(InvalidDirectionError::Infinite)
1251 );
1252 assert_eq!(
1253 Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
1254 Err(InvalidDirectionError::NaN)
1255 );
1256 assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
1257
1258 assert!(
1260 (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3A::X)
1261 .abs_diff_eq(Vec3A::Y, 10e-6)
1262 );
1263 }
1264
1265 #[test]
1266 fn dir3a_slerp() {
1267 assert_relative_eq!(
1268 Dir3A::X.slerp(Dir3A::Y, 0.5),
1269 Dir3A::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1270 );
1271 assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
1272 assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
1273 assert_relative_eq!(
1274 Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
1275 Dir3A::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1276 epsilon = 0.000001
1277 );
1278 assert_relative_eq!(
1279 Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
1280 Dir3A::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1281 );
1282 }
1283
1284 #[test]
1285 fn dir3a_renorm() {
1286 let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1288 let mut dir_a = Dir3A::X;
1289 let mut dir_b = Dir3A::X;
1290
1291 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1293
1294 for _ in 0..50 {
1295 dir_a = rot3 * dir_a;
1296 dir_b = rot3 * dir_b;
1297 dir_b = dir_b.fast_renormalize();
1298 }
1299
1300 assert!(
1302 !dir_a.is_normalized(),
1303 "Denormalization doesn't work, test is faulty"
1304 );
1305 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1306 }
1307
1308 #[test]
1309 fn dir4_creation() {
1310 assert_eq!(Dir4::new(Vec4::X * 12.5), Ok(Dir4::X));
1311 assert_eq!(
1312 Dir4::new(Vec4::new(0.0, 0.0, 0.0, 0.0)),
1313 Err(InvalidDirectionError::Zero)
1314 );
1315 assert_eq!(
1316 Dir4::new(Vec4::new(f32::INFINITY, 0.0, 0.0, 0.0)),
1317 Err(InvalidDirectionError::Infinite)
1318 );
1319 assert_eq!(
1320 Dir4::new(Vec4::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)),
1321 Err(InvalidDirectionError::Infinite)
1322 );
1323 assert_eq!(
1324 Dir4::new(Vec4::new(f32::NAN, 0.0, 0.0, 0.0)),
1325 Err(InvalidDirectionError::NaN)
1326 );
1327 assert_eq!(Dir4::new_and_length(Vec4::X * 6.5), Ok((Dir4::X, 6.5)));
1328 }
1329
1330 #[test]
1331 fn dir4_renorm() {
1332 let mat4 = bevy_math::Mat4::from_quat(Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0))
1334 * (1.0 + 1e-5);
1335 let mut dir_a = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1336 let mut dir_b = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1337 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1339 for _ in 0..50 {
1340 dir_a = Dir4(mat4 * *dir_a);
1341 dir_b = Dir4(mat4 * *dir_b);
1342 dir_b = dir_b.fast_renormalize();
1343 }
1344 assert!(
1346 !dir_a.is_normalized(),
1347 "Denormalization doesn't work, test is faulty"
1348 );
1349 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1350 }
1351}