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}