euv_engine/collider/
impl.rs1use super::*;
2
3impl AabbCollider {
5 pub fn from_center(center: Vector2D, width: f64, height: f64) -> AabbCollider {
16 AabbCollider::new(Rect::from_center(center, width, height))
17 }
18
19 pub fn collide_with_aabb(&self, other: &AabbCollider) -> Option<CollisionResult> {
29 let self_rect: Rect = self.get_rect();
30 let other_rect: Rect = other.get_rect();
31 let a_min: Vector2D = self_rect.min();
32 let a_max: Vector2D = self_rect.max();
33 let b_min: Vector2D = other_rect.min();
34 let b_max: Vector2D = other_rect.max();
35 let overlap_x: f64 =
36 (a_max.get_x().min(b_max.get_x()) - a_min.get_x().max(b_min.get_x())).max(0.0);
37 let overlap_y: f64 =
38 (a_max.get_y().min(b_max.get_y()) - a_min.get_y().max(b_min.get_y())).max(0.0);
39 if overlap_x <= COLLIDER_CONTACT_EPSILON || overlap_y <= COLLIDER_CONTACT_EPSILON {
40 return None;
41 }
42 let (normal, depth) = if overlap_x < overlap_y {
43 let direction: f64 = if self_rect.center().get_x() < other_rect.center().get_x() {
44 -1.0
45 } else {
46 1.0
47 };
48 (Vector2D::new(direction, 0.0), overlap_x)
49 } else {
50 let direction: f64 = if self_rect.center().get_y() < other_rect.center().get_y() {
51 -1.0
52 } else {
53 1.0
54 };
55 (Vector2D::new(0.0, direction), overlap_y)
56 };
57 let contact_point: Vector2D = Vector2D::new(
58 a_min
59 .get_x()
60 .max(b_min.get_x())
61 .min(a_max.get_x().min(b_max.get_x())),
62 a_min
63 .get_y()
64 .max(b_min.get_y())
65 .min(a_max.get_y().min(b_max.get_y())),
66 );
67 Some(CollisionResult::new(normal, depth, contact_point))
68 }
69
70 pub fn collide_with_circle(&self, circle: &CircleCollider) -> Option<CollisionResult> {
80 let self_rect: Rect = self.get_rect();
81 let circle_inner: Circle = circle.get_circle();
82 let rect_min: Vector2D = self_rect.min();
83 let rect_max: Vector2D = self_rect.max();
84 let closest_x: f64 = circle_inner
85 .get_center()
86 .get_x()
87 .clamp(rect_min.get_x(), rect_max.get_x());
88 let closest_y: f64 = circle_inner
89 .get_center()
90 .get_y()
91 .clamp(rect_min.get_y(), rect_max.get_y());
92 let delta: Vector2D = circle_inner.get_center() - Vector2D::new(closest_x, closest_y);
93 let distance_sq: f64 = delta.magnitude_squared();
94 if distance_sq >= circle_inner.get_radius() * circle_inner.get_radius() {
95 return None;
96 }
97 let distance: f64 = distance_sq.sqrt();
98 let normal: Vector2D = if distance < EPSILON {
99 let aabb_center: Vector2D = self_rect.center();
100 let center_delta: Vector2D = circle_inner.get_center() - aabb_center;
101 if center_delta.magnitude() < EPSILON {
102 Vector2D::up()
103 } else {
104 center_delta.normalized()
105 }
106 } else {
107 delta.scaled(1.0 / distance)
108 };
109 let depth: f64 = circle_inner.get_radius() - distance;
110 let contact_point: Vector2D = Vector2D::new(closest_x, closest_y);
111 Some(CollisionResult::new(normal, depth, contact_point))
112 }
113}
114
115impl Collider for AabbCollider {
117 fn shape(&self) -> ColliderShape {
123 ColliderShape::Aabb
124 }
125
126 fn bounding_box(&self) -> Rect {
132 self.get_rect()
133 }
134
135 fn contains_point(&self, point: Vector2D) -> bool {
145 self.get_rect().contains(point)
146 }
147
148 fn center(&self) -> Vector2D {
154 self.get_rect().center()
155 }
156}
157
158impl CircleCollider {
160 pub fn from_center(center: Vector2D, radius: f64) -> CircleCollider {
171 CircleCollider::new(Circle::new(center, radius))
172 }
173
174 pub fn collide_with_circle(&self, other: &CircleCollider) -> Option<CollisionResult> {
184 let self_circle: Circle = self.get_circle();
185 let other_circle: Circle = other.get_circle();
186 let delta: Vector2D = other_circle.get_center() - self_circle.get_center();
187 let distance: f64 = delta.magnitude();
188 let radius_sum: f64 = self_circle.get_radius() + other_circle.get_radius();
189 if distance >= radius_sum {
190 return None;
191 }
192 let normal: Vector2D = if distance < EPSILON {
193 Vector2D::right()
194 } else {
195 delta.scaled(1.0 / distance)
196 };
197 let depth: f64 = radius_sum - distance;
198 let contact_point: Vector2D =
199 self_circle.get_center() + normal.scaled(self_circle.get_radius());
200 Some(CollisionResult::new(normal, depth, contact_point))
201 }
202}
203
204impl Collider for CircleCollider {
206 fn shape(&self) -> ColliderShape {
212 ColliderShape::Circle
213 }
214
215 fn bounding_box(&self) -> Rect {
221 let circle: Circle = self.get_circle();
222 let diameter: f64 = circle.get_radius() * 2.0;
223 Rect::from_center(circle.get_center(), diameter, diameter)
224 }
225
226 fn contains_point(&self, point: Vector2D) -> bool {
236 self.get_circle().contains(point)
237 }
238
239 fn center(&self) -> Vector2D {
245 self.get_circle().get_center()
246 }
247}
248
249impl AabbCollider3D {
251 pub fn from_center(center: Vector3D, width: f64, height: f64, depth: f64) -> AabbCollider3D {
262 AabbCollider3D::new(AABB3D::from_center(center, width, height, depth))
263 }
264
265 pub fn collide_with_aabb(&self, other: &AabbCollider3D) -> Option<CollisionResult3D> {
275 let self_aabb: AABB3D = self.get_aabb();
276 let other_aabb: AABB3D = other.get_aabb();
277 let a_center: Vector3D = self_aabb.center();
278 let b_center: Vector3D = other_aabb.center();
279 let a_size: Vector3D = self_aabb.size();
280 let b_size: Vector3D = other_aabb.size();
281 let overlap_x: f64 =
282 (a_size.get_x() + b_size.get_x()) * 0.5 - (a_center.get_x() - b_center.get_x()).abs();
283 if overlap_x <= COLLIDER_CONTACT_EPSILON {
284 return None;
285 }
286 let overlap_y: f64 =
287 (a_size.get_y() + b_size.get_y()) * 0.5 - (a_center.get_y() - b_center.get_y()).abs();
288 if overlap_y <= COLLIDER_CONTACT_EPSILON {
289 return None;
290 }
291 let overlap_z: f64 =
292 (a_size.get_z() + b_size.get_z()) * 0.5 - (a_center.get_z() - b_center.get_z()).abs();
293 if overlap_z <= COLLIDER_CONTACT_EPSILON {
294 return None;
295 }
296 let (normal, depth) = if overlap_x <= overlap_y && overlap_x <= overlap_z {
297 let direction: f64 = if a_center.get_x() < b_center.get_x() {
298 -1.0
299 } else {
300 1.0
301 };
302 (Vector3D::new(direction, 0.0, 0.0), overlap_x)
303 } else if overlap_y <= overlap_z {
304 let direction: f64 = if a_center.get_y() < b_center.get_y() {
305 -1.0
306 } else {
307 1.0
308 };
309 (Vector3D::new(0.0, direction, 0.0), overlap_y)
310 } else {
311 let direction: f64 = if a_center.get_z() < b_center.get_z() {
312 -1.0
313 } else {
314 1.0
315 };
316 (Vector3D::new(0.0, 0.0, direction), overlap_z)
317 };
318 let self_min: Vector3D = self_aabb.get_min();
319 let self_max: Vector3D = self_aabb.get_max();
320 let other_min: Vector3D = other_aabb.get_min();
321 let other_max: Vector3D = other_aabb.get_max();
322 let contact_point: Vector3D = Vector3D::new(
323 self_min
324 .get_x()
325 .max(other_min.get_x())
326 .min(self_max.get_x().min(other_max.get_x())),
327 self_min
328 .get_y()
329 .max(other_min.get_y())
330 .min(self_max.get_y().min(other_max.get_y())),
331 self_min
332 .get_z()
333 .max(other_min.get_z())
334 .min(self_max.get_z().min(other_max.get_z())),
335 );
336 Some(CollisionResult3D::new(normal, depth, contact_point))
337 }
338
339 pub fn collide_with_sphere(&self, sphere: &SphereCollider3D) -> Option<CollisionResult3D> {
349 let self_aabb: AABB3D = self.get_aabb();
350 let sphere_inner: Sphere = sphere.get_sphere();
351 let aabb_min: Vector3D = self_aabb.get_min();
352 let aabb_max: Vector3D = self_aabb.get_max();
353 let closest_x: f64 = sphere_inner
354 .get_center()
355 .get_x()
356 .clamp(aabb_min.get_x(), aabb_max.get_x());
357 let closest_y: f64 = sphere_inner
358 .get_center()
359 .get_y()
360 .clamp(aabb_min.get_y(), aabb_max.get_y());
361 let closest_z: f64 = sphere_inner
362 .get_center()
363 .get_z()
364 .clamp(aabb_min.get_z(), aabb_max.get_z());
365 let closest: Vector3D = Vector3D::new(closest_x, closest_y, closest_z);
366 let delta: Vector3D = sphere_inner.get_center() - closest;
367 let distance_sq: f64 = delta.magnitude_squared();
368 if distance_sq >= sphere_inner.get_radius() * sphere_inner.get_radius() {
369 return None;
370 }
371 let distance: f64 = distance_sq.sqrt();
372 let normal: Vector3D = if distance < EPSILON {
373 let aabb_center: Vector3D = self_aabb.center();
374 let center_delta: Vector3D = sphere_inner.get_center() - aabb_center;
375 if center_delta.magnitude() < EPSILON {
376 Vector3D::up()
377 } else {
378 center_delta.normalized()
379 }
380 } else {
381 delta.scaled(1.0 / distance)
382 };
383 let depth: f64 = sphere_inner.get_radius() - distance;
384 let contact_point: Vector3D = closest;
385 Some(CollisionResult3D::new(normal, depth, contact_point))
386 }
387}
388
389impl Collider3D for AabbCollider3D {
391 fn shape(&self) -> ColliderShape3D {
397 ColliderShape3D::Aabb
398 }
399
400 fn bounding_box(&self) -> AABB3D {
406 self.get_aabb()
407 }
408
409 fn contains_point(&self, point: Vector3D) -> bool {
419 self.get_aabb().contains(point)
420 }
421
422 fn center(&self) -> Vector3D {
428 self.get_aabb().center()
429 }
430}
431
432impl SphereCollider3D {
434 pub fn from_center(center: Vector3D, radius: f64) -> SphereCollider3D {
445 SphereCollider3D::new(Sphere::new(center, radius))
446 }
447
448 pub fn collide_with_sphere(&self, other: &SphereCollider3D) -> Option<CollisionResult3D> {
458 let self_sphere: Sphere = self.get_sphere();
459 let other_sphere: Sphere = other.get_sphere();
460 let delta: Vector3D = other_sphere.get_center() - self_sphere.get_center();
461 let distance: f64 = delta.magnitude();
462 let radius_sum: f64 = self_sphere.get_radius() + other_sphere.get_radius();
463 if distance >= radius_sum {
464 return None;
465 }
466 let normal: Vector3D = if distance < EPSILON {
467 Vector3D::right()
468 } else {
469 delta.scaled(1.0 / distance)
470 };
471 let depth: f64 = radius_sum - distance;
472 let contact_point: Vector3D =
473 self_sphere.get_center() + normal.scaled(self_sphere.get_radius());
474 Some(CollisionResult3D::new(normal, depth, contact_point))
475 }
476}
477
478impl Collider3D for SphereCollider3D {
480 fn shape(&self) -> ColliderShape3D {
486 ColliderShape3D::Sphere
487 }
488
489 fn bounding_box(&self) -> AABB3D {
495 let sphere: Sphere = self.get_sphere();
496 let diameter: f64 = sphere.get_radius() * 2.0;
497 AABB3D::from_center(sphere.get_center(), diameter, diameter, diameter)
498 }
499
500 fn contains_point(&self, point: Vector3D) -> bool {
510 self.get_sphere().contains(point)
511 }
512
513 fn center(&self) -> Vector3D {
519 self.get_sphere().get_center()
520 }
521}
522
523impl AABB3D {
525 pub fn broad_phase(a: AABB3D, b: AABB3D) -> bool {
535 a.intersects(b)
536 }
537}