Skip to main content

collide/
lib.rs

1#![deny(missing_docs)]
2
3/*!
4A generic collider trait designed for use by collision detection libraries.
5
6You can define new colliders and implement the `Collider` trait, making them usable with different collision detection libraries that adopt this trait.
7
8Collision detection libraries can be generic over vector types, scalar types, and dimensions, or specialized for specific ones.
9
10## Example
11
12```rust
13use collide::{Collider, CollisionInfo};
14
15#[derive(Copy, Clone, Debug, PartialEq)]
16struct Vec2(f32, f32);
17
18// Basic operation implementations here
19
20struct Sphere {
21    center: Vec2,
22    radius: f32,
23}
24
25impl Collider for Sphere {
26    type Vector = Vec2;
27
28    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<Vec2>> {
29        // Collision logic here
30        None
31    }
32}
33```
34*/
35
36use std::{hash::Hash, ops::Neg};
37
38pub use vector_space::Transform;
39
40/// Information about a detected collision between collider objects.
41/// The data is stored in the specified vector type.
42#[derive(Copy, Clone, Debug)]
43pub struct CollisionInfo<V> {
44    /// Contact point on the first collider.
45    pub self_contact: V,
46    /// Contact point on the other collider.
47    pub other_contact: V,
48    /// Smallest displacement vector to move the first collider so objects no longer touch.
49    pub vector: V,
50}
51
52impl<V: Neg<Output = V>> Neg for CollisionInfo<V> {
53    type Output = Self;
54    fn neg(self) -> Self {
55        let Self {
56            self_contact,
57            other_contact,
58            vector,
59        } = self;
60        Self {
61            self_contact: other_contact,
62            other_contact: self_contact,
63            vector: -vector,
64        }
65    }
66}
67
68/// The collider trait that all colliders must implement.
69pub trait Collider<OtherCollider = Self> {
70    /// The underlying vector type.
71    type Vector;
72
73    /// Checks if two colliders collide.
74    /// By default just checks if a collision info is found, so implementing this manually might be more effcient.
75    fn check_collision(&self, other: &OtherCollider) -> bool {
76        self.collision_info(other).is_some()
77    }
78
79    /// Returns collision info if colliders intersect, otherwise `None`.
80    fn collision_info(&self, other: &OtherCollider) -> Option<CollisionInfo<Self::Vector>>;
81}
82
83/// A bounding volume that can be tested for overlap and merged with another.
84///
85/// Used by BVH-based collision detection. The user defines the volume type
86/// (AABB, bounding sphere, etc.) and implements this trait.
87pub trait BoundingVolume: Clone {
88    /// Returns true if this volume overlaps with another.
89    fn overlaps(&self, other: &Self) -> bool;
90
91    /// Returns the smallest volume that contains both volumes.
92    fn merged(&self, other: &Self) -> Self;
93}
94
95/// Maps a collider to a bounding volume for BVH-based broad-phase acceleration.
96///
97/// Generic over the bounding volume type, so a single collider can provide
98/// multiple bounding volume representations (e.g. both sphere and capsule).
99///
100/// Collision managers can use this to build a bounding volume hierarchy,
101/// reducing O(n²) collision queries to O(n log n).
102pub trait Bounded<B> {
103    /// Returns the bounding volume of this collider.
104    fn bounding_volume(&self) -> B;
105}
106
107/// Wraps a collider with a bounding volume for cheap pre-checks.
108///
109/// The bounding volume is checked first via `check_collision`. Only if
110/// it passes, the inner collider's `collision_info` is called.
111/// Multiple layers can be nested for cascading broad-to-narrow checks.
112#[derive(Clone, Debug)]
113pub struct BoundedCollider<B, C> {
114    /// The bounding volume used for cheap pre-checks.
115    pub bounding: B,
116    /// The actual collider for precise collision detection.
117    pub inner: C,
118}
119
120impl<B, C: Bounded<B>> BoundedCollider<B, C> {
121    /// Creates a bounded collider, computing the bounding volume automatically.
122    pub fn new(inner: C) -> Self {
123        let bounding = inner.bounding_volume();
124        Self { bounding, inner }
125    }
126}
127
128impl<B: Collider, C: Collider<Vector = B::Vector>> Collider for BoundedCollider<B, C> {
129    type Vector = C::Vector;
130
131    fn check_collision(&self, other: &Self) -> bool {
132        self.bounding.check_collision(&other.bounding) && self.inner.check_collision(&other.inner)
133    }
134
135    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<Self::Vector>> {
136        if !self.bounding.check_collision(&other.bounding) {
137            return None;
138        }
139        self.inner.collision_info(&other.inner)
140    }
141}
142
143impl<B: BoundingVolume, C: Bounded<B>> Bounded<B> for BoundedCollider<B, C> {
144    fn bounding_volume(&self) -> B {
145        self.bounding.clone()
146    }
147}
148
149/// Defines how a transform is applied to a collider, producing a new collider.
150///
151/// Shape crates implement this for their types so they can be used with `Transformed`.
152/// Also useful outside of collision detection (rendering, physics, etc.).
153pub trait Transformable<T> {
154    /// Returns a new collider with the transform applied.
155    fn transformed(&self, transform: &T) -> Self;
156}
157
158/// Wraps a collider with a transform, applying it before collision checks.
159///
160/// Both colliders are materialized in world space via `Transformable::transformed`
161/// before checking. For `Copy` types (Sphere, Capsule) this is free.
162/// For heap-allocated types (Convex) this involves allocation.
163#[derive(Clone, Debug)]
164pub struct Transformed<C, T> {
165    /// The transform applied to the inner collider.
166    pub transform: T,
167    /// The actual collider.
168    pub inner: C,
169}
170
171impl<C, T> Transformed<C, T> {
172    /// Creates a transformed collider.
173    pub fn new(inner: C, transform: T) -> Self {
174        Self { transform, inner }
175    }
176}
177
178impl<C, T, V> Collider for Transformed<C, T>
179where
180    C: Collider<Vector = V> + Transformable<T>,
181    V: Copy,
182{
183    type Vector = V;
184
185    fn check_collision(&self, other: &Self) -> bool {
186        self.inner
187            .transformed(&self.transform)
188            .check_collision(&other.inner.transformed(&other.transform))
189    }
190
191    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<V>> {
192        self.inner
193            .transformed(&self.transform)
194            .collision_info(&other.inner.transformed(&other.transform))
195    }
196}
197
198/// Maps a collider to spatial cells for broad-phase acceleration.
199///
200/// Collision managers can use this to build a spatial index and only check
201/// pairs that share at least one cell, reducing O(n²) to O(n×k) where k
202/// is the average number of colliders per cell.
203pub trait SpatialPartition {
204    /// The cell type used for spatial indexing.
205    type Cell: Hash + Eq;
206
207    /// Returns the cells this collider occupies.
208    fn cells(&self) -> impl Iterator<Item = Self::Cell>;
209}