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 AabbCollider3D {
204 pub fn from_center(center: Vector3D, width: f64, height: f64, depth: f64) -> AabbCollider3D {
217 AabbCollider3D::new(AABB3D::from_center(center, width, height, depth))
218 }
219
220 pub fn collide_with_aabb(&self, other: &AabbCollider3D) -> Option<CollisionResult3D> {
230 let self_aabb: AABB3D = self.get_aabb();
231 let other_aabb: AABB3D = other.get_aabb();
232 let a_center: Vector3D = self_aabb.center();
233 let b_center: Vector3D = other_aabb.center();
234 let a_size: Vector3D = self_aabb.size();
235 let b_size: Vector3D = other_aabb.size();
236 let overlap_x: f64 =
237 (a_size.get_x() + b_size.get_x()) * 0.5 - (a_center.get_x() - b_center.get_x()).abs();
238 if overlap_x <= COLLIDER_CONTACT_EPSILON {
239 return None;
240 }
241 let overlap_y: f64 =
242 (a_size.get_y() + b_size.get_y()) * 0.5 - (a_center.get_y() - b_center.get_y()).abs();
243 if overlap_y <= COLLIDER_CONTACT_EPSILON {
244 return None;
245 }
246 let overlap_z: f64 =
247 (a_size.get_z() + b_size.get_z()) * 0.5 - (a_center.get_z() - b_center.get_z()).abs();
248 if overlap_z <= COLLIDER_CONTACT_EPSILON {
249 return None;
250 }
251 let (normal, depth) = if overlap_x <= overlap_y && overlap_x <= overlap_z {
252 let direction: f64 = if a_center.get_x() < b_center.get_x() {
253 -1.0
254 } else {
255 1.0
256 };
257 (Vector3D::new(direction, 0.0, 0.0), overlap_x)
258 } else if overlap_y <= overlap_z {
259 let direction: f64 = if a_center.get_y() < b_center.get_y() {
260 -1.0
261 } else {
262 1.0
263 };
264 (Vector3D::new(0.0, direction, 0.0), overlap_y)
265 } else {
266 let direction: f64 = if a_center.get_z() < b_center.get_z() {
267 -1.0
268 } else {
269 1.0
270 };
271 (Vector3D::new(0.0, 0.0, direction), overlap_z)
272 };
273 let self_min: Vector3D = self_aabb.get_min();
274 let self_max: Vector3D = self_aabb.get_max();
275 let other_min: Vector3D = other_aabb.get_min();
276 let other_max: Vector3D = other_aabb.get_max();
277 let contact_point: Vector3D = Vector3D::new(
278 self_min
279 .get_x()
280 .max(other_min.get_x())
281 .min(self_max.get_x().min(other_max.get_x())),
282 self_min
283 .get_y()
284 .max(other_min.get_y())
285 .min(self_max.get_y().min(other_max.get_y())),
286 self_min
287 .get_z()
288 .max(other_min.get_z())
289 .min(self_max.get_z().min(other_max.get_z())),
290 );
291 Some(CollisionResult3D::new(normal, depth, contact_point))
292 }
293
294 pub fn collide_with_sphere(&self, sphere: &SphereCollider3D) -> Option<CollisionResult3D> {
304 let self_aabb: AABB3D = self.get_aabb();
305 let sphere_inner: Sphere = sphere.get_sphere();
306 let aabb_min: Vector3D = self_aabb.get_min();
307 let aabb_max: Vector3D = self_aabb.get_max();
308 let closest_x: f64 = sphere_inner
309 .get_center()
310 .get_x()
311 .clamp(aabb_min.get_x(), aabb_max.get_x());
312 let closest_y: f64 = sphere_inner
313 .get_center()
314 .get_y()
315 .clamp(aabb_min.get_y(), aabb_max.get_y());
316 let closest_z: f64 = sphere_inner
317 .get_center()
318 .get_z()
319 .clamp(aabb_min.get_z(), aabb_max.get_z());
320 let closest: Vector3D = Vector3D::new(closest_x, closest_y, closest_z);
321 let delta: Vector3D = sphere_inner.get_center() - closest;
322 let distance_sq: f64 = delta.magnitude_squared();
323 if distance_sq >= sphere_inner.get_radius() * sphere_inner.get_radius() {
324 return None;
325 }
326 let distance: f64 = distance_sq.sqrt();
327 let normal: Vector3D = if distance < EPSILON {
328 let aabb_center: Vector3D = self_aabb.center();
329 let center_delta: Vector3D = sphere_inner.get_center() - aabb_center;
330 if center_delta.magnitude() < EPSILON {
331 Vector3D::up()
332 } else {
333 center_delta.normalized()
334 }
335 } else {
336 delta.scaled(1.0 / distance)
337 };
338 let depth: f64 = sphere_inner.get_radius() - distance;
339 let contact_point: Vector3D = closest;
340 Some(CollisionResult3D::new(normal, depth, contact_point))
341 }
342}
343
344impl Collider3D for AabbCollider3D {
346 fn shape(&self) -> ColliderShape3D {
347 ColliderShape3D::Aabb
348 }
349
350 fn bounding_box(&self) -> AABB3D {
351 self.get_aabb()
352 }
353
354 fn contains_point(&self, point: Vector3D) -> bool {
355 self.get_aabb().contains(point)
356 }
357
358 fn center(&self) -> Vector3D {
359 self.get_aabb().center()
360 }
361}
362
363impl SphereCollider3D {
365 pub fn from_center(center: Vector3D, radius: f64) -> SphereCollider3D {
376 SphereCollider3D::new(Sphere::new(center, radius))
377 }
378
379 pub fn collide_with_sphere(&self, other: &SphereCollider3D) -> Option<CollisionResult3D> {
389 let self_sphere: Sphere = self.get_sphere();
390 let other_sphere: Sphere = other.get_sphere();
391 let delta: Vector3D = other_sphere.get_center() - self_sphere.get_center();
392 let distance: f64 = delta.magnitude();
393 let radius_sum: f64 = self_sphere.get_radius() + other_sphere.get_radius();
394 if distance >= radius_sum {
395 return None;
396 }
397 let normal: Vector3D = if distance < EPSILON {
398 Vector3D::right()
399 } else {
400 delta.scaled(1.0 / distance)
401 };
402 let depth: f64 = radius_sum - distance;
403 let contact_point: Vector3D =
404 self_sphere.get_center() + normal.scaled(self_sphere.get_radius());
405 Some(CollisionResult3D::new(normal, depth, contact_point))
406 }
407}
408
409impl Collider3D for SphereCollider3D {
411 fn shape(&self) -> ColliderShape3D {
412 ColliderShape3D::Sphere
413 }
414
415 fn bounding_box(&self) -> AABB3D {
416 let sphere: Sphere = self.get_sphere();
417 let diameter: f64 = sphere.get_radius() * 2.0;
418 AABB3D::from_center(sphere.get_center(), diameter, diameter, diameter)
419 }
420
421 fn contains_point(&self, point: Vector3D) -> bool {
422 self.get_sphere().contains(point)
423 }
424
425 fn center(&self) -> Vector3D {
426 self.get_sphere().get_center()
427 }
428}
429
430impl AABB3D {
432 pub fn broad_phase(a: AABB3D, b: AABB3D) -> bool {
443 a.intersects(b)
444 }
445}