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