Skip to main content

bevy_math/
direction.rs

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/// An error indicating that a direction is invalid.
19#[derive(Debug, PartialEq, Error)]
20pub enum InvalidDirectionError {
21    /// The length of the direction vector is zero or very close to zero.
22    #[error("The length of the direction vector is zero or very close to zero")]
23    Zero,
24    /// The length of the direction vector is `std::f32::INFINITY`.
25    #[error("The length of the direction vector is `std::f32::INFINITY`")]
26    Infinite,
27    /// The length of the direction vector is `NaN`.
28    #[error("The length of the direction vector is `NaN`")]
29    NaN,
30}
31
32impl InvalidDirectionError {
33    /// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector.
34    pub const fn from_length(length: f32) -> Self {
35        if length.is_nan() {
36            InvalidDirectionError::NaN
37        } else if !length.is_finite() {
38            // If the direction is non-finite but also not NaN, it must be infinite
39            InvalidDirectionError::Infinite
40        } else {
41            // If the direction is invalid but neither NaN nor infinite, it must be zero
42            InvalidDirectionError::Zero
43        }
44    }
45}
46
47/// Checks that a vector with the given squared length is normalized.
48///
49/// Warns for small error with a length threshold of approximately `1e-4`,
50/// and panics for large error with a length threshold of approximately `1e-2`.
51///
52/// The format used for the logged warning is `"Warning: {warning} The length is {length}`,
53/// and similarly for the error.
54#[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    // Panic for large error and warn for slight error.
61    if length_error_squared > 2e-2 || length_error_squared.is_nan() {
62        // Length error is approximately 1e-2 or more.
63        panic!(
64            "Error: {message} The length is {}.",
65            ops::sqrt(length_squared)
66        );
67    } else if length_error_squared > 2e-4 {
68        // Length error is approximately 1e-4 or more.
69        #[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/// A normalized vector pointing in a direction in 2D space
81#[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    /// A unit vector pointing along the positive X axis.
97    pub const X: Self = Self(Vec2::X);
98    /// A unit vector pointing along the positive Y axis.
99    pub const Y: Self = Self(Vec2::Y);
100    /// A unit vector pointing along the negative X axis.
101    pub const NEG_X: Self = Self(Vec2::NEG_X);
102    /// A unit vector pointing along the negative Y axis.
103    pub const NEG_Y: Self = Self(Vec2::NEG_Y);
104    /// The directional axes.
105    pub const AXES: [Self; 2] = [Self::X, Self::Y];
106    /// The cardinal directions.
107    pub const CARDINALS: [Self; 4] = [Self::X, Self::NEG_X, Self::Y, Self::NEG_Y];
108
109    /// The "north" direction, equivalent to [`Dir2::Y`].
110    pub const NORTH: Self = Self(Vec2::Y);
111    /// The "south" direction, equivalent to [`Dir2::NEG_Y`].
112    pub const SOUTH: Self = Self(Vec2::NEG_Y);
113    /// The "east" direction, equivalent to [`Dir2::X`].
114    pub const EAST: Self = Self(Vec2::X);
115    /// The "west" direction, equivalent to [`Dir2::NEG_X`].
116    pub const WEST: Self = Self(Vec2::NEG_X);
117    /// The "north-east" direction, between [`Dir2::NORTH`] and [`Dir2::EAST`].
118    pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
119    /// The "north-west" direction, between [`Dir2::NORTH`] and [`Dir2::WEST`].
120    pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
121    /// The "south-east" direction, between [`Dir2::SOUTH`] and [`Dir2::EAST`].
122    pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
123    /// The "south-west" direction, between [`Dir2::SOUTH`] and [`Dir2::WEST`].
124    pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
125
126    /// The diagonals between the cardinal directions.
127    pub const DIAGONALS: [Self; 4] = [
128        Self::NORTH_EAST,
129        Self::NORTH_WEST,
130        Self::SOUTH_EAST,
131        Self::SOUTH_WEST,
132    ];
133    /// All neighbors of a tile on a square grid in a 3x3 neighborhood. A combination of [`Self::CARDINALS`] and [`Self::DIAGONALS`]
134    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    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it.
146    ///
147    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
148    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
149    pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
150        Self::new_and_length(value).map(|(dir, _)| dir)
151    }
152
153    /// Create a [`Dir2`] from a [`Vec2`] that is already normalized.
154    ///
155    /// # Warning
156    ///
157    /// `value` must be normalized, i.e its length must be `1.0`.
158    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    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it and
169    /// also returning its original length.
170    ///
171    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
172    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
173    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    /// Create a direction from its `x` and `y` components.
183    ///
184    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
185    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
186    pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
187        Self::new(Vec2::new(x, y))
188    }
189
190    /// Create a direction from its `x` and `y` components, assuming the resulting vector is normalized.
191    ///
192    /// # Warning
193    ///
194    /// The vector produced from `x` and `y` must be normalized, i.e its length must be `1.0`.
195    pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
196        Self::new_unchecked(Vec2::new(x, y))
197    }
198
199    /// Creates a 2D direction containing `[angle.cos(), angle.sin()]`.
200    #[inline]
201    pub fn from_angle(angle: f32) -> Self {
202        Self(Vec2::from_angle(angle))
203    }
204
205    /// Returns the inner [`Vec2`]
206    pub const fn as_vec2(&self) -> Vec2 {
207        self.0
208    }
209
210    /// Performs a spherical linear interpolation between `self` and `rhs`
211    /// based on the value `s`.
212    ///
213    /// This corresponds to interpolating between the two directions at a constant angular velocity.
214    ///
215    /// When `s == 0.0`, the result will be equal to `self`.
216    /// When `s == 1.0`, the result will be equal to `rhs`.
217    ///
218    /// # Example
219    ///
220    /// ```
221    /// # use bevy_math::Dir2;
222    /// # use approx::{assert_relative_eq, RelativeEq};
223    /// #
224    /// let dir1 = Dir2::X;
225    /// let dir2 = Dir2::Y;
226    ///
227    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
228    /// #[cfg(feature = "approx")]
229    /// assert_relative_eq!(result1, Dir2::from_xy(0.75_f32.sqrt(), 0.5).unwrap());
230    ///
231    /// let result2 = dir1.slerp(dir2, 0.5);
232    /// #[cfg(feature = "approx")]
233    /// assert_relative_eq!(result2, Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap());
234    /// ```
235    #[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    /// Get the rotation that rotates this direction to `other`.
242    #[inline]
243    pub fn rotation_to(self, other: Self) -> Rot2 {
244        // Rotate `self` to X-axis, then X-axis to `other`:
245        other.rotation_from_x() * self.rotation_to_x()
246    }
247
248    /// Get the rotation that rotates `other` to this direction.
249    #[inline]
250    pub fn rotation_from(self, other: Self) -> Rot2 {
251        other.rotation_to(self)
252    }
253
254    /// Get the rotation that rotates the X-axis to this direction.
255    #[inline]
256    pub fn rotation_from_x(self) -> Rot2 {
257        Rot2::from_sin_cos(self.0.y, self.0.x)
258    }
259
260    /// Get the rotation that rotates this direction to the X-axis.
261    #[inline]
262    pub fn rotation_to_x(self) -> Rot2 {
263        // (This is cheap, it just negates one component.)
264        self.rotation_from_x().inverse()
265    }
266
267    /// Get the rotation that rotates the Y-axis to this direction.
268    #[inline]
269    pub fn rotation_from_y(self) -> Rot2 {
270        // `x <- y`, `y <- -x` correspond to rotating clockwise by pi/2;
271        // this transforms the Y-axis into the X-axis, maintaining the relative position
272        // of our direction. Then we just use the same technique as `rotation_from_x`.
273        Rot2::from_sin_cos(-self.0.x, self.0.y)
274    }
275
276    /// Get the rotation that rotates this direction to the Y-axis.
277    #[inline]
278    pub fn rotation_to_y(self) -> Rot2 {
279        self.rotation_from_y().inverse()
280    }
281
282    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
283    /// Useful for preventing numerical error accumulation.
284    /// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
285    #[inline]
286    pub fn fast_renormalize(self) -> Self {
287        let length_squared = self.0.length_squared();
288        // Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
289        Self(self * (0.5 * (3.0 - length_squared)))
290    }
291
292    /// Returns the perpendicular vector rotated to 90 degrees counterclockwise.
293    #[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    /// Rotates the [`Dir2`] using a [`Rot2`].
345    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/// A normalized vector pointing in a direction in 3D space
397#[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    /// A unit vector pointing along the positive X axis.
413    pub const X: Self = Self(Vec3::X);
414    /// A unit vector pointing along the positive Y axis.
415    pub const Y: Self = Self(Vec3::Y);
416    /// A unit vector pointing along the positive Z axis.
417    pub const Z: Self = Self(Vec3::Z);
418    /// A unit vector pointing along the negative X axis.
419    pub const NEG_X: Self = Self(Vec3::NEG_X);
420    /// A unit vector pointing along the negative Y axis.
421    pub const NEG_Y: Self = Self(Vec3::NEG_Y);
422    /// A unit vector pointing along the negative Z axis.
423    pub const NEG_Z: Self = Self(Vec3::NEG_Z);
424    /// The directional axes.
425    pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
426    /// The cardinal directions.
427    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    // Adding this allow here to make sure that the precision in FRAC_1_SQRT_2
437    // and here is the same
438    /// Approximation of 1/sqrt(3) needed for the diagonals in 3D space
439    const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32;
440    /// The directions pointing towards the vertices of a cube centered at the origin.
441    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    /// The directions towards centers of each edge of a cube
484    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    /// All neighbors of a tile on a cube grid a 3x3x3 neighborhood. A combination of [`Self::CARDINALS`], [`Self::ALL_EDGES`] and [`Self::ALL_VERTICES`]
499    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    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
561    ///
562    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
563    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
564    pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
565        Self::new_and_length(value).map(|(dir, _)| dir)
566    }
567
568    /// Create a [`Dir3`] from a [`Vec3`] that is already normalized.
569    ///
570    /// # Warning
571    ///
572    /// `value` must be normalized, i.e its length must be `1.0`.
573    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    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it and
584    /// also returning its original length.
585    ///
586    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
587    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
588    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    /// Create a direction from its `x`, `y`, and `z` components.
598    ///
599    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
600    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
601    pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
602        Self::new(Vec3::new(x, y, z))
603    }
604
605    /// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
606    ///
607    /// # Warning
608    ///
609    /// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
610    pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
611        Self::new_unchecked(Vec3::new(x, y, z))
612    }
613
614    /// Returns the inner [`Vec3`]
615    pub const fn as_vec3(&self) -> Vec3 {
616        self.0
617    }
618
619    /// Performs a spherical linear interpolation between `self` and `rhs`
620    /// based on the value `s`.
621    ///
622    /// This corresponds to interpolating between the two directions at a constant angular velocity.
623    ///
624    /// When `s == 0.0`, the result will be equal to `self`.
625    /// When `s == 1.0`, the result will be equal to `rhs`.
626    ///
627    /// # Example
628    ///
629    /// ```
630    /// # use bevy_math::Dir3;
631    /// # use approx::{assert_relative_eq, RelativeEq};
632    /// #
633    /// let dir1 = Dir3::X;
634    /// let dir2 = Dir3::Y;
635    ///
636    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
637    /// #[cfg(feature = "approx")]
638    /// assert_relative_eq!(
639    ///     result1,
640    ///     Dir3::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
641    ///     epsilon = 0.000001
642    /// );
643    ///
644    /// let result2 = dir1.slerp(dir2, 0.5);
645    /// #[cfg(feature = "approx")]
646    /// assert_relative_eq!(result2, Dir3::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
647    /// ```
648    #[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    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
655    /// Useful for preventing numerical error accumulation.
656    ///
657    /// # Example
658    /// The following seemingly benign code would start accumulating errors over time,
659    /// leading to `dir` eventually not being normalized anymore.
660    /// ```
661    /// # use bevy_math::prelude::*;
662    /// # let N: usize = 200;
663    /// let mut dir = Dir3::X;
664    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
665    /// for i in 0..N {
666    ///     dir = quaternion * dir;
667    /// }
668    /// ```
669    /// Instead, do the following.
670    /// ```
671    /// # use bevy_math::prelude::*;
672    /// # let N: usize = 200;
673    /// let mut dir = Dir3::X;
674    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
675    /// for i in 0..N {
676    ///     dir = quaternion * dir;
677    ///     dir = dir.fast_renormalize();
678    /// }
679    /// ```
680    #[inline]
681    pub fn fast_renormalize(self) -> Self {
682        // We numerically approximate the inverse square root by a Taylor series around 1
683        // As we expect the error (x := length_squared - 1) to be small
684        // inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
685        // inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
686
687        // Iterative calls to this method quickly converge to a normalized value,
688        // so long as the denormalization is not large ~ O(1/10).
689        // One iteration can be described as:
690        // l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
691        // Rewriting in terms of the error x:
692        // 1 + x <- (1 + x) * (1 - 1/2 x)²
693        // 1 + x <- (1 + x) * (1 - x + 1/4 x²)
694        // 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
695        // x <- -1/4 x² (3 - x)
696        // If the error is small, say in a range of (-1/2, 1/2), then:
697        // |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
698        // Therefore the sequence of iterates converges to 0 error as a second order method.
699
700        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    /// Rotates the [`Dir3`] using a [`Quat`].
745    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/// A normalized SIMD vector pointing in a direction in 3D space.
797///
798/// This type stores a 16 byte aligned [`Vec3A`].
799/// This may or may not be faster than [`Dir3`]: make sure to benchmark!
800#[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    /// 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 dir2_perp() {
1328        // (1, 0) rotated 90 deg counterclockwise becomes (0, 1)
1329        assert_eq!(Dir2::X.perpendicular(), Dir2::Y);
1330
1331        // (0, 1) rotated 90 deg counterclockwise becomes (-1, 0)
1332        assert_eq!(Dir2::Y.perpendicular(), Dir2::NEG_X);
1333
1334        // (-1, 0) rotated 90 deg counterclockwise becomes (0, -1)
1335        assert_eq!(Dir2::NEG_X.perpendicular(), Dir2::NEG_Y);
1336
1337        // (0, -1) rotated 90 deg counterclockwise becomes (1, 0)
1338        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        // Test rotation
1363        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        // Evil denormalized quaternion
1391        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        // We test that renormalizing an already normalized dir doesn't do anything
1396        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        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1405        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        // Test rotation
1434        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        // Evil denormalized quaternion
1462        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        // We test that renormalizing an already normalized dir doesn't do anything
1467        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        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1476        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        // Evil denormalized matrix
1508        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        // We test that renormalizing an already normalized dir doesn't do anything
1513        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        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1520        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}