Skip to main content

bevy_math/
direction.rs

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/// An error indicating that a direction is invalid.
22#[derive(Debug, PartialEq, Error)]
23pub enum InvalidDirectionError {
24    /// The length of the direction vector is zero or very close to zero.
25    #[error("The length of the direction vector is zero or very close to zero")]
26    Zero,
27    /// The length of the direction vector is `std::f32::INFINITY`.
28    #[error("The length of the direction vector is `std::f32::INFINITY`")]
29    Infinite,
30    /// The length of the direction vector is `NaN`.
31    #[error("The length of the direction vector is `NaN`")]
32    NaN,
33}
34
35impl InvalidDirectionError {
36    /// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector.
37    pub fn from_length(length: f32) -> Self {
38        if length.is_nan() {
39            InvalidDirectionError::NaN
40        } else if !length.is_finite() {
41            // If the direction is non-finite but also not NaN, it must be infinite
42            InvalidDirectionError::Infinite
43        } else {
44            // If the direction is invalid but neither NaN nor infinite, it must be zero
45            InvalidDirectionError::Zero
46        }
47    }
48}
49
50/// Checks that a vector with the given squared length is normalized.
51///
52/// Warns for small error with a length threshold of approximately `1e-4`,
53/// and panics for large error with a length threshold of approximately `1e-2`.
54///
55/// The format used for the logged warning is `"Warning: {warning} The length is {length}`,
56/// and similarly for the error.
57#[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    // Panic for large error and warn for slight error.
64    if length_error_squared > 2e-2 || length_error_squared.is_nan() {
65        // Length error is approximately 1e-2 or more.
66        panic!(
67            "Error: {message} The length is {}.",
68            ops::sqrt(length_squared)
69        );
70    } else if length_error_squared > 2e-4 {
71        // Length error is approximately 1e-4 or more.
72        #[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/// A normalized vector pointing in a direction in 2D space
84#[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    /// A unit vector pointing along the positive X axis.
101    pub const X: Self = Self(Vec2::X);
102    /// A unit vector pointing along the positive Y axis.
103    pub const Y: Self = Self(Vec2::Y);
104    /// A unit vector pointing along the negative X axis.
105    pub const NEG_X: Self = Self(Vec2::NEG_X);
106    /// A unit vector pointing along the negative Y axis.
107    pub const NEG_Y: Self = Self(Vec2::NEG_Y);
108    /// The directional axes.
109    pub const AXES: [Self; 2] = [Self::X, Self::Y];
110    /// The cardinal directions.
111    pub const CARDINALS: [Self; 4] = [Self::X, Self::NEG_X, Self::Y, Self::NEG_Y];
112
113    /// The "north" direction, equivalent to [`Dir2::Y`].
114    pub const NORTH: Self = Self(Vec2::Y);
115    /// The "south" direction, equivalent to [`Dir2::NEG_Y`].
116    pub const SOUTH: Self = Self(Vec2::NEG_Y);
117    /// The "east" direction, equivalent to [`Dir2::X`].
118    pub const EAST: Self = Self(Vec2::X);
119    /// The "west" direction, equivalent to [`Dir2::NEG_X`].
120    pub const WEST: Self = Self(Vec2::NEG_X);
121    /// The "north-east" direction, between [`Dir2::NORTH`] and [`Dir2::EAST`].
122    pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
123    /// The "north-west" direction, between [`Dir2::NORTH`] and [`Dir2::WEST`].
124    pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
125    /// The "south-east" direction, between [`Dir2::SOUTH`] and [`Dir2::EAST`].
126    pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
127    /// The "south-west" direction, between [`Dir2::SOUTH`] and [`Dir2::WEST`].
128    pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
129
130    /// The diagonals between the cardinal directions.
131    pub const DIAGONALS: [Self; 4] = [
132        Self::NORTH_EAST,
133        Self::NORTH_WEST,
134        Self::SOUTH_EAST,
135        Self::SOUTH_WEST,
136    ];
137    /// All neighbors of a tile on a square grid in a 3x3 neighborhood. A combination of [`Self::CARDINALS`] and [`Self::DIAGONALS`]
138    pub const ALL_NEIGHBORS: [Self; 8] = [
139        Self::X,
140        Self::NEG_X,
141        Self::Y,
142        Self::NEG_Y,
143        Self::NORTH_EAST,
144        Self::NORTH_WEST,
145        Self::SOUTH_EAST,
146        Self::SOUTH_WEST,
147    ];
148
149    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it.
150    ///
151    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
152    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
153    pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
154        Self::new_and_length(value).map(|(dir, _)| dir)
155    }
156
157    /// Create a [`Dir2`] from a [`Vec2`] that is already normalized.
158    ///
159    /// # Warning
160    ///
161    /// `value` must be normalized, i.e its length must be `1.0`.
162    pub fn new_unchecked(value: Vec2) -> Self {
163        #[cfg(debug_assertions)]
164        assert_is_normalized(
165            "The vector given to `Dir2::new_unchecked` is not normalized.",
166            value.length_squared(),
167        );
168
169        Self(value)
170    }
171
172    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it and
173    /// also returning its original length.
174    ///
175    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
176    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
177    pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
178        let length = value.length();
179        let direction = (length.is_finite() && length > 0.0).then_some(value / length);
180
181        direction
182            .map(|dir| (Self(dir), length))
183            .ok_or(InvalidDirectionError::from_length(length))
184    }
185
186    /// Create a direction from its `x` and `y` components.
187    ///
188    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
189    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
190    pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
191        Self::new(Vec2::new(x, y))
192    }
193
194    /// Create a direction from its `x` and `y` components, assuming the resulting vector is normalized.
195    ///
196    /// # Warning
197    ///
198    /// The vector produced from `x` and `y` must be normalized, i.e its length must be `1.0`.
199    pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
200        Self::new_unchecked(Vec2::new(x, y))
201    }
202
203    /// Creates a 2D direction containing `[angle.cos(), angle.sin()]`.
204    #[inline]
205    pub fn from_angle(angle: f32) -> Self {
206        Self(Vec2::from_angle(angle))
207    }
208
209    /// Returns the inner [`Vec2`]
210    pub const fn as_vec2(&self) -> Vec2 {
211        self.0
212    }
213
214    /// Performs a spherical linear interpolation between `self` and `rhs`
215    /// based on the value `s`.
216    ///
217    /// This corresponds to interpolating between the two directions at a constant angular velocity.
218    ///
219    /// When `s == 0.0`, the result will be equal to `self`.
220    /// When `s == 1.0`, the result will be equal to `rhs`.
221    ///
222    /// # Example
223    ///
224    /// ```
225    /// # use bevy_math::Dir2;
226    /// # use approx::{assert_relative_eq, RelativeEq};
227    /// #
228    /// let dir1 = Dir2::X;
229    /// let dir2 = Dir2::Y;
230    ///
231    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
232    /// #[cfg(feature = "approx")]
233    /// assert_relative_eq!(result1, Dir2::from_xy(0.75_f32.sqrt(), 0.5).unwrap());
234    ///
235    /// let result2 = dir1.slerp(dir2, 0.5);
236    /// #[cfg(feature = "approx")]
237    /// assert_relative_eq!(result2, Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap());
238    /// ```
239    #[inline]
240    pub fn slerp(self, rhs: Self, s: f32) -> Self {
241        let angle = self.angle_to(rhs.0);
242        Rot2::radians(angle * s) * self
243    }
244
245    /// Get the rotation that rotates this direction to `other`.
246    #[inline]
247    pub fn rotation_to(self, other: Self) -> Rot2 {
248        // Rotate `self` to X-axis, then X-axis to `other`:
249        other.rotation_from_x() * self.rotation_to_x()
250    }
251
252    /// Get the rotation that rotates `other` to this direction.
253    #[inline]
254    pub fn rotation_from(self, other: Self) -> Rot2 {
255        other.rotation_to(self)
256    }
257
258    /// Get the rotation that rotates the X-axis to this direction.
259    #[inline]
260    pub fn rotation_from_x(self) -> Rot2 {
261        Rot2::from_sin_cos(self.0.y, self.0.x)
262    }
263
264    /// Get the rotation that rotates this direction to the X-axis.
265    #[inline]
266    pub fn rotation_to_x(self) -> Rot2 {
267        // (This is cheap, it just negates one component.)
268        self.rotation_from_x().inverse()
269    }
270
271    /// Get the rotation that rotates the Y-axis to this direction.
272    #[inline]
273    pub fn rotation_from_y(self) -> Rot2 {
274        // `x <- y`, `y <- -x` correspond to rotating clockwise by pi/2;
275        // this transforms the Y-axis into the X-axis, maintaining the relative position
276        // of our direction. Then we just use the same technique as `rotation_from_x`.
277        Rot2::from_sin_cos(-self.0.x, self.0.y)
278    }
279
280    /// Get the rotation that rotates this direction to the Y-axis.
281    #[inline]
282    pub fn rotation_to_y(self) -> Rot2 {
283        self.rotation_from_y().inverse()
284    }
285
286    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
287    /// Useful for preventing numerical error accumulation.
288    /// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
289    #[inline]
290    pub fn fast_renormalize(self) -> Self {
291        let length_squared = self.0.length_squared();
292        // Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
293        Self(self * (0.5 * (3.0 - length_squared)))
294    }
295}
296
297impl TryFrom<Vec2> for Dir2 {
298    type Error = InvalidDirectionError;
299
300    fn try_from(value: Vec2) -> Result<Self, Self::Error> {
301        Self::new(value)
302    }
303}
304
305impl From<Dir2> for Vec2 {
306    fn from(value: Dir2) -> Self {
307        value.as_vec2()
308    }
309}
310
311impl core::ops::Deref for Dir2 {
312    type Target = Vec2;
313    fn deref(&self) -> &Self::Target {
314        &self.0
315    }
316}
317
318impl core::ops::Neg for Dir2 {
319    type Output = Self;
320    fn neg(self) -> Self::Output {
321        Self(-self.0)
322    }
323}
324
325impl core::ops::Mul<f32> for Dir2 {
326    type Output = Vec2;
327    fn mul(self, rhs: f32) -> Self::Output {
328        self.0 * rhs
329    }
330}
331
332impl core::ops::Mul<Dir2> for f32 {
333    type Output = Vec2;
334    fn mul(self, rhs: Dir2) -> Self::Output {
335        self * rhs.0
336    }
337}
338
339impl core::ops::Mul<Dir2> for Rot2 {
340    type Output = Dir2;
341
342    /// Rotates the [`Dir2`] using a [`Rot2`].
343    fn mul(self, direction: Dir2) -> Self::Output {
344        let rotated = self * *direction;
345
346        #[cfg(debug_assertions)]
347        assert_is_normalized(
348            "`Dir2` is denormalized after rotation.",
349            rotated.length_squared(),
350        );
351
352        Dir2(rotated)
353    }
354}
355
356impl fmt::Display for Dir2 {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        write!(f, "{}", self.0)
359    }
360}
361
362#[cfg(any(feature = "approx", test))]
363impl approx::AbsDiffEq for Dir2 {
364    type Epsilon = f32;
365    fn default_epsilon() -> f32 {
366        f32::EPSILON
367    }
368    fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
369        self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
370    }
371}
372
373#[cfg(any(feature = "approx", test))]
374impl approx::RelativeEq for Dir2 {
375    fn default_max_relative() -> f32 {
376        f32::EPSILON
377    }
378    fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
379        self.as_ref()
380            .relative_eq(other.as_ref(), epsilon, max_relative)
381    }
382}
383
384#[cfg(any(feature = "approx", test))]
385impl approx::UlpsEq for Dir2 {
386    fn default_max_ulps() -> u32 {
387        4
388    }
389    fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
390        self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
391    }
392}
393
394/// A normalized vector pointing in a direction in 3D space
395#[derive(Clone, Copy, Debug, PartialEq, Into)]
396#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
397#[cfg_attr(
398    feature = "bevy_reflect",
399    derive(Reflect),
400    reflect(Debug, PartialEq, Clone)
401)]
402#[cfg_attr(
403    all(feature = "serialize", feature = "bevy_reflect"),
404    reflect(Serialize, Deserialize)
405)]
406#[doc(alias = "Direction3d")]
407pub struct Dir3(Vec3);
408impl Primitive3d for Dir3 {}
409
410impl Dir3 {
411    /// A unit vector pointing along the positive X axis.
412    pub const X: Self = Self(Vec3::X);
413    /// A unit vector pointing along the positive Y axis.
414    pub const Y: Self = Self(Vec3::Y);
415    /// A unit vector pointing along the positive Z axis.
416    pub const Z: Self = Self(Vec3::Z);
417    /// A unit vector pointing along the negative X axis.
418    pub const NEG_X: Self = Self(Vec3::NEG_X);
419    /// A unit vector pointing along the negative Y axis.
420    pub const NEG_Y: Self = Self(Vec3::NEG_Y);
421    /// A unit vector pointing along the negative Z axis.
422    pub const NEG_Z: Self = Self(Vec3::NEG_Z);
423    /// The directional axes.
424    pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
425    /// The cardinal directions.
426    pub const CARDINALS: [Self; 6] = [
427        Self::X,
428        Self::NEG_X,
429        Self::Y,
430        Self::NEG_Y,
431        Self::Z,
432        Self::NEG_Z,
433    ];
434
435    // Adding this allow here to make sure that the precision in FRAC_1_SQRT_2
436    // and here is the same
437    /// Approximation of 1/sqrt(3) needed for the diagonals in 3D space
438    const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32;
439    /// The directions pointing towards the vertices of a cube centered at the origin.
440    pub const ALL_VERTICES: [Self; 8] = [
441        Self(Vec3::new(
442            Self::FRAC_1_SQRT_3,
443            Self::FRAC_1_SQRT_3,
444            Self::FRAC_1_SQRT_3,
445        )),
446        Self(Vec3::new(
447            -Self::FRAC_1_SQRT_3,
448            Self::FRAC_1_SQRT_3,
449            Self::FRAC_1_SQRT_3,
450        )),
451        Self(Vec3::new(
452            Self::FRAC_1_SQRT_3,
453            -Self::FRAC_1_SQRT_3,
454            Self::FRAC_1_SQRT_3,
455        )),
456        Self(Vec3::new(
457            -Self::FRAC_1_SQRT_3,
458            -Self::FRAC_1_SQRT_3,
459            Self::FRAC_1_SQRT_3,
460        )),
461        Self(Vec3::new(
462            Self::FRAC_1_SQRT_3,
463            Self::FRAC_1_SQRT_3,
464            -Self::FRAC_1_SQRT_3,
465        )),
466        Self(Vec3::new(
467            -Self::FRAC_1_SQRT_3,
468            Self::FRAC_1_SQRT_3,
469            -Self::FRAC_1_SQRT_3,
470        )),
471        Self(Vec3::new(
472            Self::FRAC_1_SQRT_3,
473            -Self::FRAC_1_SQRT_3,
474            -Self::FRAC_1_SQRT_3,
475        )),
476        Self(Vec3::new(
477            -Self::FRAC_1_SQRT_3,
478            -Self::FRAC_1_SQRT_3,
479            -Self::FRAC_1_SQRT_3,
480        )),
481    ];
482    /// The directions towards centers of each edge of a cube
483    pub const ALL_EDGES: [Self; 12] = [
484        Self(Vec3::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
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, 0., FRAC_1_SQRT_2)),
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(0., FRAC_1_SQRT_2, 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    ];
497    /// All neighbors of a tile on a cube grid a 3x3x3 neighborhood. A combination of [`Self::CARDINALS`], [`Self::ALL_EDGES`] and [`Self::ALL_VERTICES`]
498    pub const ALL_NEIGHBORS: [Self; 26] = [
499        Self::X,
500        Self::NEG_X,
501        Self::Y,
502        Self::NEG_Y,
503        Self::Z,
504        Self::NEG_Z,
505        Self(Vec3::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.)),
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, 0., FRAC_1_SQRT_2)),
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(0., FRAC_1_SQRT_2, 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(
518            Self::FRAC_1_SQRT_3,
519            Self::FRAC_1_SQRT_3,
520            Self::FRAC_1_SQRT_3,
521        )),
522        Self(Vec3::new(
523            -Self::FRAC_1_SQRT_3,
524            Self::FRAC_1_SQRT_3,
525            Self::FRAC_1_SQRT_3,
526        )),
527        Self(Vec3::new(
528            Self::FRAC_1_SQRT_3,
529            -Self::FRAC_1_SQRT_3,
530            Self::FRAC_1_SQRT_3,
531        )),
532        Self(Vec3::new(
533            -Self::FRAC_1_SQRT_3,
534            -Self::FRAC_1_SQRT_3,
535            Self::FRAC_1_SQRT_3,
536        )),
537        Self(Vec3::new(
538            Self::FRAC_1_SQRT_3,
539            Self::FRAC_1_SQRT_3,
540            -Self::FRAC_1_SQRT_3,
541        )),
542        Self(Vec3::new(
543            -Self::FRAC_1_SQRT_3,
544            Self::FRAC_1_SQRT_3,
545            -Self::FRAC_1_SQRT_3,
546        )),
547        Self(Vec3::new(
548            Self::FRAC_1_SQRT_3,
549            -Self::FRAC_1_SQRT_3,
550            -Self::FRAC_1_SQRT_3,
551        )),
552        Self(Vec3::new(
553            -Self::FRAC_1_SQRT_3,
554            -Self::FRAC_1_SQRT_3,
555            -Self::FRAC_1_SQRT_3,
556        )),
557    ];
558
559    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
560    ///
561    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
562    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
563    pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
564        Self::new_and_length(value).map(|(dir, _)| dir)
565    }
566
567    /// Create a [`Dir3`] from a [`Vec3`] that is already normalized.
568    ///
569    /// # Warning
570    ///
571    /// `value` must be normalized, i.e its length must be `1.0`.
572    pub fn new_unchecked(value: Vec3) -> Self {
573        #[cfg(debug_assertions)]
574        assert_is_normalized(
575            "The vector given to `Dir3::new_unchecked` is not normalized.",
576            value.length_squared(),
577        );
578
579        Self(value)
580    }
581
582    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it and
583    /// also returning its original length.
584    ///
585    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
586    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
587    pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
588        let length = value.length();
589        let direction = (length.is_finite() && length > 0.0).then_some(value / length);
590
591        direction
592            .map(|dir| (Self(dir), length))
593            .ok_or(InvalidDirectionError::from_length(length))
594    }
595
596    /// Create a direction from its `x`, `y`, and `z` components.
597    ///
598    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
599    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
600    pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
601        Self::new(Vec3::new(x, y, z))
602    }
603
604    /// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
605    ///
606    /// # Warning
607    ///
608    /// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
609    pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
610        Self::new_unchecked(Vec3::new(x, y, z))
611    }
612
613    /// Returns the inner [`Vec3`]
614    pub const fn as_vec3(&self) -> Vec3 {
615        self.0
616    }
617
618    /// Performs a spherical linear interpolation between `self` and `rhs`
619    /// based on the value `s`.
620    ///
621    /// This corresponds to interpolating between the two directions at a constant angular velocity.
622    ///
623    /// When `s == 0.0`, the result will be equal to `self`.
624    /// When `s == 1.0`, the result will be equal to `rhs`.
625    ///
626    /// # Example
627    ///
628    /// ```
629    /// # use bevy_math::Dir3;
630    /// # use approx::{assert_relative_eq, RelativeEq};
631    /// #
632    /// let dir1 = Dir3::X;
633    /// let dir2 = Dir3::Y;
634    ///
635    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
636    /// #[cfg(feature = "approx")]
637    /// assert_relative_eq!(
638    ///     result1,
639    ///     Dir3::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
640    ///     epsilon = 0.000001
641    /// );
642    ///
643    /// let result2 = dir1.slerp(dir2, 0.5);
644    /// #[cfg(feature = "approx")]
645    /// assert_relative_eq!(result2, Dir3::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
646    /// ```
647    #[inline]
648    pub fn slerp(self, rhs: Self, s: f32) -> Self {
649        let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
650        Dir3(quat.mul_vec3(self.0))
651    }
652
653    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
654    /// Useful for preventing numerical error accumulation.
655    ///
656    /// # Example
657    /// The following seemingly benign code would start accumulating errors over time,
658    /// leading to `dir` eventually not being normalized anymore.
659    /// ```
660    /// # use bevy_math::prelude::*;
661    /// # let N: usize = 200;
662    /// let mut dir = Dir3::X;
663    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
664    /// for i in 0..N {
665    ///     dir = quaternion * dir;
666    /// }
667    /// ```
668    /// Instead, do the following.
669    /// ```
670    /// # use bevy_math::prelude::*;
671    /// # let N: usize = 200;
672    /// let mut dir = Dir3::X;
673    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
674    /// for i in 0..N {
675    ///     dir = quaternion * dir;
676    ///     dir = dir.fast_renormalize();
677    /// }
678    /// ```
679    #[inline]
680    pub fn fast_renormalize(self) -> Self {
681        // We numerically approximate the inverse square root by a Taylor series around 1
682        // As we expect the error (x := length_squared - 1) to be small
683        // inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
684        // inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
685
686        // Iterative calls to this method quickly converge to a normalized value,
687        // so long as the denormalization is not large ~ O(1/10).
688        // One iteration can be described as:
689        // l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
690        // Rewriting in terms of the error x:
691        // 1 + x <- (1 + x) * (1 - 1/2 x)²
692        // 1 + x <- (1 + x) * (1 - x + 1/4 x²)
693        // 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
694        // x <- -1/4 x² (3 - x)
695        // If the error is small, say in a range of (-1/2, 1/2), then:
696        // |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
697        // Therefore the sequence of iterates converges to 0 error as a second order method.
698
699        let length_squared = self.0.length_squared();
700        Self(self * (0.5 * (3.0 - length_squared)))
701    }
702}
703
704impl TryFrom<Vec3> for Dir3 {
705    type Error = InvalidDirectionError;
706
707    fn try_from(value: Vec3) -> Result<Self, Self::Error> {
708        Self::new(value)
709    }
710}
711
712impl core::ops::Deref for Dir3 {
713    type Target = Vec3;
714    fn deref(&self) -> &Self::Target {
715        &self.0
716    }
717}
718
719impl core::ops::Neg for Dir3 {
720    type Output = Self;
721    fn neg(self) -> Self::Output {
722        Self(-self.0)
723    }
724}
725
726impl core::ops::Mul<f32> for Dir3 {
727    type Output = Vec3;
728    fn mul(self, rhs: f32) -> Self::Output {
729        self.0 * rhs
730    }
731}
732
733impl core::ops::Mul<Dir3> for f32 {
734    type Output = Vec3;
735    fn mul(self, rhs: Dir3) -> Self::Output {
736        self * rhs.0
737    }
738}
739
740impl core::ops::Mul<Dir3> for Quat {
741    type Output = Dir3;
742
743    /// Rotates the [`Dir3`] using a [`Quat`].
744    fn mul(self, direction: Dir3) -> Self::Output {
745        let rotated = self * *direction;
746
747        #[cfg(debug_assertions)]
748        assert_is_normalized(
749            "`Dir3` is denormalized after rotation.",
750            rotated.length_squared(),
751        );
752
753        Dir3(rotated)
754    }
755}
756
757impl fmt::Display for Dir3 {
758    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
759        write!(f, "{}", self.0)
760    }
761}
762
763#[cfg(feature = "approx")]
764impl approx::AbsDiffEq for Dir3 {
765    type Epsilon = f32;
766    fn default_epsilon() -> f32 {
767        f32::EPSILON
768    }
769    fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
770        self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
771    }
772}
773
774#[cfg(feature = "approx")]
775impl approx::RelativeEq for Dir3 {
776    fn default_max_relative() -> f32 {
777        f32::EPSILON
778    }
779    fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
780        self.as_ref()
781            .relative_eq(other.as_ref(), epsilon, max_relative)
782    }
783}
784
785#[cfg(feature = "approx")]
786impl approx::UlpsEq for Dir3 {
787    fn default_max_ulps() -> u32 {
788        4
789    }
790    fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
791        self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
792    }
793}
794
795/// A normalized SIMD vector pointing in a direction in 3D space.
796///
797/// This type stores a 16 byte aligned [`Vec3A`].
798/// This may or may not be faster than [`Dir3`]: make sure to benchmark!
799#[derive(Clone, Copy, Debug, PartialEq)]
800#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
801#[cfg_attr(
802    feature = "bevy_reflect",
803    derive(Reflect),
804    reflect(Debug, PartialEq, Clone)
805)]
806#[cfg_attr(
807    all(feature = "serialize", feature = "bevy_reflect"),
808    reflect(Serialize, Deserialize)
809)]
810#[doc(alias = "Direction3dA")]
811pub struct Dir3A(Vec3A);
812impl Primitive3d for Dir3A {}
813
814impl Dir3A {
815    /// A unit vector pointing along the positive X axis.
816    pub const X: Self = Self(Vec3A::X);
817    /// A unit vector pointing along the positive Y axis.
818    pub const Y: Self = Self(Vec3A::Y);
819    /// A unit vector pointing along the positive Z axis.
820    pub const Z: Self = Self(Vec3A::Z);
821    /// A unit vector pointing along the negative X axis.
822    pub const NEG_X: Self = Self(Vec3A::NEG_X);
823    /// A unit vector pointing along the negative Y axis.
824    pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
825    /// A unit vector pointing along the negative Z axis.
826    pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
827    /// The directional axes.
828    pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
829
830    /// Create a direction from a finite, nonzero [`Vec3A`], normalizing it.
831    ///
832    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
833    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
834    pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
835        Self::new_and_length(value).map(|(dir, _)| dir)
836    }
837
838    /// Create a [`Dir3A`] from a [`Vec3A`] that is already normalized.
839    ///
840    /// # Warning
841    ///
842    /// `value` must be normalized, i.e its length must be `1.0`.
843    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    /// Create a direction from a finite, nonzero [`Vec3A`], normalizing it and
854    /// also returning its original length.
855    ///
856    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
857    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
858    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    /// Create a direction from its `x`, `y`, and `z` components.
868    ///
869    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
870    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
871    pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
872        Self::new(Vec3A::new(x, y, z))
873    }
874
875    /// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
876    ///
877    /// # Warning
878    ///
879    /// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
880    pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
881        Self::new_unchecked(Vec3A::new(x, y, z))
882    }
883
884    /// Returns the inner [`Vec3A`]
885    pub const fn as_vec3a(&self) -> Vec3A {
886        self.0
887    }
888
889    /// Performs a spherical linear interpolation between `self` and `rhs`
890    /// based on the value `s`.
891    ///
892    /// This corresponds to interpolating between the two directions at a constant angular velocity.
893    ///
894    /// When `s == 0.0`, the result will be equal to `self`.
895    /// When `s == 1.0`, the result will be equal to `rhs`.
896    ///
897    /// # Example
898    ///
899    /// ```
900    /// # use bevy_math::Dir3A;
901    /// # use approx::{assert_relative_eq, RelativeEq};
902    /// #
903    /// let dir1 = Dir3A::X;
904    /// let dir2 = Dir3A::Y;
905    ///
906    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
907    /// #[cfg(feature = "approx")]
908    /// assert_relative_eq!(
909    ///     result1,
910    ///     Dir3A::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
911    ///     epsilon = 0.000001
912    /// );
913    ///
914    /// let result2 = dir1.slerp(dir2, 0.5);
915    /// #[cfg(feature = "approx")]
916    /// assert_relative_eq!(result2, Dir3A::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
917    /// ```
918    #[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    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
928    /// Useful for preventing numerical error accumulation.
929    ///
930    /// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
931    #[inline]
932    pub fn fast_renormalize(self) -> Self {
933        let length_squared = self.0.length_squared();
934        // Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
935        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    /// Rotates the [`Dir3A`] using a [`Quat`].
997    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/// A normalized vector pointing in a direction in 4D space
1049#[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    /// A unit vector pointing along the positive X axis
1065    pub const X: Self = Self(Vec4::X);
1066    /// A unit vector pointing along the positive Y axis.
1067    pub const Y: Self = Self(Vec4::Y);
1068    /// A unit vector pointing along the positive Z axis.
1069    pub const Z: Self = Self(Vec4::Z);
1070    /// A unit vector pointing along the positive W axis.
1071    pub const W: Self = Self(Vec4::W);
1072    /// A unit vector pointing along the negative X axis.
1073    pub const NEG_X: Self = Self(Vec4::NEG_X);
1074    /// A unit vector pointing along the negative Y axis.
1075    pub const NEG_Y: Self = Self(Vec4::NEG_Y);
1076    /// A unit vector pointing along the negative Z axis.
1077    pub const NEG_Z: Self = Self(Vec4::NEG_Z);
1078    /// A unit vector pointing along the negative W axis.
1079    pub const NEG_W: Self = Self(Vec4::NEG_W);
1080    /// The directional axes.
1081    pub const AXES: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
1082
1083    /// Create a direction from a finite, nonzero [`Vec4`], normalizing it.
1084    ///
1085    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
1086    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
1087    pub fn new(value: Vec4) -> Result<Self, InvalidDirectionError> {
1088        Self::new_and_length(value).map(|(dir, _)| dir)
1089    }
1090
1091    /// Create a [`Dir4`] from a [`Vec4`] that is already normalized.
1092    ///
1093    /// # Warning
1094    ///
1095    /// `value` must be normalized, i.e its length must be `1.0`.
1096    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    /// Create a direction from a finite, nonzero [`Vec4`], normalizing it and
1106    /// also returning its original length.
1107    ///
1108    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
1109    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
1110    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    /// Create a direction from its `x`, `y`, `z`, and `w` components.
1120    ///
1121    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
1122    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
1123    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    /// Create a direction from its `x`, `y`, `z`, and `w` components, assuming the resulting vector is normalized.
1128    ///
1129    /// # Warning
1130    ///
1131    /// The vector produced from `x`, `y`, `z`, and `w` must be normalized, i.e its length must be `1.0`.
1132    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    /// Returns the inner [`Vec4`]
1137    pub const fn as_vec4(&self) -> Vec4 {
1138        self.0
1139    }
1140
1141    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
1142    /// Useful for preventing numerical error accumulation.
1143    #[inline]
1144    pub fn fast_renormalize(self) -> Self {
1145        // We numerically approximate the inverse square root by a Taylor series around 1
1146        // As we expect the error (x := length_squared - 1) to be small
1147        // inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
1148        // inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
1149
1150        // Iterative calls to this method quickly converge to a normalized value,
1151        // so long as the denormalization is not large ~ O(1/10).
1152        // One iteration can be described as:
1153        // l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
1154        // Rewriting in terms of the error x:
1155        // 1 + x <- (1 + x) * (1 - 1/2 x)²
1156        // 1 + x <- (1 + x) * (1 - x + 1/4 x²)
1157        // 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
1158        // x <- -1/4 x² (3 - x)
1159        // If the error is small, say in a range of (-1/2, 1/2), then:
1160        // |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
1161        // Therefore the sequence of iterates converges to 0 error as a second order method.
1162
1163        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        // Evil denormalized Rot2
1304        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        // We test that renormalizing an already normalized dir doesn't do anything
1310        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        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1319        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 dir3_creation() {
1328        assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
1329        assert_eq!(
1330            Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
1331            Err(InvalidDirectionError::Zero)
1332        );
1333        assert_eq!(
1334            Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
1335            Err(InvalidDirectionError::Infinite)
1336        );
1337        assert_eq!(
1338            Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
1339            Err(InvalidDirectionError::Infinite)
1340        );
1341        assert_eq!(
1342            Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
1343            Err(InvalidDirectionError::NaN)
1344        );
1345        assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
1346
1347        // Test rotation
1348        assert!(
1349            (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3::X)
1350                .abs_diff_eq(Vec3::Y, 10e-6)
1351        );
1352    }
1353
1354    #[test]
1355    fn dir3_slerp() {
1356        assert_relative_eq!(
1357            Dir3::X.slerp(Dir3::Y, 0.5),
1358            Dir3::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1359        );
1360        assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
1361        assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
1362        assert_relative_eq!(
1363            Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
1364            Dir3::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1365            epsilon = 0.000001
1366        );
1367        assert_relative_eq!(
1368            Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
1369            Dir3::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1370        );
1371    }
1372
1373    #[test]
1374    fn dir3_renorm() {
1375        // Evil denormalized quaternion
1376        let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1377        let mut dir_a = Dir3::X;
1378        let mut dir_b = Dir3::X;
1379
1380        // We test that renormalizing an already normalized dir doesn't do anything
1381        assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1382
1383        for _ in 0..50 {
1384            dir_a = rot3 * dir_a;
1385            dir_b = rot3 * dir_b;
1386            dir_b = dir_b.fast_renormalize();
1387        }
1388
1389        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1390        assert!(
1391            !dir_a.is_normalized(),
1392            "Denormalization doesn't work, test is faulty"
1393        );
1394        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1395    }
1396
1397    #[test]
1398    fn dir3a_creation() {
1399        assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
1400        assert_eq!(
1401            Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
1402            Err(InvalidDirectionError::Zero)
1403        );
1404        assert_eq!(
1405            Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
1406            Err(InvalidDirectionError::Infinite)
1407        );
1408        assert_eq!(
1409            Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
1410            Err(InvalidDirectionError::Infinite)
1411        );
1412        assert_eq!(
1413            Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
1414            Err(InvalidDirectionError::NaN)
1415        );
1416        assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
1417
1418        // Test rotation
1419        assert!(
1420            (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3A::X)
1421                .abs_diff_eq(Vec3A::Y, 10e-6)
1422        );
1423    }
1424
1425    #[test]
1426    fn dir3a_slerp() {
1427        assert_relative_eq!(
1428            Dir3A::X.slerp(Dir3A::Y, 0.5),
1429            Dir3A::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1430        );
1431        assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
1432        assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
1433        assert_relative_eq!(
1434            Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
1435            Dir3A::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1436            epsilon = 0.000001
1437        );
1438        assert_relative_eq!(
1439            Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
1440            Dir3A::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1441        );
1442    }
1443
1444    #[test]
1445    fn dir3a_renorm() {
1446        // Evil denormalized quaternion
1447        let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1448        let mut dir_a = Dir3A::X;
1449        let mut dir_b = Dir3A::X;
1450
1451        // We test that renormalizing an already normalized dir doesn't do anything
1452        assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1453
1454        for _ in 0..50 {
1455            dir_a = rot3 * dir_a;
1456            dir_b = rot3 * dir_b;
1457            dir_b = dir_b.fast_renormalize();
1458        }
1459
1460        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1461        assert!(
1462            !dir_a.is_normalized(),
1463            "Denormalization doesn't work, test is faulty"
1464        );
1465        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1466    }
1467
1468    #[test]
1469    fn dir4_creation() {
1470        assert_eq!(Dir4::new(Vec4::X * 12.5), Ok(Dir4::X));
1471        assert_eq!(
1472            Dir4::new(Vec4::new(0.0, 0.0, 0.0, 0.0)),
1473            Err(InvalidDirectionError::Zero)
1474        );
1475        assert_eq!(
1476            Dir4::new(Vec4::new(f32::INFINITY, 0.0, 0.0, 0.0)),
1477            Err(InvalidDirectionError::Infinite)
1478        );
1479        assert_eq!(
1480            Dir4::new(Vec4::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)),
1481            Err(InvalidDirectionError::Infinite)
1482        );
1483        assert_eq!(
1484            Dir4::new(Vec4::new(f32::NAN, 0.0, 0.0, 0.0)),
1485            Err(InvalidDirectionError::NaN)
1486        );
1487        assert_eq!(Dir4::new_and_length(Vec4::X * 6.5), Ok((Dir4::X, 6.5)));
1488    }
1489
1490    #[test]
1491    fn dir4_renorm() {
1492        // Evil denormalized matrix
1493        let mat4 = bevy_math::Mat4::from_quat(Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0))
1494            * (1.0 + 1e-5);
1495        let mut dir_a = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1496        let mut dir_b = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1497        // We test that renormalizing an already normalized dir doesn't do anything
1498        assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1499        for _ in 0..50 {
1500            dir_a = Dir4(mat4 * *dir_a);
1501            dir_b = Dir4(mat4 * *dir_b);
1502            dir_b = dir_b.fast_renormalize();
1503        }
1504        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1505        assert!(
1506            !dir_a.is_normalized(),
1507            "Denormalization doesn't work, test is faulty"
1508        );
1509        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1510    }
1511}