moon_engine/
collider.rs

1//! Definition of the [`Collider`] and [`Collide`] traits, as well as simple Colliders.
2
3use crate::clamp;
4use crate::Point;
5
6/// Default bounding box size for a [`Point`].
7pub const POINT_BOUNDING_SIZE: f32 = 0.1;
8
9/// The `Collider` trait is implemented by different Colliders.
10pub trait Collider {
11    /// Get the bounding box of a Collider as an `AABB`.
12    fn get_bounding_box(&self) -> AABB;
13
14    /// Get the center of the Collider as a `Point`.
15    fn get_center(&self) -> Point;
16}
17
18/// The `Collide` trait is used to define collisions between two [Colliders](Collider).
19pub trait Collide<T: Collider> {
20    /// Checks if two [Colliders](Collider) intersect.
21    ///
22    /// Returns a `bool` indicating whether a collision occured.
23    ///
24    /// # Examples
25    ///
26    /// ```
27    /// use moon_engine::Point;
28    /// use moon_engine::collider::{Collide, Circle};
29    /// let a = Point::new(0.0, 0.5);
30    /// let b = Circle::new_size(0.1);
31    ///
32    /// let collision: bool = a.collide_with(&b);
33    /// ```
34    fn collide_with(&self, _other: &T) -> bool;
35}
36
37/// An Axis-Aligned Bounding Box (AABB).
38#[derive(Debug)]
39pub struct AABB {
40    /// The lowest point on the X and Y axes of the [`AABB`].
41    pub min: Point,
42    /// The highest point on the X and Y axes of the [`AABB`].
43    pub max: Point,
44}
45
46impl Default for AABB {
47    fn default() -> Self {
48        Self {
49            min: Point::new(-0.5, -0.5),
50            max: Point::new(0.5, 0.5),
51        }
52    }
53}
54
55impl AABB {
56    /// Creates a new [`AABB`] with a size of 1 unit, centered at a given co-ordinates.
57    pub fn new_position(x: f32, y: f32) -> Self {
58        Self {
59            min: Point::new(x - 0.5, y - 0.5),
60            max: Point::new(x + 0.5, y + 0.5),
61        }
62    }
63    /// Creates a new [`AABB`] with a given width and height, centered at the origin (0, 0).
64    pub fn new_size(width: f32, height: f32) -> Self {
65        let half: Point = Point::new(width / 2.0, height / 2.0);
66        Self {
67            min: -half,
68            max: half,
69        }
70    }
71    /// Creates a new [`AABB`] with a given width and height, centered at the given co-ordinates.
72    pub fn new_position_and_size(x: f32, y: f32, width: f32, height: f32) -> Self {
73        let position: Point = Point::new(x, y);
74        let half: Point = Point::new(width / 2.0, height / 2.0);
75        Self {
76            min: position - half,
77            max: position + half,
78        }
79    }
80}
81
82/// A Cicle Collider.
83#[derive(Debug)]
84pub struct Circle {
85    /// The [`Point`] at which the [`Circle`] is centered.
86    pub origin: Point,
87    /// Radius of the Circle Collider.
88    ///
89    /// The radius defines how far any given point on it's circumference is from it's center.
90    pub radius: f32,
91}
92
93impl Circle {
94    /// Creates a new [`Circle`] with a radius of 0.5 unit, centered at the given co-ordinates.
95    pub fn new_position(x: f32, y: f32) -> Self {
96        Self {
97            origin: Point::new(x, y),
98            radius: 0.5,
99        }
100    }
101    /// Creates a new [`Circle`] with a given radius, centered at the origin (0, 0).
102    pub fn new_size(radius: f32) -> Self {
103        Self {
104            origin: Point::zeros(),
105            radius,
106        }
107    }
108    /// Creates a new [`Circle`] with a given radius, centered at the given co-ordinates.
109    pub fn new_position_and_size(x: f32, y: f32, radius: f32) -> Self {
110        Self {
111            origin: Point::new(x, y),
112            radius,
113        }
114    }
115}
116
117impl Collider for Point {
118    /// Get a bounding box for a [`Point`], using [`POINT_BOUNDING_SIZE`] as its size
119    fn get_bounding_box(&self) -> AABB {
120        AABB::new_position_and_size(self.x, self.y, POINT_BOUNDING_SIZE, POINT_BOUNDING_SIZE)
121    }
122
123    /// Get the center of the [`Point`] Collider
124    fn get_center(&self) -> Point {
125        *self
126    }
127}
128
129impl Collider for AABB {
130    /// Return the tight bounding box of the `AABB`, as a copy of itself
131    fn get_bounding_box(&self) -> AABB {
132        AABB { ..*self }
133    }
134
135    /// Get the center of the AABB Collider
136    fn get_center(&self) -> Point {
137        self.max - self.min
138    }
139}
140
141impl Collider for Circle {
142    fn get_bounding_box(&self) -> AABB {
143        AABB {
144            min: self.origin - Point::from_element(1.0),
145            max: self.origin + Point::from_element(1.0),
146        }
147    }
148
149    fn get_center(&self) -> Point {
150        self.origin
151    }
152}
153
154/// Point and Point Collsion
155impl Collide<Point> for Point {
156    fn collide_with(&self, _other: &Point) -> bool {
157        self == _other
158    }
159}
160
161/// Point and AABB Collison
162impl Collide<AABB> for Point {
163    fn collide_with(&self, _other: &AABB) -> bool {
164        _other.collide_with(self)
165    }
166}
167
168/// Point and Circle Collison
169impl Collide<Circle> for Point {
170    fn collide_with(&self, _other: &Circle) -> bool {
171        _other.collide_with(self)
172    }
173}
174
175/// AABB and AABB Collison
176impl Collide<AABB> for AABB {
177    fn collide_with(&self, _other: &AABB) -> bool {
178        self.min.x < _other.max.x
179            && _other.min.x < self.max.x
180            && self.min.y < _other.max.y
181            && _other.min.y < self.max.y
182    }
183}
184
185/// AABB and Point Collision
186impl Collide<Point> for AABB {
187    fn collide_with(&self, _other: &Point) -> bool {
188        self.min.x < _other.x
189            && _other.x < self.max.x
190            && self.min.y < _other.y
191            && _other.y < self.max.y
192    }
193}
194
195/// AABB and Circle Collision
196impl Collide<Circle> for AABB {
197    fn collide_with(&self, _other: &Circle) -> bool {
198        let mut closest: Point = _other.origin;
199        closest.x = clamp(closest.x, self.min.x, self.max.x);
200        closest.y = clamp(closest.y, self.min.y, self.max.y);
201
202        _other.collide_with(&closest)
203    }
204}
205
206/// Circle and Circle Collision
207impl Collide<Circle> for Circle {
208    fn collide_with(&self, _other: &Circle) -> bool {
209        let radii_sum = self.radius + _other.radius;
210        let distance: Point = _other.origin - self.origin;
211        distance.norm_squared() < radii_sum * radii_sum
212    }
213}
214
215/// Circle and Point Collision
216impl Collide<Point> for Circle {
217    fn collide_with(&self, _other: &Point) -> bool {
218        let distance: Point = _other - self.origin;
219        distance.norm_squared() < self.radius * self.radius
220    }
221}
222
223/// Circle and AABB Collision
224impl Collide<AABB> for Circle {
225    fn collide_with(&self, _other: &AABB) -> bool {
226        _other.collide_with(self)
227    }
228}