euv_engine/collider/
impl.rs1use crate::*;
2
3impl AabbCollider {
5 pub fn from_center(center: Vector2D, width: f64, height: f64) -> AabbCollider {
17 AabbCollider::new(Rect::from_center(center, width, height))
18 }
19
20 pub fn collide_with_aabb(&self, other: &AabbCollider) -> Option<CollisionResult> {
30 let self_rect: Rect = self.get_rect();
31 let other_rect: Rect = other.get_rect();
32 let a_min: Vector2D = self_rect.min();
33 let a_max: Vector2D = self_rect.max();
34 let b_min: Vector2D = other_rect.min();
35 let b_max: Vector2D = other_rect.max();
36 let overlap_x: f64 =
37 (a_max.get_x().min(b_max.get_x()) - a_min.get_x().max(b_min.get_x())).max(0.0);
38 let overlap_y: f64 =
39 (a_max.get_y().min(b_max.get_y()) - a_min.get_y().max(b_min.get_y())).max(0.0);
40 if overlap_x <= COLLIDER_CONTACT_EPSILON || overlap_y <= COLLIDER_CONTACT_EPSILON {
41 return None;
42 }
43 let (normal, depth) = if overlap_x < overlap_y {
44 let direction: f64 = if self_rect.center().get_x() < other_rect.center().get_x() {
45 -1.0
46 } else {
47 1.0
48 };
49 (Vector2D::new(direction, 0.0), overlap_x)
50 } else {
51 let direction: f64 = if self_rect.center().get_y() < other_rect.center().get_y() {
52 -1.0
53 } else {
54 1.0
55 };
56 (Vector2D::new(0.0, direction), overlap_y)
57 };
58 let contact_point: Vector2D = Vector2D::new(
59 a_min
60 .get_x()
61 .max(b_min.get_x())
62 .min(a_max.get_x().min(b_max.get_x())),
63 a_min
64 .get_y()
65 .max(b_min.get_y())
66 .min(a_max.get_y().min(b_max.get_y())),
67 );
68 Some(CollisionResult::new(normal, depth, contact_point))
69 }
70
71 pub fn collide_with_circle(&self, circle: &CircleCollider) -> Option<CollisionResult> {
81 let self_rect: Rect = self.get_rect();
82 let circle_inner: Circle = circle.get_circle();
83 let rect_min: Vector2D = self_rect.min();
84 let rect_max: Vector2D = self_rect.max();
85 let closest_x: f64 = circle_inner
86 .get_center()
87 .get_x()
88 .clamp(rect_min.get_x(), rect_max.get_x());
89 let closest_y: f64 = circle_inner
90 .get_center()
91 .get_y()
92 .clamp(rect_min.get_y(), rect_max.get_y());
93 let delta: Vector2D = circle_inner.get_center() - Vector2D::new(closest_x, closest_y);
94 let distance_sq: f64 = delta.magnitude_squared();
95 if distance_sq >= circle_inner.get_radius() * circle_inner.get_radius() {
96 return None;
97 }
98 let distance: f64 = distance_sq.sqrt();
99 let normal: Vector2D = if distance < EPSILON {
100 let aabb_center: Vector2D = self_rect.center();
101 let center_delta: Vector2D = circle_inner.get_center() - aabb_center;
102 if center_delta.magnitude() < EPSILON {
103 Vector2D::up()
104 } else {
105 center_delta.normalized()
106 }
107 } else {
108 delta.scaled(1.0 / distance)
109 };
110 let depth: f64 = circle_inner.get_radius() - distance;
111 let contact_point: Vector2D = Vector2D::new(closest_x, closest_y);
112 Some(CollisionResult::new(normal, depth, contact_point))
113 }
114}
115
116impl Collider for AabbCollider {
118 fn shape(&self) -> ColliderShape {
119 ColliderShape::Aabb
120 }
121
122 fn bounding_box(&self) -> Rect {
123 self.get_rect()
124 }
125
126 fn contains_point(&self, point: Vector2D) -> bool {
127 self.get_rect().contains(point)
128 }
129
130 fn center(&self) -> Vector2D {
131 self.get_rect().center()
132 }
133}
134
135impl CircleCollider {
137 pub fn from_center(center: Vector2D, radius: f64) -> CircleCollider {
148 CircleCollider::new(Circle::new(center, radius))
149 }
150
151 pub fn collide_with_circle(&self, other: &CircleCollider) -> Option<CollisionResult> {
161 let self_circle: Circle = self.get_circle();
162 let other_circle: Circle = other.get_circle();
163 let delta: Vector2D = other_circle.get_center() - self_circle.get_center();
164 let distance: f64 = delta.magnitude();
165 let radius_sum: f64 = self_circle.get_radius() + other_circle.get_radius();
166 if distance >= radius_sum {
167 return None;
168 }
169 let normal: Vector2D = if distance < EPSILON {
170 Vector2D::right()
171 } else {
172 delta.scaled(1.0 / distance)
173 };
174 let depth: f64 = radius_sum - distance;
175 let contact_point: Vector2D =
176 self_circle.get_center() + normal.scaled(self_circle.get_radius());
177 Some(CollisionResult::new(normal, depth, contact_point))
178 }
179}
180
181impl Collider for CircleCollider {
183 fn shape(&self) -> ColliderShape {
184 ColliderShape::Circle
185 }
186
187 fn bounding_box(&self) -> Rect {
188 let circle: Circle = self.get_circle();
189 let diameter: f64 = circle.get_radius() * 2.0;
190 Rect::from_center(circle.get_center(), diameter, diameter)
191 }
192
193 fn contains_point(&self, point: Vector2D) -> bool {
194 self.get_circle().contains(point)
195 }
196
197 fn center(&self) -> Vector2D {
198 self.get_circle().get_center()
199 }
200}
201
202impl Rect {
204 pub fn broad_phase_check(a: Rect, b: Rect) -> bool {
215 a.intersects(b)
216 }
217}
218
219impl AabbCollider3D {
221 pub fn from_center(center: Vector3D, width: f64, height: f64, depth: f64) -> AabbCollider3D {
234 AabbCollider3D::new(AABB3D::from_center(center, width, height, depth))
235 }
236
237 pub fn collide_with_aabb(&self, other: &AabbCollider3D) -> Option<CollisionResult3D> {
247 let self_aabb: AABB3D = self.get_aabb();
248 let other_aabb: AABB3D = other.get_aabb();
249 let a_center: Vector3D = self_aabb.center();
250 let b_center: Vector3D = other_aabb.center();
251 let a_size: Vector3D = self_aabb.size();
252 let b_size: Vector3D = other_aabb.size();
253 let overlap_x: f64 =
254 (a_size.get_x() + b_size.get_x()) * 0.5 - (a_center.get_x() - b_center.get_x()).abs();
255 if overlap_x <= COLLIDER_CONTACT_EPSILON {
256 return None;
257 }
258 let overlap_y: f64 =
259 (a_size.get_y() + b_size.get_y()) * 0.5 - (a_center.get_y() - b_center.get_y()).abs();
260 if overlap_y <= COLLIDER_CONTACT_EPSILON {
261 return None;
262 }
263 let overlap_z: f64 =
264 (a_size.get_z() + b_size.get_z()) * 0.5 - (a_center.get_z() - b_center.get_z()).abs();
265 if overlap_z <= COLLIDER_CONTACT_EPSILON {
266 return None;
267 }
268 let (normal, depth) = if overlap_x <= overlap_y && overlap_x <= overlap_z {
269 let direction: f64 = if a_center.get_x() < b_center.get_x() {
270 -1.0
271 } else {
272 1.0
273 };
274 (Vector3D::new(direction, 0.0, 0.0), overlap_x)
275 } else if overlap_y <= overlap_z {
276 let direction: f64 = if a_center.get_y() < b_center.get_y() {
277 -1.0
278 } else {
279 1.0
280 };
281 (Vector3D::new(0.0, direction, 0.0), overlap_y)
282 } else {
283 let direction: f64 = if a_center.get_z() < b_center.get_z() {
284 -1.0
285 } else {
286 1.0
287 };
288 (Vector3D::new(0.0, 0.0, direction), overlap_z)
289 };
290 let self_min: Vector3D = self_aabb.get_min();
291 let self_max: Vector3D = self_aabb.get_max();
292 let other_min: Vector3D = other_aabb.get_min();
293 let other_max: Vector3D = other_aabb.get_max();
294 let contact_point: Vector3D = Vector3D::new(
295 self_min
296 .get_x()
297 .max(other_min.get_x())
298 .min(self_max.get_x().min(other_max.get_x())),
299 self_min
300 .get_y()
301 .max(other_min.get_y())
302 .min(self_max.get_y().min(other_max.get_y())),
303 self_min
304 .get_z()
305 .max(other_min.get_z())
306 .min(self_max.get_z().min(other_max.get_z())),
307 );
308 Some(CollisionResult3D::new(normal, depth, contact_point))
309 }
310
311 pub fn collide_with_sphere(&self, sphere: &SphereCollider3D) -> Option<CollisionResult3D> {
321 let self_aabb: AABB3D = self.get_aabb();
322 let sphere_inner: Sphere = sphere.get_sphere();
323 let aabb_min: Vector3D = self_aabb.get_min();
324 let aabb_max: Vector3D = self_aabb.get_max();
325 let closest_x: f64 = sphere_inner
326 .get_center()
327 .get_x()
328 .clamp(aabb_min.get_x(), aabb_max.get_x());
329 let closest_y: f64 = sphere_inner
330 .get_center()
331 .get_y()
332 .clamp(aabb_min.get_y(), aabb_max.get_y());
333 let closest_z: f64 = sphere_inner
334 .get_center()
335 .get_z()
336 .clamp(aabb_min.get_z(), aabb_max.get_z());
337 let closest: Vector3D = Vector3D::new(closest_x, closest_y, closest_z);
338 let delta: Vector3D = sphere_inner.get_center() - closest;
339 let distance_sq: f64 = delta.magnitude_squared();
340 if distance_sq >= sphere_inner.get_radius() * sphere_inner.get_radius() {
341 return None;
342 }
343 let distance: f64 = distance_sq.sqrt();
344 let normal: Vector3D = if distance < EPSILON {
345 let aabb_center: Vector3D = self_aabb.center();
346 let center_delta: Vector3D = sphere_inner.get_center() - aabb_center;
347 if center_delta.magnitude() < EPSILON {
348 Vector3D::up()
349 } else {
350 center_delta.normalized()
351 }
352 } else {
353 delta.scaled(1.0 / distance)
354 };
355 let depth: f64 = sphere_inner.get_radius() - distance;
356 let contact_point: Vector3D = closest;
357 Some(CollisionResult3D::new(normal, depth, contact_point))
358 }
359}
360
361impl Collider3D for AabbCollider3D {
363 fn shape(&self) -> ColliderShape3D {
364 ColliderShape3D::Aabb
365 }
366
367 fn bounding_box(&self) -> AABB3D {
368 self.get_aabb()
369 }
370
371 fn contains_point(&self, point: Vector3D) -> bool {
372 self.get_aabb().contains(point)
373 }
374
375 fn center(&self) -> Vector3D {
376 self.get_aabb().center()
377 }
378}
379
380impl SphereCollider3D {
382 pub fn from_center(center: Vector3D, radius: f64) -> SphereCollider3D {
393 SphereCollider3D::new(Sphere::new(center, radius))
394 }
395
396 pub fn collide_with_sphere(&self, other: &SphereCollider3D) -> Option<CollisionResult3D> {
406 let self_sphere: Sphere = self.get_sphere();
407 let other_sphere: Sphere = other.get_sphere();
408 let delta: Vector3D = other_sphere.get_center() - self_sphere.get_center();
409 let distance: f64 = delta.magnitude();
410 let radius_sum: f64 = self_sphere.get_radius() + other_sphere.get_radius();
411 if distance >= radius_sum {
412 return None;
413 }
414 let normal: Vector3D = if distance < EPSILON {
415 Vector3D::right()
416 } else {
417 delta.scaled(1.0 / distance)
418 };
419 let depth: f64 = radius_sum - distance;
420 let contact_point: Vector3D =
421 self_sphere.get_center() + normal.scaled(self_sphere.get_radius());
422 Some(CollisionResult3D::new(normal, depth, contact_point))
423 }
424}
425
426impl Collider3D for SphereCollider3D {
428 fn shape(&self) -> ColliderShape3D {
429 ColliderShape3D::Sphere
430 }
431
432 fn bounding_box(&self) -> AABB3D {
433 let sphere: Sphere = self.get_sphere();
434 let diameter: f64 = sphere.get_radius() * 2.0;
435 AABB3D::from_center(sphere.get_center(), diameter, diameter, diameter)
436 }
437
438 fn contains_point(&self, point: Vector3D) -> bool {
439 self.get_sphere().contains(point)
440 }
441
442 fn center(&self) -> Vector3D {
443 self.get_sphere().get_center()
444 }
445}
446
447impl AABB3D {
449 pub fn broad_phase_check(a: AABB3D, b: AABB3D) -> bool {
460 a.intersects(b)
461 }
462}