euv_engine/collider/
impl.rs1use super::*;
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 {
124 ColliderShape::Aabb
125 }
126
127 fn bounding_box(&self) -> Rect {
133 self.get_rect()
134 }
135
136 fn contains_point(&self, point: Vector2D) -> bool {
146 self.get_rect().contains(point)
147 }
148
149 fn center(&self) -> Vector2D {
155 self.get_rect().center()
156 }
157}
158
159impl CircleCollider {
161 pub fn from_center(center: Vector2D, radius: f64) -> CircleCollider {
172 CircleCollider::new(Circle::new(center, radius))
173 }
174
175 pub fn collide_with_circle(&self, other: &CircleCollider) -> Option<CollisionResult> {
185 let self_circle: Circle = self.get_circle();
186 let other_circle: Circle = other.get_circle();
187 let delta: Vector2D = other_circle.get_center() - self_circle.get_center();
188 let distance: f64 = delta.magnitude();
189 let radius_sum: f64 = self_circle.get_radius() + other_circle.get_radius();
190 if distance >= radius_sum {
191 return None;
192 }
193 let normal: Vector2D = if distance < EPSILON {
194 Vector2D::right()
195 } else {
196 delta.scaled(1.0 / distance)
197 };
198 let depth: f64 = radius_sum - distance;
199 let contact_point: Vector2D =
200 self_circle.get_center() + normal.scaled(self_circle.get_radius());
201 Some(CollisionResult::new(normal, depth, contact_point))
202 }
203}
204
205impl Collider for CircleCollider {
207 fn shape(&self) -> ColliderShape {
213 ColliderShape::Circle
214 }
215
216 fn bounding_box(&self) -> Rect {
222 let circle: Circle = self.get_circle();
223 let diameter: f64 = circle.get_radius() * 2.0;
224 Rect::from_center(circle.get_center(), diameter, diameter)
225 }
226
227 fn contains_point(&self, point: Vector2D) -> bool {
237 self.get_circle().contains(point)
238 }
239
240 fn center(&self) -> Vector2D {
246 self.get_circle().get_center()
247 }
248}
249
250impl AabbCollider3D {
252 pub fn from_center(center: Vector3D, width: f64, height: f64, depth: f64) -> AabbCollider3D {
265 AabbCollider3D::new(AABB3D::from_center(center, width, height, depth))
266 }
267
268 pub fn collide_with_aabb(&self, other: &AabbCollider3D) -> Option<CollisionResult3D> {
278 let self_aabb: AABB3D = self.get_aabb();
279 let other_aabb: AABB3D = other.get_aabb();
280 let a_center: Vector3D = self_aabb.center();
281 let b_center: Vector3D = other_aabb.center();
282 let a_size: Vector3D = self_aabb.size();
283 let b_size: Vector3D = other_aabb.size();
284 let overlap_x: f64 =
285 (a_size.get_x() + b_size.get_x()) * 0.5 - (a_center.get_x() - b_center.get_x()).abs();
286 if overlap_x <= COLLIDER_CONTACT_EPSILON {
287 return None;
288 }
289 let overlap_y: f64 =
290 (a_size.get_y() + b_size.get_y()) * 0.5 - (a_center.get_y() - b_center.get_y()).abs();
291 if overlap_y <= COLLIDER_CONTACT_EPSILON {
292 return None;
293 }
294 let overlap_z: f64 =
295 (a_size.get_z() + b_size.get_z()) * 0.5 - (a_center.get_z() - b_center.get_z()).abs();
296 if overlap_z <= COLLIDER_CONTACT_EPSILON {
297 return None;
298 }
299 let (normal, depth) = if overlap_x <= overlap_y && overlap_x <= overlap_z {
300 let direction: f64 = if a_center.get_x() < b_center.get_x() {
301 -1.0
302 } else {
303 1.0
304 };
305 (Vector3D::new(direction, 0.0, 0.0), overlap_x)
306 } else if overlap_y <= overlap_z {
307 let direction: f64 = if a_center.get_y() < b_center.get_y() {
308 -1.0
309 } else {
310 1.0
311 };
312 (Vector3D::new(0.0, direction, 0.0), overlap_y)
313 } else {
314 let direction: f64 = if a_center.get_z() < b_center.get_z() {
315 -1.0
316 } else {
317 1.0
318 };
319 (Vector3D::new(0.0, 0.0, direction), overlap_z)
320 };
321 let self_min: Vector3D = self_aabb.get_min();
322 let self_max: Vector3D = self_aabb.get_max();
323 let other_min: Vector3D = other_aabb.get_min();
324 let other_max: Vector3D = other_aabb.get_max();
325 let contact_point: Vector3D = Vector3D::new(
326 self_min
327 .get_x()
328 .max(other_min.get_x())
329 .min(self_max.get_x().min(other_max.get_x())),
330 self_min
331 .get_y()
332 .max(other_min.get_y())
333 .min(self_max.get_y().min(other_max.get_y())),
334 self_min
335 .get_z()
336 .max(other_min.get_z())
337 .min(self_max.get_z().min(other_max.get_z())),
338 );
339 Some(CollisionResult3D::new(normal, depth, contact_point))
340 }
341
342 pub fn collide_with_sphere(&self, sphere: &SphereCollider3D) -> Option<CollisionResult3D> {
352 let self_aabb: AABB3D = self.get_aabb();
353 let sphere_inner: Sphere = sphere.get_sphere();
354 let aabb_min: Vector3D = self_aabb.get_min();
355 let aabb_max: Vector3D = self_aabb.get_max();
356 let closest_x: f64 = sphere_inner
357 .get_center()
358 .get_x()
359 .clamp(aabb_min.get_x(), aabb_max.get_x());
360 let closest_y: f64 = sphere_inner
361 .get_center()
362 .get_y()
363 .clamp(aabb_min.get_y(), aabb_max.get_y());
364 let closest_z: f64 = sphere_inner
365 .get_center()
366 .get_z()
367 .clamp(aabb_min.get_z(), aabb_max.get_z());
368 let closest: Vector3D = Vector3D::new(closest_x, closest_y, closest_z);
369 let delta: Vector3D = sphere_inner.get_center() - closest;
370 let distance_sq: f64 = delta.magnitude_squared();
371 if distance_sq >= sphere_inner.get_radius() * sphere_inner.get_radius() {
372 return None;
373 }
374 let distance: f64 = distance_sq.sqrt();
375 let normal: Vector3D = if distance < EPSILON {
376 let aabb_center: Vector3D = self_aabb.center();
377 let center_delta: Vector3D = sphere_inner.get_center() - aabb_center;
378 if center_delta.magnitude() < EPSILON {
379 Vector3D::up()
380 } else {
381 center_delta.normalized()
382 }
383 } else {
384 delta.scaled(1.0 / distance)
385 };
386 let depth: f64 = sphere_inner.get_radius() - distance;
387 let contact_point: Vector3D = closest;
388 Some(CollisionResult3D::new(normal, depth, contact_point))
389 }
390}
391
392impl Collider3D for AabbCollider3D {
394 fn shape(&self) -> ColliderShape3D {
400 ColliderShape3D::Aabb
401 }
402
403 fn bounding_box(&self) -> AABB3D {
409 self.get_aabb()
410 }
411
412 fn contains_point(&self, point: Vector3D) -> bool {
422 self.get_aabb().contains(point)
423 }
424
425 fn center(&self) -> Vector3D {
431 self.get_aabb().center()
432 }
433}
434
435impl SphereCollider3D {
437 pub fn from_center(center: Vector3D, radius: f64) -> SphereCollider3D {
448 SphereCollider3D::new(Sphere::new(center, radius))
449 }
450
451 pub fn collide_with_sphere(&self, other: &SphereCollider3D) -> Option<CollisionResult3D> {
461 let self_sphere: Sphere = self.get_sphere();
462 let other_sphere: Sphere = other.get_sphere();
463 let delta: Vector3D = other_sphere.get_center() - self_sphere.get_center();
464 let distance: f64 = delta.magnitude();
465 let radius_sum: f64 = self_sphere.get_radius() + other_sphere.get_radius();
466 if distance >= radius_sum {
467 return None;
468 }
469 let normal: Vector3D = if distance < EPSILON {
470 Vector3D::right()
471 } else {
472 delta.scaled(1.0 / distance)
473 };
474 let depth: f64 = radius_sum - distance;
475 let contact_point: Vector3D =
476 self_sphere.get_center() + normal.scaled(self_sphere.get_radius());
477 Some(CollisionResult3D::new(normal, depth, contact_point))
478 }
479}
480
481impl Collider3D for SphereCollider3D {
483 fn shape(&self) -> ColliderShape3D {
489 ColliderShape3D::Sphere
490 }
491
492 fn bounding_box(&self) -> AABB3D {
498 let sphere: Sphere = self.get_sphere();
499 let diameter: f64 = sphere.get_radius() * 2.0;
500 AABB3D::from_center(sphere.get_center(), diameter, diameter, diameter)
501 }
502
503 fn contains_point(&self, point: Vector3D) -> bool {
513 self.get_sphere().contains(point)
514 }
515
516 fn center(&self) -> Vector3D {
522 self.get_sphere().get_center()
523 }
524}
525
526impl AABB3D {
528 pub fn broad_phase(a: AABB3D, b: AABB3D) -> bool {
539 a.intersects(b)
540 }
541}