Skip to main content

rapier2d/utils/
mod.rs

1//! Miscellaneous utilities.
2
3mod angular_inertia_ops;
4mod component_mul;
5mod copysign;
6mod cross_product;
7mod cross_product_matrix;
8mod dot_product;
9mod fp_flags;
10mod index_mut2;
11mod matrix_column;
12mod orthonormal_basis;
13mod pos_ops;
14mod rotation_ops;
15mod scalar_type;
16mod simd_real_copy;
17mod simd_select;
18
19pub use component_mul::ComponentMul;
20pub use copysign::CopySign;
21pub use index_mut2::IndexMut2;
22pub use matrix_column::MatrixColumn;
23pub use orthonormal_basis::OrthonormalBasis;
24pub use pos_ops::PoseOps;
25pub use rotation_ops::RotationOps;
26pub use scalar_type::ScalarType;
27pub use simd_real_copy::SimdRealCopy;
28pub use simd_select::SimdSelect;
29
30pub use angular_inertia_ops::AngularInertiaOps;
31pub use cross_product::CrossProduct;
32pub use cross_product_matrix::CrossProductMatrix;
33pub use dot_product::{DotProduct, SimdLength};
34pub(crate) use fp_flags::{DisableFloatingPointExceptionsFlags, FlushToZeroDenormalsAreZeroFlags};
35
36#[cfg(feature = "simd-is-enabled")]
37use crate::math::SIMD_WIDTH;
38use crate::math::{Real, SimdVector, Vector};
39#[cfg(feature = "dim2")]
40use na::Matrix2;
41#[cfg(feature = "dim3")]
42use na::Matrix3;
43
44/// Dimension minus one (1 for 2D, 2 for 3D).
45#[cfg(feature = "dim2")]
46pub const DIM_MINUS_ONE: usize = 1;
47/// Dimension minus one (1 for 2D, 2 for 3D).
48#[cfg(feature = "dim3")]
49pub const DIM_MINUS_ONE: usize = 2;
50
51/// Try to normalize a vector and return both the normalized vector and the original length.
52///
53/// Returns `None` if the vector's length is below the threshold.
54/// This is the glam equivalent of nalgebra's `Unit::try_new_and_get`.
55pub fn try_normalize_and_get_length(v: Vector, threshold: Real) -> Option<(Vector, Real)> {
56    let len = v.length();
57    if len > threshold {
58        Some((v / len, len))
59    } else {
60        None
61    }
62}
63
64/// Convert glam Vector to nalgebra `SimdVector<Real>`
65#[inline]
66pub fn vect_to_na(v: Vector) -> SimdVector<Real> {
67    v.into()
68}
69
70use crate::math::Matrix;
71
72/// Convert glam Matrix to nalgebra `Matrix2<Real>` (2D matrix)
73#[cfg(feature = "dim2")]
74#[inline]
75pub fn mat_to_na(m: Matrix) -> Matrix2<Real> {
76    m.into()
77}
78
79/// Convert glam Matrix to nalgebra `Matrix3<Real>` (3D matrix)
80#[cfg(feature = "dim3")]
81#[inline]
82pub fn mat_to_na(m: Matrix) -> Matrix3<Real> {
83    Matrix3::new(
84        m.x_axis.x, m.y_axis.x, m.z_axis.x, m.x_axis.y, m.y_axis.y, m.z_axis.y, m.x_axis.z,
85        m.y_axis.z, m.z_axis.z,
86    )
87}
88
89const INV_EPSILON: Real = 1.0e-20;
90
91pub(crate) fn inv(val: Real) -> Real {
92    if (-INV_EPSILON..=INV_EPSILON).contains(&val) {
93        0.0
94    } else {
95        1.0 / val
96    }
97}
98
99pub(crate) fn simd_inv<N: SimdRealCopy>(val: N) -> N {
100    let eps = N::splat(INV_EPSILON);
101    N::zero().select(val.simd_gt(-eps) & val.simd_lt(eps), N::one() / val)
102}
103
104pub(crate) fn select_other<T: PartialEq>(pair: (T, T), elt: T) -> T {
105    if pair.0 == elt { pair.1 } else { pair.0 }
106}
107
108/// Calculate the difference with smallest absolute value between the two given values.
109pub fn smallest_abs_diff_between_sin_angles<N: SimdRealCopy>(a: N, b: N) -> N {
110    // Select the smallest path among the two angles to reach the target.
111    let s_err = a - b;
112    let sgn = s_err.simd_signum();
113    let s_err_complement = s_err - sgn * N::splat(2.0);
114    let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs());
115    s_err.select(s_err_is_smallest, s_err_complement)
116}
117
118/// Calculate the difference with smallest absolute value between the two given angles.
119pub fn smallest_abs_diff_between_angles<N: SimdRealCopy>(a: N, b: N) -> N {
120    // Select the smallest path among the two angles to reach the target.
121    let s_err = a - b;
122    let sgn = s_err.simd_signum();
123    let s_err_complement = s_err - sgn * N::simd_two_pi();
124    let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs());
125    s_err.select(s_err_is_smallest, s_err_complement)
126}
127
128#[cfg(feature = "simd-nightly")]
129#[inline(always)]
130pub(crate) fn transmute_to_wide(val: [std::simd::f32x4; SIMD_WIDTH]) -> [wide::f32x4; SIMD_WIDTH] {
131    unsafe { std::mem::transmute(val) }
132}
133
134#[cfg(feature = "simd-stable")]
135#[inline(always)]
136pub(crate) fn transmute_to_wide(val: [wide::f32x4; SIMD_WIDTH]) -> [wide::f32x4; SIMD_WIDTH] {
137    val
138}
139
140/// Helpers around serialization.
141#[cfg(feature = "serde-serialize")]
142pub mod serde {
143    use serde::{Deserialize, Serialize};
144    use std::iter::FromIterator;
145
146    /// Serializes to a `Vec<(K, V)>`.
147    ///
148    /// Useful for [`std::collections::HashMap`] with a non-string key,
149    /// which is unsupported by [`serde_json`](https://docs.rs/serde_json/).
150    pub fn serialize_to_vec_tuple<
151        'a,
152        S: serde::Serializer,
153        T: IntoIterator<Item = (&'a K, &'a V)>,
154        K: Serialize + 'a,
155        V: Serialize + 'a,
156    >(
157        target: T,
158        s: S,
159    ) -> Result<S::Ok, S::Error> {
160        let container: Vec<_> = target.into_iter().collect();
161        serde::Serialize::serialize(&container, s)
162    }
163
164    /// Deserializes from a `Vec<(K, V)>`.
165    ///
166    /// Useful for [`std::collections::HashMap`] with a non-string key,
167    /// which is unsupported by [`serde_json`](https://docs.rs/serde_json/).
168    pub fn deserialize_from_vec_tuple<
169        'de,
170        D: serde::Deserializer<'de>,
171        T: FromIterator<(K, V)>,
172        K: Deserialize<'de>,
173        V: Deserialize<'de>,
174    >(
175        d: D,
176    ) -> Result<T, D::Error> {
177        let hashmap_as_vec: Vec<(K, V)> = Deserialize::deserialize(d)?;
178        Ok(T::from_iter(hashmap_as_vec))
179    }
180
181    #[cfg(test)]
182    mod test {
183        use std::collections::HashMap;
184
185        /// This test uses serde_json because json doesn't support non string
186        /// keys in hashmaps, which requires a custom serialization.
187        #[test]
188        fn serde_json_hashmap() {
189            #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
190            struct Test {
191                #[cfg_attr(
192                    feature = "serde-serialize",
193                    serde(
194                        serialize_with = "crate::utils::serde::serialize_to_vec_tuple",
195                        deserialize_with = "crate::utils::serde::deserialize_from_vec_tuple"
196                    )
197                )]
198                pub map: HashMap<usize, String>,
199            }
200
201            let s = Test {
202                map: [(42, "Forty-Two".to_string())].into(),
203            };
204            let j = serde_json::to_string(&s).unwrap();
205            assert_eq!(&j, "{\"map\":[[42,\"Forty-Two\"]]}");
206            let p: Test = serde_json::from_str(&j).unwrap();
207            assert_eq!(&p, &s);
208        }
209    }
210}