1use crate::{Quat, Rot2, Vec2, Vec3, Vec3A, Vec4};
2
3use core::f32::consts::FRAC_1_SQRT_2;
4use core::fmt;
5use derive_more::derive::Into;
6
7#[cfg(feature = "bevy_reflect")]
8use bevy_reflect::Reflect;
9
10#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
11use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
12
13#[cfg(all(debug_assertions, feature = "std"))]
14use std::eprintln;
15
16use thiserror::Error;
17
18#[derive(Debug, PartialEq, Error)]
20pub enum InvalidDirectionError {
21 #[error("The length of the direction vector is zero or very close to zero")]
23 Zero,
24 #[error("The length of the direction vector is `std::f32::INFINITY`")]
26 Infinite,
27 #[error("The length of the direction vector is `NaN`")]
29 NaN,
30}
31
32impl InvalidDirectionError {
33 pub const fn from_length(length: f32) -> Self {
35 if length.is_nan() {
36 InvalidDirectionError::NaN
37 } else if !length.is_finite() {
38 InvalidDirectionError::Infinite
40 } else {
41 InvalidDirectionError::Zero
43 }
44 }
45}
46
47#[cfg(debug_assertions)]
55fn assert_is_normalized(message: &str, length_squared: f32) {
56 use crate::ops;
57
58 let length_error_squared = ops::abs(length_squared - 1.0);
59
60 if length_error_squared > 2e-2 || length_error_squared.is_nan() {
62 panic!(
64 "Error: {message} The length is {}.",
65 ops::sqrt(length_squared)
66 );
67 } else if length_error_squared > 2e-4 {
68 #[cfg(feature = "std")]
70 #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
71 {
72 eprintln!(
73 "Warning: {message} The length is {}.",
74 ops::sqrt(length_squared)
75 );
76 }
77 }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
82#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
83#[cfg_attr(
84 feature = "bevy_reflect",
85 derive(Reflect),
86 reflect(Debug, PartialEq, Clone)
87)]
88#[cfg_attr(
89 all(feature = "serialize", feature = "bevy_reflect"),
90 reflect(Serialize, Deserialize)
91)]
92#[doc(alias = "Direction2d")]
93pub struct Dir2(Vec2);
94
95impl Dir2 {
96 pub const X: Self = Self(Vec2::X);
98 pub const Y: Self = Self(Vec2::Y);
100 pub const NEG_X: Self = Self(Vec2::NEG_X);
102 pub const NEG_Y: Self = Self(Vec2::NEG_Y);
104 pub const AXES: [Self; 2] = [Self::X, Self::Y];
106 pub const CARDINALS: [Self; 4] = [Self::X, Self::NEG_X, Self::Y, Self::NEG_Y];
108
109 pub const NORTH: Self = Self(Vec2::Y);
111 pub const SOUTH: Self = Self(Vec2::NEG_Y);
113 pub const EAST: Self = Self(Vec2::X);
115 pub const WEST: Self = Self(Vec2::NEG_X);
117 pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
119 pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
121 pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
123 pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
125
126 pub const DIAGONALS: [Self; 4] = [
128 Self::NORTH_EAST,
129 Self::NORTH_WEST,
130 Self::SOUTH_EAST,
131 Self::SOUTH_WEST,
132 ];
133 pub const ALL_NEIGHBORS: [Self; 8] = [
135 Self::X,
136 Self::NEG_X,
137 Self::Y,
138 Self::NEG_Y,
139 Self::NORTH_EAST,
140 Self::NORTH_WEST,
141 Self::SOUTH_EAST,
142 Self::SOUTH_WEST,
143 ];
144
145 pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
150 Self::new_and_length(value).map(|(dir, _)| dir)
151 }
152
153 pub fn new_unchecked(value: Vec2) -> Self {
159 #[cfg(debug_assertions)]
160 assert_is_normalized(
161 "The vector given to `Dir2::new_unchecked` is not normalized.",
162 value.length_squared(),
163 );
164
165 Self(value)
166 }
167
168 pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
174 let length = value.length();
175 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
176
177 direction
178 .map(|dir| (Self(dir), length))
179 .ok_or(InvalidDirectionError::from_length(length))
180 }
181
182 pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
187 Self::new(Vec2::new(x, y))
188 }
189
190 pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
196 Self::new_unchecked(Vec2::new(x, y))
197 }
198
199 #[inline]
201 pub fn from_angle(angle: f32) -> Self {
202 Self(Vec2::from_angle(angle))
203 }
204
205 pub const fn as_vec2(&self) -> Vec2 {
207 self.0
208 }
209
210 #[inline]
236 pub fn slerp(self, rhs: Self, s: f32) -> Self {
237 let angle = self.angle_to(rhs.0);
238 Rot2::radians(angle * s) * self
239 }
240
241 #[inline]
243 pub fn rotation_to(self, other: Self) -> Rot2 {
244 other.rotation_from_x() * self.rotation_to_x()
246 }
247
248 #[inline]
250 pub fn rotation_from(self, other: Self) -> Rot2 {
251 other.rotation_to(self)
252 }
253
254 #[inline]
256 pub fn rotation_from_x(self) -> Rot2 {
257 Rot2::from_sin_cos(self.0.y, self.0.x)
258 }
259
260 #[inline]
262 pub fn rotation_to_x(self) -> Rot2 {
263 self.rotation_from_x().inverse()
265 }
266
267 #[inline]
269 pub fn rotation_from_y(self) -> Rot2 {
270 Rot2::from_sin_cos(-self.0.x, self.0.y)
274 }
275
276 #[inline]
278 pub fn rotation_to_y(self) -> Rot2 {
279 self.rotation_from_y().inverse()
280 }
281
282 #[inline]
286 pub fn fast_renormalize(self) -> Self {
287 let length_squared = self.0.length_squared();
288 Self(self * (0.5 * (3.0 - length_squared)))
290 }
291
292 #[inline]
294 pub fn perpendicular(self) -> Self {
295 Self::new_unchecked(self.as_vec2().perp())
296 }
297}
298
299impl TryFrom<Vec2> for Dir2 {
300 type Error = InvalidDirectionError;
301
302 fn try_from(value: Vec2) -> Result<Self, Self::Error> {
303 Self::new(value)
304 }
305}
306
307impl From<Dir2> for Vec2 {
308 fn from(value: Dir2) -> Self {
309 value.as_vec2()
310 }
311}
312
313impl core::ops::Deref for Dir2 {
314 type Target = Vec2;
315 fn deref(&self) -> &Self::Target {
316 &self.0
317 }
318}
319
320impl core::ops::Neg for Dir2 {
321 type Output = Self;
322 fn neg(self) -> Self::Output {
323 Self(-self.0)
324 }
325}
326
327impl core::ops::Mul<f32> for Dir2 {
328 type Output = Vec2;
329 fn mul(self, rhs: f32) -> Self::Output {
330 self.0 * rhs
331 }
332}
333
334impl core::ops::Mul<Dir2> for f32 {
335 type Output = Vec2;
336 fn mul(self, rhs: Dir2) -> Self::Output {
337 self * rhs.0
338 }
339}
340
341impl core::ops::Mul<Dir2> for Rot2 {
342 type Output = Dir2;
343
344 fn mul(self, direction: Dir2) -> Self::Output {
346 let rotated = self * *direction;
347
348 #[cfg(debug_assertions)]
349 assert_is_normalized(
350 "`Dir2` is denormalized after rotation.",
351 rotated.length_squared(),
352 );
353
354 Dir2(rotated)
355 }
356}
357
358impl fmt::Display for Dir2 {
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 write!(f, "{}", self.0)
361 }
362}
363
364#[cfg(any(feature = "approx", test))]
365impl approx::AbsDiffEq for Dir2 {
366 type Epsilon = f32;
367 fn default_epsilon() -> f32 {
368 f32::EPSILON
369 }
370 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
371 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
372 }
373}
374
375#[cfg(any(feature = "approx", test))]
376impl approx::RelativeEq for Dir2 {
377 fn default_max_relative() -> f32 {
378 f32::EPSILON
379 }
380 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
381 self.as_ref()
382 .relative_eq(other.as_ref(), epsilon, max_relative)
383 }
384}
385
386#[cfg(any(feature = "approx", test))]
387impl approx::UlpsEq for Dir2 {
388 fn default_max_ulps() -> u32 {
389 4
390 }
391 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
392 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
393 }
394}
395
396#[derive(Clone, Copy, Debug, PartialEq, Into)]
398#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
399#[cfg_attr(
400 feature = "bevy_reflect",
401 derive(Reflect),
402 reflect(Debug, PartialEq, Clone)
403)]
404#[cfg_attr(
405 all(feature = "serialize", feature = "bevy_reflect"),
406 reflect(Serialize, Deserialize)
407)]
408#[doc(alias = "Direction3d")]
409pub struct Dir3(Vec3);
410
411impl Dir3 {
412 pub const X: Self = Self(Vec3::X);
414 pub const Y: Self = Self(Vec3::Y);
416 pub const Z: Self = Self(Vec3::Z);
418 pub const NEG_X: Self = Self(Vec3::NEG_X);
420 pub const NEG_Y: Self = Self(Vec3::NEG_Y);
422 pub const NEG_Z: Self = Self(Vec3::NEG_Z);
424 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
426 pub const CARDINALS: [Self; 6] = [
428 Self::X,
429 Self::NEG_X,
430 Self::Y,
431 Self::NEG_Y,
432 Self::Z,
433 Self::NEG_Z,
434 ];
435
436 const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32;
440 pub const ALL_VERTICES: [Self; 8] = [
442 Self(Vec3::new(
443 Self::FRAC_1_SQRT_3,
444 Self::FRAC_1_SQRT_3,
445 Self::FRAC_1_SQRT_3,
446 )),
447 Self(Vec3::new(
448 -Self::FRAC_1_SQRT_3,
449 Self::FRAC_1_SQRT_3,
450 Self::FRAC_1_SQRT_3,
451 )),
452 Self(Vec3::new(
453 Self::FRAC_1_SQRT_3,
454 -Self::FRAC_1_SQRT_3,
455 Self::FRAC_1_SQRT_3,
456 )),
457 Self(Vec3::new(
458 -Self::FRAC_1_SQRT_3,
459 -Self::FRAC_1_SQRT_3,
460 Self::FRAC_1_SQRT_3,
461 )),
462 Self(Vec3::new(
463 Self::FRAC_1_SQRT_3,
464 Self::FRAC_1_SQRT_3,
465 -Self::FRAC_1_SQRT_3,
466 )),
467 Self(Vec3::new(
468 -Self::FRAC_1_SQRT_3,
469 Self::FRAC_1_SQRT_3,
470 -Self::FRAC_1_SQRT_3,
471 )),
472 Self(Vec3::new(
473 Self::FRAC_1_SQRT_3,
474 -Self::FRAC_1_SQRT_3,
475 -Self::FRAC_1_SQRT_3,
476 )),
477 Self(Vec3::new(
478 -Self::FRAC_1_SQRT_3,
479 -Self::FRAC_1_SQRT_3,
480 -Self::FRAC_1_SQRT_3,
481 )),
482 ];
483 pub const ALL_EDGES: [Self; 12] = [
485 Self(Vec3::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
486 Self(Vec3::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
487 Self(Vec3::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.)),
488 Self(Vec3::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.)),
489 Self(Vec3::new(FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2)),
490 Self(Vec3::new(-FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2)),
491 Self(Vec3::new(FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2)),
492 Self(Vec3::new(-FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2)),
493 Self(Vec3::new(0., FRAC_1_SQRT_2, FRAC_1_SQRT_2)),
494 Self(Vec3::new(0., -FRAC_1_SQRT_2, FRAC_1_SQRT_2)),
495 Self(Vec3::new(0., FRAC_1_SQRT_2, -FRAC_1_SQRT_2)),
496 Self(Vec3::new(0., -FRAC_1_SQRT_2, -FRAC_1_SQRT_2)),
497 ];
498 pub const ALL_NEIGHBORS: [Self; 26] = [
500 Self::X,
501 Self::NEG_X,
502 Self::Y,
503 Self::NEG_Y,
504 Self::Z,
505 Self::NEG_Z,
506 Self(Vec3::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
507 Self(Vec3::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
508 Self(Vec3::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.)),
509 Self(Vec3::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.)),
510 Self(Vec3::new(FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2)),
511 Self(Vec3::new(-FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2)),
512 Self(Vec3::new(FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2)),
513 Self(Vec3::new(-FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2)),
514 Self(Vec3::new(0., FRAC_1_SQRT_2, FRAC_1_SQRT_2)),
515 Self(Vec3::new(0., -FRAC_1_SQRT_2, FRAC_1_SQRT_2)),
516 Self(Vec3::new(0., FRAC_1_SQRT_2, -FRAC_1_SQRT_2)),
517 Self(Vec3::new(0., -FRAC_1_SQRT_2, -FRAC_1_SQRT_2)),
518 Self(Vec3::new(
519 Self::FRAC_1_SQRT_3,
520 Self::FRAC_1_SQRT_3,
521 Self::FRAC_1_SQRT_3,
522 )),
523 Self(Vec3::new(
524 -Self::FRAC_1_SQRT_3,
525 Self::FRAC_1_SQRT_3,
526 Self::FRAC_1_SQRT_3,
527 )),
528 Self(Vec3::new(
529 Self::FRAC_1_SQRT_3,
530 -Self::FRAC_1_SQRT_3,
531 Self::FRAC_1_SQRT_3,
532 )),
533 Self(Vec3::new(
534 -Self::FRAC_1_SQRT_3,
535 -Self::FRAC_1_SQRT_3,
536 Self::FRAC_1_SQRT_3,
537 )),
538 Self(Vec3::new(
539 Self::FRAC_1_SQRT_3,
540 Self::FRAC_1_SQRT_3,
541 -Self::FRAC_1_SQRT_3,
542 )),
543 Self(Vec3::new(
544 -Self::FRAC_1_SQRT_3,
545 Self::FRAC_1_SQRT_3,
546 -Self::FRAC_1_SQRT_3,
547 )),
548 Self(Vec3::new(
549 Self::FRAC_1_SQRT_3,
550 -Self::FRAC_1_SQRT_3,
551 -Self::FRAC_1_SQRT_3,
552 )),
553 Self(Vec3::new(
554 -Self::FRAC_1_SQRT_3,
555 -Self::FRAC_1_SQRT_3,
556 -Self::FRAC_1_SQRT_3,
557 )),
558 ];
559
560 pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
565 Self::new_and_length(value).map(|(dir, _)| dir)
566 }
567
568 pub fn new_unchecked(value: Vec3) -> Self {
574 #[cfg(debug_assertions)]
575 assert_is_normalized(
576 "The vector given to `Dir3::new_unchecked` is not normalized.",
577 value.length_squared(),
578 );
579
580 Self(value)
581 }
582
583 pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
589 let length = value.length();
590 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
591
592 direction
593 .map(|dir| (Self(dir), length))
594 .ok_or(InvalidDirectionError::from_length(length))
595 }
596
597 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
602 Self::new(Vec3::new(x, y, z))
603 }
604
605 pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
611 Self::new_unchecked(Vec3::new(x, y, z))
612 }
613
614 pub const fn as_vec3(&self) -> Vec3 {
616 self.0
617 }
618
619 #[inline]
649 pub fn slerp(self, rhs: Self, s: f32) -> Self {
650 let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
651 Dir3(quat.mul_vec3(self.0))
652 }
653
654 #[inline]
681 pub fn fast_renormalize(self) -> Self {
682 let length_squared = self.0.length_squared();
701 Self(self * (0.5 * (3.0 - length_squared)))
702 }
703}
704
705impl TryFrom<Vec3> for Dir3 {
706 type Error = InvalidDirectionError;
707
708 fn try_from(value: Vec3) -> Result<Self, Self::Error> {
709 Self::new(value)
710 }
711}
712
713impl core::ops::Deref for Dir3 {
714 type Target = Vec3;
715 fn deref(&self) -> &Self::Target {
716 &self.0
717 }
718}
719
720impl core::ops::Neg for Dir3 {
721 type Output = Self;
722 fn neg(self) -> Self::Output {
723 Self(-self.0)
724 }
725}
726
727impl core::ops::Mul<f32> for Dir3 {
728 type Output = Vec3;
729 fn mul(self, rhs: f32) -> Self::Output {
730 self.0 * rhs
731 }
732}
733
734impl core::ops::Mul<Dir3> for f32 {
735 type Output = Vec3;
736 fn mul(self, rhs: Dir3) -> Self::Output {
737 self * rhs.0
738 }
739}
740
741impl core::ops::Mul<Dir3> for Quat {
742 type Output = Dir3;
743
744 fn mul(self, direction: Dir3) -> Self::Output {
746 let rotated = self * *direction;
747
748 #[cfg(debug_assertions)]
749 assert_is_normalized(
750 "`Dir3` is denormalized after rotation.",
751 rotated.length_squared(),
752 );
753
754 Dir3(rotated)
755 }
756}
757
758impl fmt::Display for Dir3 {
759 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
760 write!(f, "{}", self.0)
761 }
762}
763
764#[cfg(feature = "approx")]
765impl approx::AbsDiffEq for Dir3 {
766 type Epsilon = f32;
767 fn default_epsilon() -> f32 {
768 f32::EPSILON
769 }
770 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
771 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
772 }
773}
774
775#[cfg(feature = "approx")]
776impl approx::RelativeEq for Dir3 {
777 fn default_max_relative() -> f32 {
778 f32::EPSILON
779 }
780 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
781 self.as_ref()
782 .relative_eq(other.as_ref(), epsilon, max_relative)
783 }
784}
785
786#[cfg(feature = "approx")]
787impl approx::UlpsEq for Dir3 {
788 fn default_max_ulps() -> u32 {
789 4
790 }
791 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
792 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
793 }
794}
795
796#[derive(Clone, Copy, Debug, PartialEq)]
801#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
802#[cfg_attr(
803 feature = "bevy_reflect",
804 derive(Reflect),
805 reflect(Debug, PartialEq, Clone)
806)]
807#[cfg_attr(
808 all(feature = "serialize", feature = "bevy_reflect"),
809 reflect(Serialize, Deserialize)
810)]
811#[doc(alias = "Direction3dA")]
812pub struct Dir3A(Vec3A);
813
814impl Dir3A {
815 pub const X: Self = Self(Vec3A::X);
817 pub const Y: Self = Self(Vec3A::Y);
819 pub const Z: Self = Self(Vec3A::Z);
821 pub const NEG_X: Self = Self(Vec3A::NEG_X);
823 pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
825 pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
827 pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
829
830 pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
835 Self::new_and_length(value).map(|(dir, _)| dir)
836 }
837
838 pub fn new_unchecked(value: Vec3A) -> Self {
844 #[cfg(debug_assertions)]
845 assert_is_normalized(
846 "The vector given to `Dir3A::new_unchecked` is not normalized.",
847 value.length_squared(),
848 );
849
850 Self(value)
851 }
852
853 pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> {
859 let length = value.length();
860 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
861
862 direction
863 .map(|dir| (Self(dir), length))
864 .ok_or(InvalidDirectionError::from_length(length))
865 }
866
867 pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
872 Self::new(Vec3A::new(x, y, z))
873 }
874
875 pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
881 Self::new_unchecked(Vec3A::new(x, y, z))
882 }
883
884 pub const fn as_vec3a(&self) -> Vec3A {
886 self.0
887 }
888
889 #[inline]
919 pub fn slerp(self, rhs: Self, s: f32) -> Self {
920 let quat = Quat::IDENTITY.slerp(
921 Quat::from_rotation_arc(Vec3::from(self.0), Vec3::from(rhs.0)),
922 s,
923 );
924 Dir3A(quat.mul_vec3a(self.0))
925 }
926
927 #[inline]
932 pub fn fast_renormalize(self) -> Self {
933 let length_squared = self.0.length_squared();
934 Self(self * (0.5 * (3.0 - length_squared)))
936 }
937}
938
939impl From<Dir3> for Dir3A {
940 fn from(value: Dir3) -> Self {
941 Self(value.0.into())
942 }
943}
944
945impl From<Dir3A> for Dir3 {
946 fn from(value: Dir3A) -> Self {
947 Self(value.0.into())
948 }
949}
950
951impl TryFrom<Vec3A> for Dir3A {
952 type Error = InvalidDirectionError;
953
954 fn try_from(value: Vec3A) -> Result<Self, Self::Error> {
955 Self::new(value)
956 }
957}
958
959impl From<Dir3A> for Vec3A {
960 fn from(value: Dir3A) -> Self {
961 value.0
962 }
963}
964
965impl core::ops::Deref for Dir3A {
966 type Target = Vec3A;
967 fn deref(&self) -> &Self::Target {
968 &self.0
969 }
970}
971
972impl core::ops::Neg for Dir3A {
973 type Output = Self;
974 fn neg(self) -> Self::Output {
975 Self(-self.0)
976 }
977}
978
979impl core::ops::Mul<f32> for Dir3A {
980 type Output = Vec3A;
981 fn mul(self, rhs: f32) -> Self::Output {
982 self.0 * rhs
983 }
984}
985
986impl core::ops::Mul<Dir3A> for f32 {
987 type Output = Vec3A;
988 fn mul(self, rhs: Dir3A) -> Self::Output {
989 self * rhs.0
990 }
991}
992
993impl core::ops::Mul<Dir3A> for Quat {
994 type Output = Dir3A;
995
996 fn mul(self, direction: Dir3A) -> Self::Output {
998 let rotated = self * *direction;
999
1000 #[cfg(debug_assertions)]
1001 assert_is_normalized(
1002 "`Dir3A` is denormalized after rotation.",
1003 rotated.length_squared(),
1004 );
1005
1006 Dir3A(rotated)
1007 }
1008}
1009
1010impl fmt::Display for Dir3A {
1011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012 write!(f, "{}", self.0)
1013 }
1014}
1015
1016#[cfg(feature = "approx")]
1017impl approx::AbsDiffEq for Dir3A {
1018 type Epsilon = f32;
1019 fn default_epsilon() -> f32 {
1020 f32::EPSILON
1021 }
1022 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
1023 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
1024 }
1025}
1026
1027#[cfg(feature = "approx")]
1028impl approx::RelativeEq for Dir3A {
1029 fn default_max_relative() -> f32 {
1030 f32::EPSILON
1031 }
1032 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
1033 self.as_ref()
1034 .relative_eq(other.as_ref(), epsilon, max_relative)
1035 }
1036}
1037
1038#[cfg(feature = "approx")]
1039impl approx::UlpsEq for Dir3A {
1040 fn default_max_ulps() -> u32 {
1041 4
1042 }
1043 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
1044 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
1045 }
1046}
1047
1048#[derive(Clone, Copy, Debug, PartialEq, Into)]
1050#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1051#[cfg_attr(
1052 feature = "bevy_reflect",
1053 derive(Reflect),
1054 reflect(Debug, PartialEq, Clone)
1055)]
1056#[cfg_attr(
1057 all(feature = "serialize", feature = "bevy_reflect"),
1058 reflect(Serialize, Deserialize)
1059)]
1060#[doc(alias = "Direction4d")]
1061pub struct Dir4(Vec4);
1062
1063impl Dir4 {
1064 pub const X: Self = Self(Vec4::X);
1066 pub const Y: Self = Self(Vec4::Y);
1068 pub const Z: Self = Self(Vec4::Z);
1070 pub const W: Self = Self(Vec4::W);
1072 pub const NEG_X: Self = Self(Vec4::NEG_X);
1074 pub const NEG_Y: Self = Self(Vec4::NEG_Y);
1076 pub const NEG_Z: Self = Self(Vec4::NEG_Z);
1078 pub const NEG_W: Self = Self(Vec4::NEG_W);
1080 pub const AXES: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
1082
1083 pub fn new(value: Vec4) -> Result<Self, InvalidDirectionError> {
1088 Self::new_and_length(value).map(|(dir, _)| dir)
1089 }
1090
1091 pub fn new_unchecked(value: Vec4) -> Self {
1097 #[cfg(debug_assertions)]
1098 assert_is_normalized(
1099 "The vector given to `Dir4::new_unchecked` is not normalized.",
1100 value.length_squared(),
1101 );
1102 Self(value)
1103 }
1104
1105 pub fn new_and_length(value: Vec4) -> Result<(Self, f32), InvalidDirectionError> {
1111 let length = value.length();
1112 let direction = (length.is_finite() && length > 0.0).then_some(value / length);
1113
1114 direction
1115 .map(|dir| (Self(dir), length))
1116 .ok_or(InvalidDirectionError::from_length(length))
1117 }
1118
1119 pub fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Result<Self, InvalidDirectionError> {
1124 Self::new(Vec4::new(x, y, z, w))
1125 }
1126
1127 pub fn from_xyzw_unchecked(x: f32, y: f32, z: f32, w: f32) -> Self {
1133 Self::new_unchecked(Vec4::new(x, y, z, w))
1134 }
1135
1136 pub const fn as_vec4(&self) -> Vec4 {
1138 self.0
1139 }
1140
1141 #[inline]
1144 pub fn fast_renormalize(self) -> Self {
1145 let length_squared = self.0.length_squared();
1164 Self(self * (0.5 * (3.0 - length_squared)))
1165 }
1166}
1167
1168impl TryFrom<Vec4> for Dir4 {
1169 type Error = InvalidDirectionError;
1170
1171 fn try_from(value: Vec4) -> Result<Self, Self::Error> {
1172 Self::new(value)
1173 }
1174}
1175
1176impl core::ops::Deref for Dir4 {
1177 type Target = Vec4;
1178 fn deref(&self) -> &Self::Target {
1179 &self.0
1180 }
1181}
1182
1183impl core::ops::Neg for Dir4 {
1184 type Output = Self;
1185 fn neg(self) -> Self::Output {
1186 Self(-self.0)
1187 }
1188}
1189
1190impl core::ops::Mul<f32> for Dir4 {
1191 type Output = Vec4;
1192 fn mul(self, rhs: f32) -> Self::Output {
1193 self.0 * rhs
1194 }
1195}
1196
1197impl core::ops::Mul<Dir4> for f32 {
1198 type Output = Vec4;
1199 fn mul(self, rhs: Dir4) -> Self::Output {
1200 self * rhs.0
1201 }
1202}
1203
1204impl fmt::Display for Dir4 {
1205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1206 write!(f, "{}", self.0)
1207 }
1208}
1209
1210#[cfg(feature = "approx")]
1211impl approx::AbsDiffEq for Dir4 {
1212 type Epsilon = f32;
1213 fn default_epsilon() -> f32 {
1214 f32::EPSILON
1215 }
1216 fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
1217 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
1218 }
1219}
1220
1221#[cfg(feature = "approx")]
1222impl approx::RelativeEq for Dir4 {
1223 fn default_max_relative() -> f32 {
1224 f32::EPSILON
1225 }
1226 fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
1227 self.as_ref()
1228 .relative_eq(other.as_ref(), epsilon, max_relative)
1229 }
1230}
1231
1232#[cfg(feature = "approx")]
1233impl approx::UlpsEq for Dir4 {
1234 fn default_max_ulps() -> u32 {
1235 4
1236 }
1237
1238 fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
1239 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
1240 }
1241}
1242
1243#[cfg(test)]
1244#[cfg(feature = "approx")]
1245mod tests {
1246 use crate::ops;
1247
1248 use super::*;
1249 use approx::assert_relative_eq;
1250
1251 #[test]
1252 fn dir2_creation() {
1253 assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X));
1254 assert_eq!(
1255 Dir2::new(Vec2::new(0.0, 0.0)),
1256 Err(InvalidDirectionError::Zero)
1257 );
1258 assert_eq!(
1259 Dir2::new(Vec2::new(f32::INFINITY, 0.0)),
1260 Err(InvalidDirectionError::Infinite)
1261 );
1262 assert_eq!(
1263 Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)),
1264 Err(InvalidDirectionError::Infinite)
1265 );
1266 assert_eq!(
1267 Dir2::new(Vec2::new(f32::NAN, 0.0)),
1268 Err(InvalidDirectionError::NaN)
1269 );
1270 assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5)));
1271 }
1272
1273 #[test]
1274 fn dir2_slerp() {
1275 assert_relative_eq!(
1276 Dir2::X.slerp(Dir2::Y, 0.5),
1277 Dir2::from_xy(ops::sqrt(0.5_f32), ops::sqrt(0.5_f32)).unwrap()
1278 );
1279 assert_eq!(Dir2::Y.slerp(Dir2::X, 0.0), Dir2::Y);
1280 assert_relative_eq!(Dir2::X.slerp(Dir2::Y, 1.0), Dir2::Y);
1281 assert_relative_eq!(
1282 Dir2::Y.slerp(Dir2::X, 1.0 / 3.0),
1283 Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1284 );
1285 assert_relative_eq!(
1286 Dir2::X.slerp(Dir2::Y, 2.0 / 3.0),
1287 Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1288 );
1289 }
1290
1291 #[test]
1292 fn dir2_to_rotation2d() {
1293 assert_relative_eq!(Dir2::EAST.rotation_to(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1294 assert_relative_eq!(Dir2::NORTH.rotation_from(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1295 assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rot2::FRAC_PI_2);
1296 assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rot2::PI);
1297 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_x(), Rot2::degrees(135.0));
1298 assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rot2::FRAC_PI_4);
1299 }
1300
1301 #[test]
1302 fn dir2_renorm() {
1303 let (sin, cos) = ops::sin_cos(1.0_f32);
1305 let rot2 = Rot2::from_sin_cos(sin * (1.0 + 1e-5), cos * (1.0 + 1e-5));
1306 let mut dir_a = Dir2::X;
1307 let mut dir_b = Dir2::X;
1308
1309 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1311
1312 for _ in 0..50 {
1313 dir_a = rot2 * dir_a;
1314 dir_b = rot2 * dir_b;
1315 dir_b = dir_b.fast_renormalize();
1316 }
1317
1318 assert!(
1320 !dir_a.is_normalized(),
1321 "Denormalization doesn't work, test is faulty"
1322 );
1323 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1324 }
1325
1326 #[test]
1327 fn dir2_perp() {
1328 assert_eq!(Dir2::X.perpendicular(), Dir2::Y);
1330
1331 assert_eq!(Dir2::Y.perpendicular(), Dir2::NEG_X);
1333
1334 assert_eq!(Dir2::NEG_X.perpendicular(), Dir2::NEG_Y);
1336
1337 assert_eq!(Dir2::NEG_Y.perpendicular(), Dir2::X);
1339 }
1340
1341 #[test]
1342 fn dir3_creation() {
1343 assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
1344 assert_eq!(
1345 Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
1346 Err(InvalidDirectionError::Zero)
1347 );
1348 assert_eq!(
1349 Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
1350 Err(InvalidDirectionError::Infinite)
1351 );
1352 assert_eq!(
1353 Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
1354 Err(InvalidDirectionError::Infinite)
1355 );
1356 assert_eq!(
1357 Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
1358 Err(InvalidDirectionError::NaN)
1359 );
1360 assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
1361
1362 assert!(
1364 (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3::X)
1365 .abs_diff_eq(Vec3::Y, 10e-6)
1366 );
1367 }
1368
1369 #[test]
1370 fn dir3_slerp() {
1371 assert_relative_eq!(
1372 Dir3::X.slerp(Dir3::Y, 0.5),
1373 Dir3::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1374 );
1375 assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
1376 assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
1377 assert_relative_eq!(
1378 Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
1379 Dir3::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1380 epsilon = 0.000001
1381 );
1382 assert_relative_eq!(
1383 Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
1384 Dir3::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1385 );
1386 }
1387
1388 #[test]
1389 fn dir3_renorm() {
1390 let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1392 let mut dir_a = Dir3::X;
1393 let mut dir_b = Dir3::X;
1394
1395 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1397
1398 for _ in 0..50 {
1399 dir_a = rot3 * dir_a;
1400 dir_b = rot3 * dir_b;
1401 dir_b = dir_b.fast_renormalize();
1402 }
1403
1404 assert!(
1406 !dir_a.is_normalized(),
1407 "Denormalization doesn't work, test is faulty"
1408 );
1409 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1410 }
1411
1412 #[test]
1413 fn dir3a_creation() {
1414 assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
1415 assert_eq!(
1416 Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
1417 Err(InvalidDirectionError::Zero)
1418 );
1419 assert_eq!(
1420 Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
1421 Err(InvalidDirectionError::Infinite)
1422 );
1423 assert_eq!(
1424 Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
1425 Err(InvalidDirectionError::Infinite)
1426 );
1427 assert_eq!(
1428 Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
1429 Err(InvalidDirectionError::NaN)
1430 );
1431 assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
1432
1433 assert!(
1435 (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3A::X)
1436 .abs_diff_eq(Vec3A::Y, 10e-6)
1437 );
1438 }
1439
1440 #[test]
1441 fn dir3a_slerp() {
1442 assert_relative_eq!(
1443 Dir3A::X.slerp(Dir3A::Y, 0.5),
1444 Dir3A::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1445 );
1446 assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
1447 assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
1448 assert_relative_eq!(
1449 Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
1450 Dir3A::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1451 epsilon = 0.000001
1452 );
1453 assert_relative_eq!(
1454 Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
1455 Dir3A::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1456 );
1457 }
1458
1459 #[test]
1460 fn dir3a_renorm() {
1461 let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1463 let mut dir_a = Dir3A::X;
1464 let mut dir_b = Dir3A::X;
1465
1466 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1468
1469 for _ in 0..50 {
1470 dir_a = rot3 * dir_a;
1471 dir_b = rot3 * dir_b;
1472 dir_b = dir_b.fast_renormalize();
1473 }
1474
1475 assert!(
1477 !dir_a.is_normalized(),
1478 "Denormalization doesn't work, test is faulty"
1479 );
1480 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1481 }
1482
1483 #[test]
1484 fn dir4_creation() {
1485 assert_eq!(Dir4::new(Vec4::X * 12.5), Ok(Dir4::X));
1486 assert_eq!(
1487 Dir4::new(Vec4::new(0.0, 0.0, 0.0, 0.0)),
1488 Err(InvalidDirectionError::Zero)
1489 );
1490 assert_eq!(
1491 Dir4::new(Vec4::new(f32::INFINITY, 0.0, 0.0, 0.0)),
1492 Err(InvalidDirectionError::Infinite)
1493 );
1494 assert_eq!(
1495 Dir4::new(Vec4::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)),
1496 Err(InvalidDirectionError::Infinite)
1497 );
1498 assert_eq!(
1499 Dir4::new(Vec4::new(f32::NAN, 0.0, 0.0, 0.0)),
1500 Err(InvalidDirectionError::NaN)
1501 );
1502 assert_eq!(Dir4::new_and_length(Vec4::X * 6.5), Ok((Dir4::X, 6.5)));
1503 }
1504
1505 #[test]
1506 fn dir4_renorm() {
1507 let mat4 = bevy_math::Mat4::from_quat(Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0))
1509 * (1.0 + 1e-5);
1510 let mut dir_a = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1511 let mut dir_b = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1512 assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1514 for _ in 0..50 {
1515 dir_a = Dir4(mat4 * *dir_a);
1516 dir_b = Dir4(mat4 * *dir_b);
1517 dir_b = dir_b.fast_renormalize();
1518 }
1519 assert!(
1521 !dir_a.is_normalized(),
1522 "Denormalization doesn't work, test is faulty"
1523 );
1524 assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1525 }
1526}