1#[cfg(not(target_arch = "spirv"))]
4mod angular_inertia_ops;
5mod component_mul;
6mod copysign;
7mod cross_product;
8mod cross_product_matrix;
9mod dot_product;
10mod fp_flags;
11mod index_mut2;
12mod matrix_column;
13mod orthonormal_basis;
14#[cfg(not(target_arch = "spirv"))]
15mod pos_ops;
16#[cfg(all(feature = "alloc", not(target_arch = "spirv")))]
17mod prefetch;
18#[cfg(not(target_arch = "spirv"))]
19mod rotation_ops;
20#[cfg(not(target_arch = "spirv"))]
21mod scalar_type;
22mod simd_real_copy;
23mod simd_select;
24
25pub use component_mul::ComponentMul;
26pub use copysign::CopySign;
27pub use index_mut2::IndexMut2;
28pub use matrix_column::MatrixColumn;
29pub use orthonormal_basis::OrthonormalBasis;
30#[cfg(not(target_arch = "spirv"))]
31pub use pos_ops::PoseOps;
32#[cfg(all(feature = "alloc", not(target_arch = "spirv")))]
33pub(crate) use prefetch::prefetch_read;
34#[cfg(not(target_arch = "spirv"))]
35pub use rotation_ops::RotationOps;
36#[cfg(not(target_arch = "spirv"))]
37pub use scalar_type::ScalarType;
38pub use simd_real_copy::SimdRealCopy;
39pub use simd_select::SimdSelect;
40
41#[cfg(not(target_arch = "spirv"))]
42pub use angular_inertia_ops::AngularInertiaOps;
43pub use cross_product::CrossProduct;
44pub use cross_product_matrix::CrossProductMatrix;
45pub use dot_product::{DotProduct, SimdLength};
46#[allow(unused_imports)]
47pub(crate) use fp_flags::DisableFloatingPointExceptionsFlags;
48
49#[cfg(feature = "alloc")]
50use crate::math::SIMD_WIDTH;
51#[cfg(not(target_arch = "spirv"))]
52use crate::math::SimdVector;
53use crate::math::{Real, Vector};
54#[cfg(all(feature = "dim2", not(target_arch = "spirv")))]
55use na::Matrix2;
56#[cfg(all(feature = "dim3", not(target_arch = "spirv")))]
57use na::Matrix3;
58
59#[cfg(feature = "dim2")]
61pub const DIM_MINUS_ONE: usize = 1;
62#[cfg(feature = "dim3")]
64pub const DIM_MINUS_ONE: usize = 2;
65
66pub fn try_normalize_and_get_length(v: Vector, threshold: Real) -> Option<(Vector, Real)> {
71 let len = v.length();
72 if len > threshold {
73 Some((v / len, len))
74 } else {
75 None
76 }
77}
78
79#[allow(dead_code)]
89#[inline(always)]
90pub(crate) fn canonicalize_zero<T>(x: T) -> T
91where
92 T: core::ops::Add<Output = T> + Default,
93{
94 #[cfg(feature = "enhanced-determinism")]
95 {
96 x + T::default()
97 }
98 #[cfg(not(feature = "enhanced-determinism"))]
99 {
100 x
101 }
102}
103
104#[cfg(not(target_arch = "spirv"))]
106#[inline]
107pub fn vect_to_na(v: Vector) -> SimdVector<Real> {
108 v.into()
109}
110
111#[cfg(not(target_arch = "spirv"))]
112use crate::math::Matrix;
113
114#[cfg(all(feature = "dim2", not(target_arch = "spirv")))]
116#[inline]
117pub fn mat_to_na(m: Matrix) -> Matrix2<Real> {
118 m.into()
119}
120
121#[cfg(all(feature = "dim3", not(target_arch = "spirv")))]
123#[inline]
124pub fn mat_to_na(m: Matrix) -> Matrix3<Real> {
125 Matrix3::new(
126 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,
127 m.y_axis.z, m.z_axis.z,
128 )
129}
130
131const INV_EPSILON: Real = 1.0e-20;
132
133#[allow(dead_code)]
134pub(crate) fn inv(val: Real) -> Real {
135 if (-INV_EPSILON..=INV_EPSILON).contains(&val) {
136 0.0
137 } else {
138 1.0 / val
139 }
140}
141
142#[allow(dead_code)]
143pub(crate) fn simd_inv<N: SimdRealCopy>(val: N) -> N {
144 let eps = N::splat(INV_EPSILON);
145 N::zero().select(val.simd_gt(-eps) & val.simd_lt(eps), N::one() / val)
146}
147
148#[allow(dead_code)]
149pub(crate) fn select_other<T: PartialEq>(pair: (T, T), elt: T) -> T {
150 if pair.0 == elt { pair.1 } else { pair.0 }
151}
152
153#[cfg(not(feature = "unsync-callbacks"))]
155pub trait MaybeSync: Sync {}
156#[cfg(not(feature = "unsync-callbacks"))]
157impl<T: Sync + ?Sized> MaybeSync for T {}
158
159#[cfg(feature = "unsync-callbacks")]
161pub trait MaybeSync {}
162#[cfg(feature = "unsync-callbacks")]
163impl<T: ?Sized> MaybeSync for T {}
164
165#[cfg(feature = "alloc")]
172pub(crate) fn hashmap_remove<K, V>(
173 map: &mut parry::utils::hashmap::HashMap<K, V>,
174 key: &K,
175) -> Option<V>
176where
177 K: core::hash::Hash + Eq,
178{
179 #[cfg(feature = "enhanced-determinism")]
180 return map.swap_remove(key);
181 #[cfg(not(feature = "enhanced-determinism"))]
182 return map.remove(key);
183}
184
185#[cfg(feature = "parallel")]
190#[derive(Copy, Clone)]
191pub(crate) struct SyncPtr<T>(pub *mut T);
192#[cfg(feature = "parallel")]
193unsafe impl<T: Send> Send for SyncPtr<T> {}
194#[cfg(feature = "parallel")]
195unsafe impl<T: Send> Sync for SyncPtr<T> {}
196
197#[cfg(feature = "parallel")]
198impl<T> SyncPtr<T> {
199 pub(crate) fn add(&self, i: usize) -> *mut T {
202 unsafe { self.0.add(i) }
203 }
204}
205
206pub fn smallest_abs_diff_between_sin_angles<N: SimdRealCopy>(a: N, b: N) -> N {
208 let s_err = a - b;
210 let sgn = s_err.simd_signum();
211 let s_err_complement = s_err - sgn * N::splat(2.0);
212 let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs());
213 s_err.select(s_err_is_smallest, s_err_complement)
214}
215
216pub fn smallest_abs_diff_between_angles<N: SimdRealCopy>(a: N, b: N) -> N {
218 let s_err = a - b;
220 let sgn = s_err.simd_signum();
221 let s_err_complement = s_err - sgn * N::simd_two_pi();
222 let s_err_is_smallest = s_err.simd_abs().simd_lt(s_err_complement.simd_abs());
223 s_err.select(s_err_is_smallest, s_err_complement)
224}
225
226#[cfg(all(feature = "alloc", feature = "f32"))]
235pub(crate) type SolverBlock = simba::simd::WideF32x4;
236#[cfg(all(feature = "alloc", feature = "f64"))]
240pub(crate) type SolverBlock = simba::simd::AutoF64x4;
241
242#[cfg(all(feature = "alloc", feature = "f32"))]
245pub(crate) type RawBlock = wide::f32x4;
246#[cfg(all(feature = "alloc", feature = "f64"))]
248pub(crate) type RawBlock = [Real; 4];
249
250#[cfg(all(feature = "alloc", feature = "f32"))]
253#[inline(always)]
254fn transpose4(data: [RawBlock; 4]) -> [RawBlock; 4] {
255 wide::f32x4::transpose(data)
256}
257
258#[cfg(all(feature = "alloc", feature = "f64"))]
260#[inline(always)]
261fn transpose4(data: [RawBlock; 4]) -> [RawBlock; 4] {
262 let [
263 [a0, a1, a2, a3],
264 [b0, b1, b2, b3],
265 [c0, c1, c2, c3],
266 [d0, d1, d2, d3],
267 ] = data;
268 [
269 [a0, b0, c0, d0],
270 [a1, b1, c1, d1],
271 [a2, b2, c2, d2],
272 [a3, b3, c3, d3],
273 ]
274}
275
276#[cfg(feature = "alloc")]
282#[inline(always)]
283pub(crate) fn transpose_wide(aos: [RawBlock; SIMD_WIDTH]) -> [crate::math::SimdReal; 4] {
284 #[cfg(not(feature = "simd8"))]
285 {
286 unsafe { core::mem::transmute(transpose4(aos)) }
287 }
288 #[cfg(feature = "simd8")]
289 {
290 let lo = transpose4([aos[0], aos[1], aos[2], aos[3]]);
291 let hi = transpose4([aos[4], aos[5], aos[6], aos[7]]);
292 core::array::from_fn(|j| unsafe {
294 core::mem::transmute::<[RawBlock; 2], crate::math::SimdReal>([lo[j], hi[j]])
295 })
296 }
297}
298
299#[cfg(feature = "alloc")]
302#[inline(always)]
303pub(crate) fn transpose_wide_inv(soa: [crate::math::SimdReal; 4]) -> [RawBlock; SIMD_WIDTH] {
304 #[cfg(not(feature = "simd8"))]
305 {
306 transpose4(unsafe {
307 core::mem::transmute::<[crate::math::SimdReal; 4], [RawBlock; 4]>(soa)
308 })
309 }
310 #[cfg(feature = "simd8")]
311 {
312 let split: [[RawBlock; 2]; 4] = unsafe { core::mem::transmute(soa) };
314 let lo: [RawBlock; 4] = core::array::from_fn(|j| split[j][0]);
315 let hi: [RawBlock; 4] = core::array::from_fn(|j| split[j][1]);
316 let aos_lo = transpose4(lo); let aos_hi = transpose4(hi); core::array::from_fn(|i| if i < 4 { aos_lo[i] } else { aos_hi[i - 4] })
319 }
320}
321
322#[cfg(feature = "serde-serialize")]
324pub mod serde {
325 use crate::alloc_prelude::*;
326 use core::iter::FromIterator;
327 use serde::{Deserialize, Serialize};
328
329 pub fn serialize_to_vec_tuple<
334 'a,
335 S: serde::Serializer,
336 T: IntoIterator<Item = (&'a K, &'a V)>,
337 K: Serialize + 'a,
338 V: Serialize + 'a,
339 >(
340 target: T,
341 s: S,
342 ) -> Result<S::Ok, S::Error> {
343 let container: Vec<_> = target.into_iter().collect();
344 serde::Serialize::serialize(&container, s)
345 }
346
347 pub fn serialize_sorted_to_vec_tuple<
350 'a,
351 S: serde::Serializer,
352 T: IntoIterator<Item = (&'a K, &'a V)>,
353 K: Serialize + 'a,
354 V: Serialize + 'a,
355 O: Ord,
356 >(
357 target: T,
358 key: impl Fn(&K) -> O,
359 s: S,
360 ) -> Result<S::Ok, S::Error> {
361 let mut container: Vec<_> = target.into_iter().collect();
362 container.sort_unstable_by_key(|(a, _)| key(a));
363 serde::Serialize::serialize(&container, s)
364 }
365
366 pub fn deserialize_from_vec_tuple<
371 'de,
372 D: serde::Deserializer<'de>,
373 T: FromIterator<(K, V)>,
374 K: Deserialize<'de>,
375 V: Deserialize<'de>,
376 >(
377 d: D,
378 ) -> Result<T, D::Error> {
379 let hashmap_as_vec: Vec<(K, V)> = Deserialize::deserialize(d)?;
380 Ok(T::from_iter(hashmap_as_vec))
381 }
382
383 #[cfg(test)]
384 mod test {
385 use crate::alloc_prelude::*;
386 use std::collections::HashMap;
387
388 #[test]
391 fn serde_json_hashmap() {
392 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
393 struct Test {
394 #[cfg_attr(
395 feature = "serde-serialize",
396 serde(
397 serialize_with = "crate::utils::serde::serialize_to_vec_tuple",
398 deserialize_with = "crate::utils::serde::deserialize_from_vec_tuple"
399 )
400 )]
401 pub map: HashMap<usize, String>,
402 }
403
404 let s = Test {
405 map: [(42, "Forty-Two".to_string())].into(),
406 };
407 let j = serde_json::to_string(&s).unwrap();
408 assert_eq!(&j, "{\"map\":[[42,\"Forty-Two\"]]}");
409 let p: Test = serde_json::from_str(&j).unwrap();
410 assert_eq!(&p, &s);
411 }
412 }
413}