avian3d/math/
mod.rs

1//! Math types and traits used by the crate.
2//!
3//! Most of the math types are feature-dependent, so they will be different for `2d`/`3d` and `f32`/`f64`.
4
5#[cfg(feature = "f32")]
6mod single;
7#[cfg(feature = "f32")]
8pub use single::*;
9
10#[cfg(feature = "f64")]
11mod double;
12#[cfg(feature = "f64")]
13pub use double::*;
14
15use bevy_math::{prelude::*, *};
16
17/// The active dimension.
18#[cfg(feature = "2d")]
19pub const DIM: usize = 2;
20/// The active dimension.
21#[cfg(feature = "3d")]
22pub const DIM: usize = 3;
23
24/// The `f32` vector type chosen based on the dimension.
25#[cfg(feature = "2d")]
26pub(crate) use bevy_math::Vec2 as VectorF32;
27
28/// The `f32` vector type chosen based on the dimension.
29#[cfg(feature = "3d")]
30pub(crate) use bevy_math::Vec3 as VectorF32;
31
32/// The ray type chosen based on the dimension.
33#[cfg(feature = "2d")]
34pub(crate) type Ray = Ray2d;
35/// The ray type chosen based on the dimension.
36#[cfg(feature = "3d")]
37pub(crate) type Ray = Ray3d;
38
39// Note: This is called `Dir` instead of `Direction` because Bevy has a conflicting `Direction` type.
40/// The direction type chosen based on the dimension.
41#[cfg(feature = "2d")]
42pub(crate) type Dir = Dir2;
43/// The direction type chosen based on the dimension.
44#[cfg(feature = "3d")]
45pub(crate) type Dir = Dir3;
46
47/// Adjust the precision of the math construct to the precision chosen for compilation.
48pub trait AdjustPrecision {
49    /// A math construct type with the desired precision.
50    type Adjusted;
51    /// Adjusts the precision of [`self`] to [`Self::Adjusted`](#associatedtype.Adjusted).
52    fn adjust_precision(&self) -> Self::Adjusted;
53}
54
55/// Adjust the precision down to `f32` regardless of compilation.
56pub trait AsF32 {
57    /// The `f32` version of a math construct.
58    type F32;
59    /// Returns the `f32` version of this type.
60    fn f32(&self) -> Self::F32;
61}
62
63impl AsF32 for DVec3 {
64    type F32 = Vec3;
65    fn f32(&self) -> Self::F32 {
66        self.as_vec3()
67    }
68}
69
70impl AsF32 for Vec3 {
71    type F32 = Self;
72    fn f32(&self) -> Self::F32 {
73        *self
74    }
75}
76
77impl AsF32 for DVec2 {
78    type F32 = Vec2;
79    fn f32(&self) -> Self::F32 {
80        self.as_vec2()
81    }
82}
83
84impl AsF32 for Vec2 {
85    type F32 = Self;
86    fn f32(&self) -> Self::F32 {
87        *self
88    }
89}
90
91impl AsF32 for Vec4 {
92    type F32 = Self;
93    fn f32(&self) -> Self::F32 {
94        *self
95    }
96}
97
98impl AsF32 for DQuat {
99    type F32 = Quat;
100    fn f32(&self) -> Self::F32 {
101        self.as_quat()
102    }
103}
104
105impl AsF32 for Quat {
106    type F32 = Self;
107    fn f32(&self) -> Self::F32 {
108        *self
109    }
110}
111
112impl AsF32 for DMat2 {
113    type F32 = Mat2;
114    fn f32(&self) -> Self::F32 {
115        self.as_mat2()
116    }
117}
118
119impl AsF32 for Mat2 {
120    type F32 = Self;
121    fn f32(&self) -> Self::F32 {
122        *self
123    }
124}
125
126impl AsF32 for DMat3 {
127    type F32 = Mat3;
128    fn f32(&self) -> Self::F32 {
129        self.as_mat3()
130    }
131}
132
133impl AsF32 for Mat3 {
134    type F32 = Self;
135    fn f32(&self) -> Self::F32 {
136        *self
137    }
138}
139
140#[cfg(feature = "2d")]
141pub(crate) fn cross(a: Vector, b: Vector) -> Scalar {
142    a.perp_dot(b)
143}
144
145#[cfg(feature = "3d")]
146pub(crate) fn cross(a: Vector, b: Vector) -> Vector {
147    a.cross(b)
148}
149
150/// An extension trait for computing reciprocals without division by zero.
151pub trait RecipOrZero {
152    /// Computes the reciprocal of `self` if `self` is not zero,
153    /// and returns zero otherwise to avoid division by zero.
154    fn recip_or_zero(self) -> Self;
155}
156
157impl RecipOrZero for f32 {
158    #[inline]
159    fn recip_or_zero(self) -> Self {
160        if self != 0.0 && self.is_finite() {
161            self.recip()
162        } else {
163            0.0
164        }
165    }
166}
167
168impl RecipOrZero for f64 {
169    #[inline]
170    fn recip_or_zero(self) -> Self {
171        if self != 0.0 && self.is_finite() {
172            self.recip()
173        } else {
174            0.0
175        }
176    }
177}
178
179impl RecipOrZero for Vec2 {
180    #[inline]
181    fn recip_or_zero(self) -> Self {
182        Self::new(self.x.recip_or_zero(), self.y.recip_or_zero())
183    }
184}
185
186impl RecipOrZero for Vec3 {
187    #[inline]
188    fn recip_or_zero(self) -> Self {
189        Self::new(
190            self.x.recip_or_zero(),
191            self.y.recip_or_zero(),
192            self.z.recip_or_zero(),
193        )
194    }
195}
196
197impl RecipOrZero for DVec2 {
198    #[inline]
199    fn recip_or_zero(self) -> Self {
200        Self::new(self.x.recip_or_zero(), self.y.recip_or_zero())
201    }
202}
203
204impl RecipOrZero for DVec3 {
205    #[inline]
206    fn recip_or_zero(self) -> Self {
207        Self::new(
208            self.x.recip_or_zero(),
209            self.y.recip_or_zero(),
210            self.z.recip_or_zero(),
211        )
212    }
213}
214
215/// An extension trait for matrix types.
216pub trait MatExt {
217    /// Computes the inverse of `self` if `self` is not zero,
218    /// and returns zero otherwise to avoid division by zero.
219    fn inverse_or_zero(self) -> Self;
220}
221
222impl MatExt for Mat2 {
223    #[inline]
224    fn inverse_or_zero(self) -> Self {
225        if self.determinant() == 0.0 {
226            Self::ZERO
227        } else {
228            self.inverse()
229        }
230    }
231}
232
233impl MatExt for DMat2 {
234    #[inline]
235    fn inverse_or_zero(self) -> Self {
236        if self.determinant() == 0.0 {
237            Self::ZERO
238        } else {
239            self.inverse()
240        }
241    }
242}
243
244impl MatExt for Mat3 {
245    #[inline]
246    fn inverse_or_zero(self) -> Self {
247        if self.determinant() == 0.0 {
248            Self::ZERO
249        } else {
250            self.inverse()
251        }
252    }
253}
254
255impl MatExt for DMat3 {
256    #[inline]
257    fn inverse_or_zero(self) -> Self {
258        if self.determinant() == 0.0 {
259            Self::ZERO
260        } else {
261            self.inverse()
262        }
263    }
264}
265
266#[expect(clippy::unnecessary_cast)]
267#[cfg(all(feature = "2d", any(feature = "parry-f32", feature = "parry-f64")))]
268pub(crate) fn na_iso_to_iso(isometry: &parry::math::Isometry<Scalar>) -> Isometry2d {
269    Isometry2d::new(
270        Vector::from(isometry.translation).f32(),
271        Rot2::from_sin_cos(isometry.rotation.im as f32, isometry.rotation.re as f32),
272    )
273}
274
275#[cfg(all(
276    feature = "default-collider",
277    any(feature = "parry-f32", feature = "parry-f64")
278))]
279use crate::prelude::*;
280#[cfg(all(
281    feature = "default-collider",
282    any(feature = "parry-f32", feature = "parry-f64")
283))]
284use parry::math::Isometry;
285
286#[cfg(all(
287    feature = "2d",
288    feature = "default-collider",
289    any(feature = "parry-f32", feature = "parry-f64")
290))]
291pub(crate) fn make_isometry(
292    position: impl Into<Position>,
293    rotation: impl Into<Rotation>,
294) -> Isometry<Scalar> {
295    let position: Position = position.into();
296    let rotation: Rotation = rotation.into();
297    Isometry::<Scalar>::new(position.0.into(), rotation.into())
298}
299
300#[cfg(all(
301    feature = "3d",
302    feature = "default-collider",
303    any(feature = "parry-f32", feature = "parry-f64")
304))]
305pub(crate) fn make_isometry(
306    position: impl Into<Position>,
307    rotation: impl Into<Rotation>,
308) -> Isometry<Scalar> {
309    let position: Position = position.into();
310    let rotation: Rotation = rotation.into();
311    Isometry::<Scalar>::new(position.0.into(), rotation.to_scaled_axis().into())
312}
313
314/// Computes the skew-symmetric matrix corresponding to the given vector.
315///
316/// ```text
317///                          [   0  -v.z  v.y ]
318/// skew_symmetric_mat3(v) = [  v.z   0  -v.x ]
319///                          [ -v.y  v.x   0  ]
320/// ```
321#[inline]
322#[must_use]
323#[cfg(feature = "3d")]
324pub fn skew_symmetric_mat3(v: Vector3) -> Matrix3 {
325    Matrix3::from_cols_array(&[0.0, v.z, -v.y, -v.z, 0.0, v.x, v.y, -v.x, 0.0])
326}