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}