collide 0.7.0

Simple extensible collision management
Documentation
#![deny(missing_docs)]

/*!
A generic collider trait designed for use by collision detection libraries.

You can define new colliders and implement the `Collider` trait, making them usable with different collision detection libraries that adopt this trait.

Collision detection libraries can be generic over vector types, scalar types, and dimensions, or specialized for specific ones.

## Example

```rust
use collide::{Collider, CollisionInfo};

#[derive(Copy, Clone, Debug, PartialEq)]
struct Vec2(f32, f32);

// Basic operation implementations here

struct Sphere {
    center: Vec2,
    radius: f32,
}

impl Collider for Sphere {
    type Vector = Vec2;

    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<Vec2>> {
        // Collision logic here
        None
    }
}
```
*/

use std::{hash::Hash, ops::Neg};

pub use vector_space::Transform;

/// Information about a detected collision between collider objects.
/// The data is stored in the specified vector type.
#[derive(Copy, Clone, Debug)]
pub struct CollisionInfo<V> {
    /// Contact point on the first collider.
    pub self_contact: V,
    /// Contact point on the other collider.
    pub other_contact: V,
    /// Smallest displacement vector to move the first collider so objects no longer touch.
    pub vector: V,
}

impl<V: Neg<Output = V>> Neg for CollisionInfo<V> {
    type Output = Self;
    fn neg(self) -> Self {
        let Self {
            self_contact,
            other_contact,
            vector,
        } = self;
        Self {
            self_contact: other_contact,
            other_contact: self_contact,
            vector: -vector,
        }
    }
}

/// The collider trait that all colliders must implement.
pub trait Collider<OtherCollider = Self> {
    /// The underlying vector type.
    type Vector;

    /// Checks if two colliders collide.
    /// By default just checks if a collision info is found, so implementing this manually might be more effcient.
    fn check_collision(&self, other: &OtherCollider) -> bool {
        self.collision_info(other).is_some()
    }

    /// Returns collision info if colliders intersect, otherwise `None`.
    fn collision_info(&self, other: &OtherCollider) -> Option<CollisionInfo<Self::Vector>>;
}

/// A bounding volume that can be tested for overlap and merged with another.
///
/// Used by BVH-based collision detection. The user defines the volume type
/// (AABB, bounding sphere, etc.) and implements this trait.
pub trait BoundingVolume: Clone {
    /// Returns true if this volume overlaps with another.
    fn overlaps(&self, other: &Self) -> bool;

    /// Returns the smallest volume that contains both volumes.
    fn merged(&self, other: &Self) -> Self;
}

/// Maps a collider to a bounding volume for BVH-based broad-phase acceleration.
///
/// Generic over the bounding volume type, so a single collider can provide
/// multiple bounding volume representations (e.g. both sphere and capsule).
///
/// Collision managers can use this to build a bounding volume hierarchy,
/// reducing O(n²) collision queries to O(n log n).
pub trait Bounded<B> {
    /// Returns the bounding volume of this collider.
    fn bounding_volume(&self) -> B;
}

/// Wraps a collider with a bounding volume for cheap pre-checks.
///
/// The bounding volume is checked first via `check_collision`. Only if
/// it passes, the inner collider's `collision_info` is called.
/// Multiple layers can be nested for cascading broad-to-narrow checks.
#[derive(Clone, Debug)]
pub struct BoundedCollider<B, C> {
    /// The bounding volume used for cheap pre-checks.
    pub bounding: B,
    /// The actual collider for precise collision detection.
    pub inner: C,
}

impl<B, C: Bounded<B>> BoundedCollider<B, C> {
    /// Creates a bounded collider, computing the bounding volume automatically.
    pub fn new(inner: C) -> Self {
        let bounding = inner.bounding_volume();
        Self { bounding, inner }
    }
}

impl<B: Collider, C: Collider<Vector = B::Vector>> Collider for BoundedCollider<B, C> {
    type Vector = C::Vector;

    fn check_collision(&self, other: &Self) -> bool {
        self.bounding.check_collision(&other.bounding) && self.inner.check_collision(&other.inner)
    }

    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<Self::Vector>> {
        if !self.bounding.check_collision(&other.bounding) {
            return None;
        }
        self.inner.collision_info(&other.inner)
    }
}

impl<B: BoundingVolume, C: Bounded<B>> Bounded<B> for BoundedCollider<B, C> {
    fn bounding_volume(&self) -> B {
        self.bounding.clone()
    }
}

/// Defines how a transform is applied to a collider, producing a new collider.
///
/// Shape crates implement this for their types so they can be used with `Transformed`.
/// Also useful outside of collision detection (rendering, physics, etc.).
pub trait Transformable<T> {
    /// Returns a new collider with the transform applied.
    fn transformed(&self, transform: &T) -> Self;
}

/// Wraps a collider with a transform, applying it before collision checks.
///
/// Both colliders are materialized in world space via `Transformable::transformed`
/// before checking. For `Copy` types (Sphere, Capsule) this is free.
/// For heap-allocated types (Convex) this involves allocation.
#[derive(Clone, Debug)]
pub struct Transformed<C, T> {
    /// The transform applied to the inner collider.
    pub transform: T,
    /// The actual collider.
    pub inner: C,
}

impl<C, T> Transformed<C, T> {
    /// Creates a transformed collider.
    pub fn new(inner: C, transform: T) -> Self {
        Self { transform, inner }
    }
}

impl<C, T, V> Collider for Transformed<C, T>
where
    C: Collider<Vector = V> + Transformable<T>,
    V: Copy,
{
    type Vector = V;

    fn check_collision(&self, other: &Self) -> bool {
        self.inner
            .transformed(&self.transform)
            .check_collision(&other.inner.transformed(&other.transform))
    }

    fn collision_info(&self, other: &Self) -> Option<CollisionInfo<V>> {
        self.inner
            .transformed(&self.transform)
            .collision_info(&other.inner.transformed(&other.transform))
    }
}

/// Maps a collider to spatial cells for broad-phase acceleration.
///
/// Collision managers can use this to build a spatial index and only check
/// pairs that share at least one cell, reducing O(n²) to O(n×k) where k
/// is the average number of colliders per cell.
pub trait SpatialPartition {
    /// The cell type used for spatial indexing.
    type Cell: Hash + Eq;

    /// Returns the cells this collider occupies.
    fn cells(&self) -> impl Iterator<Item = Self::Cell>;
}