bevy_math/
direction.rs

1use crate::{
2    primitives::{Primitive2d, Primitive3d},
3    Quat, Rot2, Vec2, Vec3, Vec3A,
4};
5
6use core::f32::consts::FRAC_1_SQRT_2;
7use derive_more::derive::Into;
8
9#[cfg(feature = "bevy_reflect")]
10use bevy_reflect::Reflect;
11#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
12use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
13
14/// An error indicating that a direction is invalid.
15#[derive(Debug, PartialEq)]
16pub enum InvalidDirectionError {
17    /// The length of the direction vector is zero or very close to zero.
18    Zero,
19    /// The length of the direction vector is `std::f32::INFINITY`.
20    Infinite,
21    /// The length of the direction vector is `NaN`.
22    NaN,
23}
24
25impl InvalidDirectionError {
26    /// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector.
27    pub fn from_length(length: f32) -> Self {
28        if length.is_nan() {
29            InvalidDirectionError::NaN
30        } else if !length.is_finite() {
31            // If the direction is non-finite but also not NaN, it must be infinite
32            InvalidDirectionError::Infinite
33        } else {
34            // If the direction is invalid but neither NaN nor infinite, it must be zero
35            InvalidDirectionError::Zero
36        }
37    }
38}
39
40impl core::fmt::Display for InvalidDirectionError {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        write!(
43            f,
44            "Direction can not be zero (or very close to zero), or non-finite."
45        )
46    }
47}
48
49/// Checks that a vector with the given squared length is normalized.
50///
51/// Warns for small error with a length threshold of approximately `1e-4`,
52/// and panics for large error with a length threshold of approximately `1e-2`.
53///
54/// The format used for the logged warning is `"Warning: {warning} The length is {length}`,
55/// and similarly for the error.
56#[cfg(debug_assertions)]
57fn assert_is_normalized(message: &str, length_squared: f32) {
58    let length_error_squared = (length_squared - 1.0).abs();
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!("Error: {message} The length is {}.", length_squared.sqrt());
64    } else if length_error_squared > 2e-4 {
65        // Length error is approximately 1e-4 or more.
66        eprintln!(
67            "Warning: {message} The length is {}.",
68            length_squared.sqrt()
69        );
70    }
71}
72
73/// A normalized vector pointing in a direction in 2D space
74#[deprecated(
75    since = "0.14.0",
76    note = "`Direction2d` has been renamed. Please use `Dir2` instead."
77)]
78pub type Direction2d = Dir2;
79
80/// A normalized vector pointing in a direction in 3D space
81#[deprecated(
82    since = "0.14.0",
83    note = "`Direction3d` has been renamed. Please use `Dir3` instead."
84)]
85pub type Direction3d = Dir3;
86
87/// A normalized vector pointing in a direction in 2D space
88#[derive(Clone, Copy, Debug, PartialEq)]
89#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
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
111    /// The "north" direction, equivalent to [`Dir2::Y`].
112    pub const NORTH: Self = Self(Vec2::Y);
113    /// The "south" direction, equivalent to [`Dir2::NEG_Y`].
114    pub const SOUTH: Self = Self(Vec2::NEG_Y);
115    /// The "east" direction, equivalent to [`Dir2::X`].
116    pub const EAST: Self = Self(Vec2::X);
117    /// The "west" direction, equivalent to [`Dir2::NEG_X`].
118    pub const WEST: Self = Self(Vec2::NEG_X);
119    /// The "north-east" direction, between [`Dir2::NORTH`] and [`Dir2::EAST`].
120    pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
121    /// The "north-west" direction, between [`Dir2::NORTH`] and [`Dir2::WEST`].
122    pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
123    /// The "south-east" direction, between [`Dir2::SOUTH`] and [`Dir2::EAST`].
124    pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
125    /// The "south-west" direction, between [`Dir2::SOUTH`] and [`Dir2::WEST`].
126    pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
127
128    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it.
129    ///
130    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
131    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
132    pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
133        Self::new_and_length(value).map(|(dir, _)| dir)
134    }
135
136    /// Create a [`Dir2`] from a [`Vec2`] that is already normalized.
137    ///
138    /// # Warning
139    ///
140    /// `value` must be normalized, i.e its length must be `1.0`.
141    pub fn new_unchecked(value: Vec2) -> Self {
142        #[cfg(debug_assertions)]
143        assert_is_normalized(
144            "The vector given to `Dir2::new_unchecked` is not normalized.",
145            value.length_squared(),
146        );
147
148        Self(value)
149    }
150
151    /// Create a direction from a finite, nonzero [`Vec2`], normalizing it and
152    /// also returning its original length.
153    ///
154    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
155    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
156    pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
157        let length = value.length();
158        let direction = (length.is_finite() && length > 0.0).then_some(value / length);
159
160        direction
161            .map(|dir| (Self(dir), length))
162            .ok_or(InvalidDirectionError::from_length(length))
163    }
164
165    /// Create a direction from its `x` and `y` components.
166    ///
167    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
168    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
169    pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
170        Self::new(Vec2::new(x, y))
171    }
172
173    /// Create a direction from its `x` and `y` components, assuming the resulting vector is normalized.
174    ///
175    /// # Warning
176    ///
177    /// The vector produced from `x` and `y` must be normalized, i.e its length must be `1.0`.
178    pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
179        Self::new_unchecked(Vec2::new(x, y))
180    }
181
182    /// Returns the inner [`Vec2`]
183    pub const fn as_vec2(&self) -> Vec2 {
184        self.0
185    }
186
187    /// Performs a spherical linear interpolation between `self` and `rhs`
188    /// based on the value `s`.
189    ///
190    /// This corresponds to interpolating between the two directions at a constant angular velocity.
191    ///
192    /// When `s == 0.0`, the result will be equal to `self`.
193    /// When `s == 1.0`, the result will be equal to `rhs`.
194    ///
195    /// # Example
196    ///
197    /// ```
198    /// # use bevy_math::Dir2;
199    /// # use approx::{assert_relative_eq, RelativeEq};
200    /// #
201    /// let dir1 = Dir2::X;
202    /// let dir2 = Dir2::Y;
203    ///
204    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
205    /// assert_relative_eq!(result1, Dir2::from_xy(0.75_f32.sqrt(), 0.5).unwrap());
206    ///
207    /// let result2 = dir1.slerp(dir2, 0.5);
208    /// assert_relative_eq!(result2, Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap());
209    /// ```
210    #[inline]
211    pub fn slerp(self, rhs: Self, s: f32) -> Self {
212        let angle = self.angle_to(rhs.0);
213        Rot2::radians(angle * s) * self
214    }
215
216    /// Get the rotation that rotates this direction to `other`.
217    #[inline]
218    pub fn rotation_to(self, other: Self) -> Rot2 {
219        // Rotate `self` to X-axis, then X-axis to `other`:
220        other.rotation_from_x() * self.rotation_to_x()
221    }
222
223    /// Get the rotation that rotates `other` to this direction.
224    #[inline]
225    pub fn rotation_from(self, other: Self) -> Rot2 {
226        other.rotation_to(self)
227    }
228
229    /// Get the rotation that rotates the X-axis to this direction.
230    #[inline]
231    pub fn rotation_from_x(self) -> Rot2 {
232        Rot2::from_sin_cos(self.0.y, self.0.x)
233    }
234
235    /// Get the rotation that rotates this direction to the X-axis.
236    #[inline]
237    pub fn rotation_to_x(self) -> Rot2 {
238        // (This is cheap, it just negates one component.)
239        self.rotation_from_x().inverse()
240    }
241
242    /// Get the rotation that rotates the Y-axis to this direction.
243    #[inline]
244    pub fn rotation_from_y(self) -> Rot2 {
245        // `x <- y`, `y <- -x` correspond to rotating clockwise by pi/2;
246        // this transforms the Y-axis into the X-axis, maintaining the relative position
247        // of our direction. Then we just use the same technique as `rotation_from_x`.
248        Rot2::from_sin_cos(-self.0.x, self.0.y)
249    }
250
251    /// Get the rotation that rotates this direction to the Y-axis.
252    #[inline]
253    pub fn rotation_to_y(self) -> Rot2 {
254        self.rotation_from_y().inverse()
255    }
256
257    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
258    /// Useful for preventing numerical error accumulation.
259    /// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
260    #[inline]
261    pub fn fast_renormalize(self) -> Self {
262        let length_squared = self.0.length_squared();
263        // Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
264        Self(self * (0.5 * (3.0 - length_squared)))
265    }
266}
267
268impl TryFrom<Vec2> for Dir2 {
269    type Error = InvalidDirectionError;
270
271    fn try_from(value: Vec2) -> Result<Self, Self::Error> {
272        Self::new(value)
273    }
274}
275
276impl From<Dir2> for Vec2 {
277    fn from(value: Dir2) -> Self {
278        value.as_vec2()
279    }
280}
281
282impl core::ops::Deref for Dir2 {
283    type Target = Vec2;
284    fn deref(&self) -> &Self::Target {
285        &self.0
286    }
287}
288
289impl core::ops::Neg for Dir2 {
290    type Output = Self;
291    fn neg(self) -> Self::Output {
292        Self(-self.0)
293    }
294}
295
296impl core::ops::Mul<f32> for Dir2 {
297    type Output = Vec2;
298    fn mul(self, rhs: f32) -> Self::Output {
299        self.0 * rhs
300    }
301}
302
303impl core::ops::Mul<Dir2> for f32 {
304    type Output = Vec2;
305    fn mul(self, rhs: Dir2) -> Self::Output {
306        self * rhs.0
307    }
308}
309
310impl core::ops::Mul<Dir2> for Rot2 {
311    type Output = Dir2;
312
313    /// Rotates the [`Dir2`] using a [`Rot2`].
314    fn mul(self, direction: Dir2) -> Self::Output {
315        let rotated = self * *direction;
316
317        #[cfg(debug_assertions)]
318        assert_is_normalized(
319            "`Dir2` is denormalized after rotation.",
320            rotated.length_squared(),
321        );
322
323        Dir2(rotated)
324    }
325}
326
327#[cfg(any(feature = "approx", test))]
328impl approx::AbsDiffEq for Dir2 {
329    type Epsilon = f32;
330    fn default_epsilon() -> f32 {
331        f32::EPSILON
332    }
333    fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
334        self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
335    }
336}
337
338#[cfg(any(feature = "approx", test))]
339impl approx::RelativeEq for Dir2 {
340    fn default_max_relative() -> f32 {
341        f32::EPSILON
342    }
343    fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
344        self.as_ref()
345            .relative_eq(other.as_ref(), epsilon, max_relative)
346    }
347}
348
349#[cfg(any(feature = "approx", test))]
350impl approx::UlpsEq for Dir2 {
351    fn default_max_ulps() -> u32 {
352        4
353    }
354    fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
355        self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
356    }
357}
358
359/// A normalized vector pointing in a direction in 3D space
360#[derive(Clone, Copy, Debug, PartialEq, Into)]
361#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
362#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
363#[cfg_attr(
364    all(feature = "serialize", feature = "bevy_reflect"),
365    reflect(Serialize, Deserialize)
366)]
367#[doc(alias = "Direction3d")]
368pub struct Dir3(Vec3);
369impl Primitive3d for Dir3 {}
370
371impl Dir3 {
372    /// A unit vector pointing along the positive X axis.
373    pub const X: Self = Self(Vec3::X);
374    /// A unit vector pointing along the positive Y axis.
375    pub const Y: Self = Self(Vec3::Y);
376    /// A unit vector pointing along the positive Z axis.
377    pub const Z: Self = Self(Vec3::Z);
378    /// A unit vector pointing along the negative X axis.
379    pub const NEG_X: Self = Self(Vec3::NEG_X);
380    /// A unit vector pointing along the negative Y axis.
381    pub const NEG_Y: Self = Self(Vec3::NEG_Y);
382    /// A unit vector pointing along the negative Z axis.
383    pub const NEG_Z: Self = Self(Vec3::NEG_Z);
384    /// The directional axes.
385    pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
386
387    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
388    ///
389    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
390    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
391    pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
392        Self::new_and_length(value).map(|(dir, _)| dir)
393    }
394
395    /// Create a [`Dir3`] from a [`Vec3`] that is already normalized.
396    ///
397    /// # Warning
398    ///
399    /// `value` must be normalized, i.e its length must be `1.0`.
400    pub fn new_unchecked(value: Vec3) -> Self {
401        #[cfg(debug_assertions)]
402        assert_is_normalized(
403            "The vector given to `Dir3::new_unchecked` is not normalized.",
404            value.length_squared(),
405        );
406
407        Self(value)
408    }
409
410    /// Create a direction from a finite, nonzero [`Vec3`], normalizing it and
411    /// also returning its original length.
412    ///
413    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
414    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
415    pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
416        let length = value.length();
417        let direction = (length.is_finite() && length > 0.0).then_some(value / length);
418
419        direction
420            .map(|dir| (Self(dir), length))
421            .ok_or(InvalidDirectionError::from_length(length))
422    }
423
424    /// Create a direction from its `x`, `y`, and `z` components.
425    ///
426    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
427    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
428    pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
429        Self::new(Vec3::new(x, y, z))
430    }
431
432    /// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
433    ///
434    /// # Warning
435    ///
436    /// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
437    pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
438        Self::new_unchecked(Vec3::new(x, y, z))
439    }
440
441    /// Returns the inner [`Vec3`]
442    pub const fn as_vec3(&self) -> Vec3 {
443        self.0
444    }
445
446    /// Performs a spherical linear interpolation between `self` and `rhs`
447    /// based on the value `s`.
448    ///
449    /// This corresponds to interpolating between the two directions at a constant angular velocity.
450    ///
451    /// When `s == 0.0`, the result will be equal to `self`.
452    /// When `s == 1.0`, the result will be equal to `rhs`.
453    ///
454    /// # Example
455    ///
456    /// ```
457    /// # use bevy_math::Dir3;
458    /// # use approx::{assert_relative_eq, RelativeEq};
459    /// #
460    /// let dir1 = Dir3::X;
461    /// let dir2 = Dir3::Y;
462    ///
463    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
464    /// assert_relative_eq!(
465    ///     result1,
466    ///     Dir3::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
467    ///     epsilon = 0.000001
468    /// );
469    ///
470    /// let result2 = dir1.slerp(dir2, 0.5);
471    /// assert_relative_eq!(result2, Dir3::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
472    /// ```
473    #[inline]
474    pub fn slerp(self, rhs: Self, s: f32) -> Self {
475        let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
476        Dir3(quat.mul_vec3(self.0))
477    }
478
479    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
480    /// Useful for preventing numerical error accumulation.
481    ///
482    /// # Example
483    /// The following seemingly benign code would start accumulating errors over time,
484    /// leading to `dir` eventually not being normalized anymore.
485    /// ```
486    /// # use bevy_math::prelude::*;
487    /// # let N: usize = 200;
488    /// let mut dir = Dir3::X;
489    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
490    /// for i in 0..N {
491    ///     dir = quaternion * dir;
492    /// }
493    /// ```
494    /// Instead, do the following.
495    /// ```
496    /// # use bevy_math::prelude::*;
497    /// # let N: usize = 200;
498    /// let mut dir = Dir3::X;
499    /// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
500    /// for i in 0..N {
501    ///     dir = quaternion * dir;
502    ///     dir = dir.fast_renormalize();
503    /// }
504    /// ```
505    #[inline]
506    pub fn fast_renormalize(self) -> Self {
507        // We numerically approximate the inverse square root by a Taylor series around 1
508        // As we expect the error (x := length_squared - 1) to be small
509        // inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
510        // inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
511
512        // Iterative calls to this method quickly converge to a normalized value,
513        // so long as the denormalization is not large ~ O(1/10).
514        // One iteration can be described as:
515        // l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
516        // Rewriting in terms of the error x:
517        // 1 + x <- (1 + x) * (1 - 1/2 x)²
518        // 1 + x <- (1 + x) * (1 - x + 1/4 x²)
519        // 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
520        // x <- -1/4 x² (3 - x)
521        // If the error is small, say in a range of (-1/2, 1/2), then:
522        // |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
523        // Therefore the sequence of iterates converges to 0 error as a second order method.
524
525        let length_squared = self.0.length_squared();
526        Self(self * (0.5 * (3.0 - length_squared)))
527    }
528}
529
530impl TryFrom<Vec3> for Dir3 {
531    type Error = InvalidDirectionError;
532
533    fn try_from(value: Vec3) -> Result<Self, Self::Error> {
534        Self::new(value)
535    }
536}
537
538impl core::ops::Deref for Dir3 {
539    type Target = Vec3;
540    fn deref(&self) -> &Self::Target {
541        &self.0
542    }
543}
544
545impl core::ops::Neg for Dir3 {
546    type Output = Self;
547    fn neg(self) -> Self::Output {
548        Self(-self.0)
549    }
550}
551
552impl core::ops::Mul<f32> for Dir3 {
553    type Output = Vec3;
554    fn mul(self, rhs: f32) -> Self::Output {
555        self.0 * rhs
556    }
557}
558
559impl core::ops::Mul<Dir3> for f32 {
560    type Output = Vec3;
561    fn mul(self, rhs: Dir3) -> Self::Output {
562        self * rhs.0
563    }
564}
565
566impl core::ops::Mul<Dir3> for Quat {
567    type Output = Dir3;
568
569    /// Rotates the [`Dir3`] using a [`Quat`].
570    fn mul(self, direction: Dir3) -> Self::Output {
571        let rotated = self * *direction;
572
573        #[cfg(debug_assertions)]
574        assert_is_normalized(
575            "`Dir3` is denormalized after rotation.",
576            rotated.length_squared(),
577        );
578
579        Dir3(rotated)
580    }
581}
582
583#[cfg(feature = "approx")]
584impl approx::AbsDiffEq for Dir3 {
585    type Epsilon = f32;
586    fn default_epsilon() -> f32 {
587        f32::EPSILON
588    }
589    fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
590        self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
591    }
592}
593
594#[cfg(feature = "approx")]
595impl approx::RelativeEq for Dir3 {
596    fn default_max_relative() -> f32 {
597        f32::EPSILON
598    }
599    fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
600        self.as_ref()
601            .relative_eq(other.as_ref(), epsilon, max_relative)
602    }
603}
604
605#[cfg(feature = "approx")]
606impl approx::UlpsEq for Dir3 {
607    fn default_max_ulps() -> u32 {
608        4
609    }
610    fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
611        self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
612    }
613}
614
615/// A normalized SIMD vector pointing in a direction in 3D space.
616///
617/// This type stores a 16 byte aligned [`Vec3A`].
618/// This may or may not be faster than [`Dir3`]: make sure to benchmark!
619#[derive(Clone, Copy, Debug, PartialEq)]
620#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
621#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
622#[cfg_attr(
623    all(feature = "serialize", feature = "bevy_reflect"),
624    reflect(Serialize, Deserialize)
625)]
626#[doc(alias = "Direction3dA")]
627pub struct Dir3A(Vec3A);
628impl Primitive3d for Dir3A {}
629
630impl Dir3A {
631    /// A unit vector pointing along the positive X axis.
632    pub const X: Self = Self(Vec3A::X);
633    /// A unit vector pointing along the positive Y axis.
634    pub const Y: Self = Self(Vec3A::Y);
635    /// A unit vector pointing along the positive Z axis.
636    pub const Z: Self = Self(Vec3A::Z);
637    /// A unit vector pointing along the negative X axis.
638    pub const NEG_X: Self = Self(Vec3A::NEG_X);
639    /// A unit vector pointing along the negative Y axis.
640    pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
641    /// A unit vector pointing along the negative Z axis.
642    pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
643    /// The directional axes.
644    pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
645
646    /// Create a direction from a finite, nonzero [`Vec3A`], normalizing it.
647    ///
648    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
649    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
650    pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
651        Self::new_and_length(value).map(|(dir, _)| dir)
652    }
653
654    /// Create a [`Dir3A`] from a [`Vec3A`] that is already normalized.
655    ///
656    /// # Warning
657    ///
658    /// `value` must be normalized, i.e its length must be `1.0`.
659    pub fn new_unchecked(value: Vec3A) -> Self {
660        #[cfg(debug_assertions)]
661        assert_is_normalized(
662            "The vector given to `Dir3A::new_unchecked` is not normalized.",
663            value.length_squared(),
664        );
665
666        Self(value)
667    }
668
669    /// Create a direction from a finite, nonzero [`Vec3A`], normalizing it and
670    /// also returning its original length.
671    ///
672    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
673    /// of the given vector is zero (or very close to zero), infinite, or `NaN`.
674    pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> {
675        let length = value.length();
676        let direction = (length.is_finite() && length > 0.0).then_some(value / length);
677
678        direction
679            .map(|dir| (Self(dir), length))
680            .ok_or(InvalidDirectionError::from_length(length))
681    }
682
683    /// Create a direction from its `x`, `y`, and `z` components.
684    ///
685    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
686    /// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
687    pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
688        Self::new(Vec3A::new(x, y, z))
689    }
690
691    /// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
692    ///
693    /// # Warning
694    ///
695    /// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
696    pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
697        Self::new_unchecked(Vec3A::new(x, y, z))
698    }
699
700    /// Returns the inner [`Vec3A`]
701    pub const fn as_vec3a(&self) -> Vec3A {
702        self.0
703    }
704
705    /// Performs a spherical linear interpolation between `self` and `rhs`
706    /// based on the value `s`.
707    ///
708    /// This corresponds to interpolating between the two directions at a constant angular velocity.
709    ///
710    /// When `s == 0.0`, the result will be equal to `self`.
711    /// When `s == 1.0`, the result will be equal to `rhs`.
712    ///
713    /// # Example
714    ///
715    /// ```
716    /// # use bevy_math::Dir3A;
717    /// # use approx::{assert_relative_eq, RelativeEq};
718    /// #
719    /// let dir1 = Dir3A::X;
720    /// let dir2 = Dir3A::Y;
721    ///
722    /// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
723    /// assert_relative_eq!(
724    ///     result1,
725    ///     Dir3A::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
726    ///     epsilon = 0.000001
727    /// );
728    ///
729    /// let result2 = dir1.slerp(dir2, 0.5);
730    /// assert_relative_eq!(result2, Dir3A::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
731    /// ```
732    #[inline]
733    pub fn slerp(self, rhs: Self, s: f32) -> Self {
734        let quat = Quat::IDENTITY.slerp(
735            Quat::from_rotation_arc(Vec3::from(self.0), Vec3::from(rhs.0)),
736            s,
737        );
738        Dir3A(quat.mul_vec3a(self.0))
739    }
740
741    /// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
742    /// Useful for preventing numerical error accumulation.
743    ///
744    /// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
745    #[inline]
746    pub fn fast_renormalize(self) -> Self {
747        let length_squared = self.0.length_squared();
748        // Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
749        Self(self * (0.5 * (3.0 - length_squared)))
750    }
751}
752
753impl From<Dir3> for Dir3A {
754    fn from(value: Dir3) -> Self {
755        Self(value.0.into())
756    }
757}
758
759impl From<Dir3A> for Dir3 {
760    fn from(value: Dir3A) -> Self {
761        Self(value.0.into())
762    }
763}
764
765impl TryFrom<Vec3A> for Dir3A {
766    type Error = InvalidDirectionError;
767
768    fn try_from(value: Vec3A) -> Result<Self, Self::Error> {
769        Self::new(value)
770    }
771}
772
773impl From<Dir3A> for Vec3A {
774    fn from(value: Dir3A) -> Self {
775        value.0
776    }
777}
778
779impl core::ops::Deref for Dir3A {
780    type Target = Vec3A;
781    fn deref(&self) -> &Self::Target {
782        &self.0
783    }
784}
785
786impl core::ops::Neg for Dir3A {
787    type Output = Self;
788    fn neg(self) -> Self::Output {
789        Self(-self.0)
790    }
791}
792
793impl core::ops::Mul<f32> for Dir3A {
794    type Output = Vec3A;
795    fn mul(self, rhs: f32) -> Self::Output {
796        self.0 * rhs
797    }
798}
799
800impl core::ops::Mul<Dir3A> for f32 {
801    type Output = Vec3A;
802    fn mul(self, rhs: Dir3A) -> Self::Output {
803        self * rhs.0
804    }
805}
806
807impl core::ops::Mul<Dir3A> for Quat {
808    type Output = Dir3A;
809
810    /// Rotates the [`Dir3A`] using a [`Quat`].
811    fn mul(self, direction: Dir3A) -> Self::Output {
812        let rotated = self * *direction;
813
814        #[cfg(debug_assertions)]
815        assert_is_normalized(
816            "`Dir3A` is denormalized after rotation.",
817            rotated.length_squared(),
818        );
819
820        Dir3A(rotated)
821    }
822}
823
824#[cfg(feature = "approx")]
825impl approx::AbsDiffEq for Dir3A {
826    type Epsilon = f32;
827    fn default_epsilon() -> f32 {
828        f32::EPSILON
829    }
830    fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
831        self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
832    }
833}
834
835#[cfg(feature = "approx")]
836impl approx::RelativeEq for Dir3A {
837    fn default_max_relative() -> f32 {
838        f32::EPSILON
839    }
840    fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
841        self.as_ref()
842            .relative_eq(other.as_ref(), epsilon, max_relative)
843    }
844}
845
846#[cfg(feature = "approx")]
847impl approx::UlpsEq for Dir3A {
848    fn default_max_ulps() -> u32 {
849        4
850    }
851    fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
852        self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
853    }
854}
855
856#[cfg(test)]
857mod tests {
858    use crate::ops;
859
860    use super::*;
861    use approx::assert_relative_eq;
862
863    #[test]
864    fn dir2_creation() {
865        assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X));
866        assert_eq!(
867            Dir2::new(Vec2::new(0.0, 0.0)),
868            Err(InvalidDirectionError::Zero)
869        );
870        assert_eq!(
871            Dir2::new(Vec2::new(f32::INFINITY, 0.0)),
872            Err(InvalidDirectionError::Infinite)
873        );
874        assert_eq!(
875            Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)),
876            Err(InvalidDirectionError::Infinite)
877        );
878        assert_eq!(
879            Dir2::new(Vec2::new(f32::NAN, 0.0)),
880            Err(InvalidDirectionError::NaN)
881        );
882        assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5)));
883    }
884
885    #[test]
886    fn dir2_slerp() {
887        assert_relative_eq!(
888            Dir2::X.slerp(Dir2::Y, 0.5),
889            Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap()
890        );
891        assert_eq!(Dir2::Y.slerp(Dir2::X, 0.0), Dir2::Y);
892        assert_relative_eq!(Dir2::X.slerp(Dir2::Y, 1.0), Dir2::Y);
893        assert_relative_eq!(
894            Dir2::Y.slerp(Dir2::X, 1.0 / 3.0),
895            Dir2::from_xy(0.5, 0.75_f32.sqrt()).unwrap()
896        );
897        assert_relative_eq!(
898            Dir2::X.slerp(Dir2::Y, 2.0 / 3.0),
899            Dir2::from_xy(0.5, 0.75_f32.sqrt()).unwrap()
900        );
901    }
902
903    #[test]
904    fn dir2_to_rotation2d() {
905        // assert_relative_eq!(Dir2::EAST.rotation_to(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
906        // assert_relative_eq!(Dir2::NORTH.rotation_from(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
907        // assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rot2::FRAC_PI_2);
908        // assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rot2::PI);
909        // assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_x(), Rot2::degrees(135.0));
910        // assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rot2::FRAC_PI_4);
911    }
912
913    #[test]
914    fn dir2_renorm() {
915        // Evil denormalized Rot2
916        let (sin, cos) = ops::sin_cos(1.0_f32);
917        let rot2 = Rot2::from_sin_cos(sin * (1.0 + 1e-5), cos * (1.0 + 1e-5));
918        let mut dir_a = Dir2::X;
919        let mut dir_b = Dir2::X;
920
921        // We test that renormalizing an already normalized dir doesn't do anything
922        assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
923
924        for _ in 0..50 {
925            dir_a = rot2 * dir_a;
926            dir_b = rot2 * dir_b;
927            dir_b = dir_b.fast_renormalize();
928        }
929
930        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
931        assert!(
932            !dir_a.is_normalized(),
933            "Dernormalization doesn't work, test is faulty"
934        );
935        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
936    }
937
938    #[test]
939    fn dir3_creation() {
940        assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
941        assert_eq!(
942            Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
943            Err(InvalidDirectionError::Zero)
944        );
945        assert_eq!(
946            Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
947            Err(InvalidDirectionError::Infinite)
948        );
949        assert_eq!(
950            Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
951            Err(InvalidDirectionError::Infinite)
952        );
953        assert_eq!(
954            Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
955            Err(InvalidDirectionError::NaN)
956        );
957        assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
958
959        // Test rotation
960        assert!(
961            (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3::X)
962                .abs_diff_eq(Vec3::Y, 10e-6)
963        );
964    }
965
966    #[test]
967    fn dir3_slerp() {
968        // assert_relative_eq!(
969        //     Dir3::X.slerp(Dir3::Y, 0.5),
970        //     Dir3::from_xyz(0.5f32.sqrt(), 0.5f32.sqrt(), 0.0).unwrap()
971        // );
972        // assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
973        // assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
974        // assert_relative_eq!(
975        //     Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
976        //     Dir3::from_xyz(0.75f32.sqrt(), 0.0, 0.5).unwrap(),
977        //     epsilon = 0.000001
978        // );
979        // assert_relative_eq!(
980        //     Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
981        //     Dir3::from_xyz(0.0, 0.75f32.sqrt(), 0.5).unwrap()
982        // );
983    }
984
985    #[test]
986    fn dir3_renorm() {
987        // Evil denormalized quaternion
988        let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
989        let mut dir_a = Dir3::X;
990        let mut dir_b = Dir3::X;
991
992        // We test that renormalizing an already normalized dir doesn't do anything
993        // assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
994
995        for _ in 0..50 {
996            dir_a = rot3 * dir_a;
997            dir_b = rot3 * dir_b;
998            dir_b = dir_b.fast_renormalize();
999        }
1000
1001        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1002        assert!(
1003            !dir_a.is_normalized(),
1004            "Dernormalization doesn't work, test is faulty"
1005        );
1006        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1007    }
1008
1009    #[test]
1010    fn dir3a_creation() {
1011        assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
1012        assert_eq!(
1013            Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
1014            Err(InvalidDirectionError::Zero)
1015        );
1016        assert_eq!(
1017            Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
1018            Err(InvalidDirectionError::Infinite)
1019        );
1020        assert_eq!(
1021            Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
1022            Err(InvalidDirectionError::Infinite)
1023        );
1024        assert_eq!(
1025            Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
1026            Err(InvalidDirectionError::NaN)
1027        );
1028        assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
1029
1030        // Test rotation
1031        assert!(
1032            (Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3A::X)
1033                .abs_diff_eq(Vec3A::Y, 10e-6)
1034        );
1035    }
1036
1037    #[test]
1038    fn dir3a_slerp() {
1039        // assert_relative_eq!(
1040        //     Dir3A::X.slerp(Dir3A::Y, 0.5),
1041        //     Dir3A::from_xyz(0.5f32.sqrt(), 0.5f32.sqrt(), 0.0).unwrap()
1042        // );
1043        // assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
1044        // assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
1045        // assert_relative_eq!(
1046        //     Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
1047        //     Dir3A::from_xyz(0.75f32.sqrt(), 0.0, 0.5).unwrap(),
1048        //     epsilon = 0.000001
1049        // );
1050        // assert_relative_eq!(
1051        //     Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
1052        //     Dir3A::from_xyz(0.0, 0.75f32.sqrt(), 0.5).unwrap()
1053        // );
1054    }
1055
1056    #[test]
1057    fn dir3a_renorm() {
1058        // Evil denormalized quaternion
1059        let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1060        let mut dir_a = Dir3A::X;
1061        let mut dir_b = Dir3A::X;
1062
1063        // We test that renormalizing an already normalized dir doesn't do anything
1064        // assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1065
1066        for _ in 0..50 {
1067            dir_a = rot3 * dir_a;
1068            dir_b = rot3 * dir_b;
1069            dir_b = dir_b.fast_renormalize();
1070        }
1071
1072        // `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1073        assert!(
1074            !dir_a.is_normalized(),
1075            "Dernormalization doesn't work, test is faulty"
1076        );
1077        assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1078    }
1079}