collision2d/lib.rs
1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4//! A simple 2D collision detection library supporting simple primitive shapes only,
5//! and that is suitable for development on `no_std` targets.
6//!
7//! # Features
8//!
9//! * `std`: *(enabled by default)* enable use of the standard library. Must be disabled for `no_std` crates.
10//! * `aabb`: *(enabled by default)* Axis-Aligned Bounding Box (`Aabb`) shape
11//! * `libm`: Use `libm` for math. This is notably necessary for some features to work on `no_std`
12
13#[cfg(feature = "aabb")]
14mod aabb;
15
16use core::borrow::Borrow;
17
18#[cfg(feature = "aabb")]
19pub use aabb::Aabb;
20
21/// Trait implemented by shapes that can be checked for collisions with shapes of type `S`
22pub trait Collides<S = Self> {
23 /// Returns true if `self` overlaps `other`
24 #[must_use]
25 fn collides(&self, other: impl Borrow<S>) -> bool;
26
27 /// Returns true if `self` collides with any `others`
28 ///
29 /// If the `others` iterator is empty, this function returns `false`
30 #[must_use]
31 fn collides_any<'a, T>(&self, others: impl IntoIterator<Item = T>) -> bool
32 where
33 T: Borrow<S> + 'a,
34 {
35 others.into_iter().any(|other| self.collides(other))
36 }
37}
38
39/// Trait implemented by shapes that can check for penetration into other shapes of type `S`
40pub trait Penetration<S = Self> {
41 /// Returns by how much `self` should be moved in order to resolve penetration with `other`
42 ///
43 /// Returns `None` if the two shapes are not collided
44 #[must_use]
45 fn penetration(&self, other: impl Borrow<S>) -> Option<[f32; 2]>;
46
47 /// Returns the maximum penetration of `self` against the `others` shapes.
48 ///
49 /// Returns `None` if `self` does not penetrate any of the `others` shape.
50 #[must_use]
51 fn max_penetration<'a, T>(&self, others: impl IntoIterator<Item = T>) -> Option<[f32; 2]>
52 where
53 T: Borrow<S> + 'a,
54 {
55 let mut max_magnitude = f32::MIN;
56 let mut max = None;
57 others
58 .into_iter()
59 .filter_map(move |other| self.penetration(other))
60 .for_each(|p| {
61 let magnitude = abs(p[0]) + abs(p[1]);
62 if magnitude > max_magnitude {
63 max_magnitude = magnitude;
64 max = Some(p);
65 }
66 });
67 max
68 }
69}
70
71#[cfg(feature = "std")]
72fn abs(v: f32) -> f32 {
73 v.abs()
74}
75
76#[cfg(all(not(feature = "std"), feature = "libm"))]
77fn abs(v: f32) -> f32 {
78 libm::fabsf(v)
79}
80
81#[cfg(all(not(feature = "std"), not(feature = "libm")))]
82fn abs(v: f32) -> f32 {
83 f32::from_bits(v.to_bits() & 0x7fff_ffff)
84}