1use super::shapes::{
7 Box2D, Box3D, Circle, LineSegment2D, LineSegment3D, Sphere, Triangle2D, Triangle3D,
8};
9
10#[allow(dead_code)]
25pub fn point_circle_collision(point: &[f64; 2], circle: &Circle) -> bool {
26 let dx = point[0] - circle.center[0];
27 let dy = point[1] - circle.center[1];
28 let distance_squared = dx * dx + dy * dy;
29
30 distance_squared <= circle.radius * circle.radius
31}
32
33#[allow(dead_code)]
44pub fn point_box2d_collision(point: &[f64; 2], box2d: &Box2D) -> bool {
45 point[0] >= box2d.min[0]
46 && point[0] <= box2d.max[0]
47 && point[1] >= box2d.min[1]
48 && point[1] <= box2d.max[1]
49}
50
51#[allow(dead_code)]
62pub fn point_triangle2d_collision(point: &[f64; 2], triangle: &Triangle2D) -> bool {
63 let area = 0.5
65 * ((triangle.v2[0] - triangle.v1[0]) * (triangle.v3[1] - triangle.v1[1])
66 - (triangle.v3[0] - triangle.v1[0]) * (triangle.v2[1] - triangle.v1[1]))
67 .abs();
68
69 if area == 0.0 {
70 return false; }
72
73 let a = ((triangle.v2[0] - point[0]) * (triangle.v3[1] - point[1])
74 - (triangle.v3[0] - point[0]) * (triangle.v2[1] - point[1]))
75 .abs()
76 / (2.0 * area);
77
78 let b = ((triangle.v3[0] - point[0]) * (triangle.v1[1] - point[1])
79 - (triangle.v1[0] - point[0]) * (triangle.v3[1] - point[1]))
80 .abs()
81 / (2.0 * area);
82
83 let c = 1.0 - a - b;
84
85 const EPSILON: f64 = 1e-10;
88 a >= -EPSILON
89 && b >= -EPSILON
90 && c >= -EPSILON
91 && a <= 1.0 + EPSILON
92 && b <= 1.0 + EPSILON
93 && c <= 1.0 + EPSILON
94}
95
96#[allow(dead_code)]
111pub fn point_sphere_collision(point: &[f64; 3], sphere: &Sphere) -> bool {
112 let dx = point[0] - sphere.center[0];
113 let dy = point[1] - sphere.center[1];
114 let dz = point[2] - sphere.center[2];
115 let distance_squared = dx * dx + dy * dy + dz * dz;
116
117 distance_squared <= sphere.radius * sphere.radius
118}
119
120#[allow(dead_code)]
131pub fn point_box3d_collision(point: &[f64; 3], box3d: &Box3D) -> bool {
132 point[0] >= box3d.min[0]
133 && point[0] <= box3d.max[0]
134 && point[1] >= box3d.min[1]
135 && point[1] <= box3d.max[1]
136 && point[2] >= box3d.min[2]
137 && point[2] <= box3d.max[2]
138}
139
140#[allow(dead_code)]
151pub fn point_triangle3d_collision(point: &[f64; 3], triangle: &Triangle3D) -> bool {
152 let edge1 = [
157 triangle.v2[0] - triangle.v1[0],
158 triangle.v2[1] - triangle.v1[1],
159 triangle.v2[2] - triangle.v1[2],
160 ];
161
162 let edge2 = [
163 triangle.v3[0] - triangle.v1[0],
164 triangle.v3[1] - triangle.v1[1],
165 triangle.v3[2] - triangle.v1[2],
166 ];
167
168 let normal = [
170 edge1[1] * edge2[2] - edge1[2] * edge2[1],
171 edge1[2] * edge2[0] - edge1[0] * edge2[2],
172 edge1[0] * edge2[1] - edge1[1] * edge2[0],
173 ];
174
175 let normal_length =
177 (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
178 if normal_length == 0.0 {
179 return false; }
181
182 let normalized_normal = [
183 normal[0] / normal_length,
184 normal[1] / normal_length,
185 normal[2] / normal_length,
186 ];
187
188 let dist_to_plane = (point[0] - triangle.v1[0]) * normalized_normal[0]
190 + (point[1] - triangle.v1[1]) * normalized_normal[1]
191 + (point[2] - triangle.v1[2]) * normalized_normal[2];
192
193 const EPSILON: f64 = 1e-6;
195 if dist_to_plane.abs() > EPSILON {
196 return false; }
198
199 let max_component = if normalized_normal[0].abs() > normalized_normal[1].abs() {
202 if normalized_normal[0].abs() > normalized_normal[2].abs() {
203 0
204 } else {
205 2
206 }
207 } else if normalized_normal[1].abs() > normalized_normal[2].abs() {
208 1
209 } else {
210 2
211 };
212
213 let mut p1 = [0.0, 0.0];
215 let mut p2 = [0.0, 0.0];
216 let mut p3 = [0.0, 0.0];
217 let mut pp = [0.0, 0.0];
218
219 match max_component {
220 0 => {
221 p1[0] = triangle.v1[1];
223 p1[1] = triangle.v1[2];
224 p2[0] = triangle.v2[1];
225 p2[1] = triangle.v2[2];
226 p3[0] = triangle.v3[1];
227 p3[1] = triangle.v3[2];
228 pp[0] = point[1];
229 pp[1] = point[2];
230 }
231 1 => {
232 p1[0] = triangle.v1[0];
234 p1[1] = triangle.v1[2];
235 p2[0] = triangle.v2[0];
236 p2[1] = triangle.v2[2];
237 p3[0] = triangle.v3[0];
238 p3[1] = triangle.v3[2];
239 pp[0] = point[0];
240 pp[1] = point[2];
241 }
242 _ => {
243 p1[0] = triangle.v1[0];
245 p1[1] = triangle.v1[1];
246 p2[0] = triangle.v2[0];
247 p2[1] = triangle.v2[1];
248 p3[0] = triangle.v3[0];
249 p3[1] = triangle.v3[1];
250 pp[0] = point[0];
251 pp[1] = point[1];
252 }
253 }
254
255 let triangle2d = Triangle2D {
257 v1: p1,
258 v2: p2,
259 v3: p3,
260 };
261
262 point_triangle2d_collision(&pp, &triangle2d)
263}
264
265#[allow(dead_code)]
280pub fn circle_circle_collision(circle1: &Circle, circle2: &Circle) -> bool {
281 let dx = circle1.center[0] - circle2.center[0];
282 let dy = circle1.center[1] - circle2.center[1];
283 let distance_squared = dx * dx + dy * dy;
284 let sum_of_radii = circle1.radius + circle2.radius;
285
286 distance_squared <= sum_of_radii * sum_of_radii
287}
288
289#[allow(dead_code)]
300pub fn circle_box2d_collision(circle: &Circle, box2d: &Box2D) -> bool {
301 let closest_x = circle.center[0].max(box2d.min[0]).min(box2d.max[0]);
303 let closest_y = circle.center[1].max(box2d.min[1]).min(box2d.max[1]);
304
305 let dx = circle.center[0] - closest_x;
307 let dy = circle.center[1] - closest_y;
308 let distance_squared = dx * dx + dy * dy;
309
310 distance_squared <= circle.radius * circle.radius
312}
313
314#[allow(dead_code)]
325pub fn line2d_line2d_collision(line1: &LineSegment2D, line2: &LineSegment2D) -> bool {
326 let orientation = |p: &[f64; 2], q: &[f64; 2], r: &[f64; 2]| -> i32 {
328 let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
329
330 if val.abs() < 1e-10 {
331 0 } else if val > 0.0 {
333 1 } else {
335 2 }
337 };
338
339 let on_segment = |p: &[f64; 2], q: &[f64; 2], r: &[f64; 2]| -> bool {
341 q[0] <= p[0].max(r[0])
342 && q[0] >= p[0].min(r[0])
343 && q[1] <= p[1].max(r[1])
344 && q[1] >= p[1].min(r[1])
345 };
346
347 let o1 = orientation(&line1.start, &line1.end, &line2.start);
348 let o2 = orientation(&line1.start, &line1.end, &line2.end);
349 let o3 = orientation(&line2.start, &line2.end, &line1.start);
350 let o4 = orientation(&line2.start, &line2.end, &line1.end);
351
352 if o1 != o2 && o3 != o4 {
354 return true;
355 }
356
357 if o1 == 0 && on_segment(&line1.start, &line2.start, &line1.end) {
359 return true;
360 }
361 if o2 == 0 && on_segment(&line1.start, &line2.end, &line1.end) {
362 return true;
363 }
364 if o3 == 0 && on_segment(&line2.start, &line1.start, &line2.end) {
365 return true;
366 }
367 if o4 == 0 && on_segment(&line2.start, &line1.end, &line2.end) {
368 return true;
369 }
370
371 false
372}
373
374#[allow(dead_code)]
385pub fn box2d_box2d_collision(box1: &Box2D, box2: &Box2D) -> bool {
386 box1.min[0] <= box2.max[0]
388 && box1.max[0] >= box2.min[0]
389 && box1.min[1] <= box2.max[1]
390 && box1.max[1] >= box2.min[1]
391}
392
393#[allow(dead_code)]
404pub fn triangle2d_circle_collision(triangle: &Triangle2D, circle: &Circle) -> bool {
405 if point_triangle2d_collision(&circle.center, triangle) {
407 return true;
408 }
409
410 let edges = [
412 LineSegment2D {
413 start: triangle.v1,
414 end: triangle.v2,
415 },
416 LineSegment2D {
417 start: triangle.v2,
418 end: triangle.v3,
419 },
420 LineSegment2D {
421 start: triangle.v3,
422 end: triangle.v1,
423 },
424 ];
425
426 for edge in &edges {
427 let edge_vec = [edge.end[0] - edge.start[0], edge.end[1] - edge.start[1]];
429
430 let circle_vec = [
432 circle.center[0] - edge.start[0],
433 circle.center[1] - edge.start[1],
434 ];
435
436 let edge_length_squared = edge_vec[0] * edge_vec[0] + edge_vec[1] * edge_vec[1];
438
439 let t = (circle_vec[0] * edge_vec[0] + circle_vec[1] * edge_vec[1]) / edge_length_squared;
441
442 let t_clamped = t.clamp(0.0, 1.0);
444
445 let closest_point = [
447 edge.start[0] + t_clamped * edge_vec[0],
448 edge.start[1] + t_clamped * edge_vec[1],
449 ];
450
451 let dx = circle.center[0] - closest_point[0];
453 let dy = circle.center[1] - closest_point[1];
454 let distance_squared = dx * dx + dy * dy;
455
456 if distance_squared <= circle.radius * circle.radius {
458 return true;
459 }
460 }
461
462 false
463}
464
465#[allow(dead_code)]
480pub fn sphere_sphere_collision(sphere1: &Sphere, sphere2: &Sphere) -> bool {
481 let dx = sphere1.center[0] - sphere2.center[0];
482 let dy = sphere1.center[1] - sphere2.center[1];
483 let dz = sphere1.center[2] - sphere2.center[2];
484 let distance_squared = dx * dx + dy * dy + dz * dz;
485 let sum_of_radii = sphere1.radius + sphere2.radius;
486
487 distance_squared <= sum_of_radii * sum_of_radii
488}
489
490#[allow(dead_code)]
501pub fn sphere_box3d_collision(sphere: &Sphere, box3d: &Box3D) -> bool {
502 let closest_x = sphere.center[0].max(box3d.min[0]).min(box3d.max[0]);
504 let closest_y = sphere.center[1].max(box3d.min[1]).min(box3d.max[1]);
505 let closest_z = sphere.center[2].max(box3d.min[2]).min(box3d.max[2]);
506
507 let dx = sphere.center[0] - closest_x;
509 let dy = sphere.center[1] - closest_y;
510 let dz = sphere.center[2] - closest_z;
511 let distance_squared = dx * dx + dy * dy + dz * dz;
512
513 distance_squared <= sphere.radius * sphere.radius
515}
516
517#[allow(dead_code)]
528pub fn box3d_box3d_collision(box1: &Box3D, box2: &Box3D) -> bool {
529 box1.min[0] <= box2.max[0]
531 && box1.max[0] >= box2.min[0]
532 && box1.min[1] <= box2.max[1]
533 && box1.max[1] >= box2.min[1]
534 && box1.min[2] <= box2.max[2]
535 && box1.max[2] >= box2.min[2]
536}
537
538#[allow(dead_code)]
549pub fn line3d_line3d_collision(line1: &LineSegment3D, line2: &LineSegment3D) -> bool {
550 let d1 = [
555 line1.end[0] - line1.start[0],
556 line1.end[1] - line1.start[1],
557 line1.end[2] - line1.start[2],
558 ];
559
560 let d2 = [
561 line2.end[0] - line2.start[0],
562 line2.end[1] - line2.start[1],
563 line2.end[2] - line2.start[2],
564 ];
565
566 let r = [
568 line1.start[0] - line2.start[0],
569 line1.start[1] - line2.start[1],
570 line1.start[2] - line2.start[2],
571 ];
572
573 let a = d1[0] * d1[0] + d1[1] * d1[1] + d1[2] * d1[2]; let b = d1[0] * d2[0] + d1[1] * d2[1] + d1[2] * d2[2]; let c = d2[0] * d2[0] + d2[1] * d2[1] + d2[2] * d2[2]; let d = d1[0] * r[0] + d1[1] * r[1] + d1[2] * r[2]; let e = d2[0] * r[0] + d2[1] * r[1] + d2[2] * r[2]; if a < 1e-10 || c < 1e-10 {
582 return false;
583 }
584
585 let denom = a * c - b * b;
587
588 if denom.abs() < 1e-10 {
590 let cross_d1_d2 = [
594 d1[1] * d2[2] - d1[2] * d2[1],
595 d1[2] * d2[0] - d1[0] * d2[2],
596 d1[0] * d2[1] - d1[1] * d2[0],
597 ];
598
599 let dot_r_cross = r[0] * cross_d1_d2[0] + r[1] * cross_d1_d2[1] + r[2] * cross_d1_d2[2];
600
601 if dot_r_cross.abs() > 1e-10 {
603 return false;
604 }
605
606 let abs_d1 = [d1[0].abs(), d1[1].abs(), d1[2].abs()];
608 let max_component = if abs_d1[0] > abs_d1[1] {
609 if abs_d1[0] > abs_d1[2] {
610 0
611 } else {
612 2
613 }
614 } else if abs_d1[1] > abs_d1[2] {
615 1
616 } else {
617 2
618 };
619
620 let proj_line1_start = line1.start[max_component];
622 let proj_line1_end = line1.end[max_component];
623 let proj_line2_start = line2.start[max_component];
624 let proj_line2_end = line2.end[max_component];
625
626 let (min1, max1) = if proj_line1_start < proj_line1_end {
628 (proj_line1_start, proj_line1_end)
629 } else {
630 (proj_line1_end, proj_line1_start)
631 };
632
633 let (min2, max2) = if proj_line2_start < proj_line2_end {
634 (proj_line2_start, proj_line2_end)
635 } else {
636 (proj_line2_end, proj_line2_start)
637 };
638
639 return max1 >= min2 && max2 >= min1;
641 }
642
643 let mut s = (b * e - c * d) / denom;
645 let mut t = (a * e - b * d) / denom;
646
647 s = s.clamp(0.0, 1.0);
649 t = t.clamp(0.0, 1.0);
650
651 let closest1 = [
653 line1.start[0] + s * d1[0],
654 line1.start[1] + s * d1[1],
655 line1.start[2] + s * d1[2],
656 ];
657
658 let closest2 = [
659 line2.start[0] + t * d2[0],
660 line2.start[1] + t * d2[1],
661 line2.start[2] + t * d2[2],
662 ];
663
664 let dx = closest1[0] - closest2[0];
666 let dy = closest1[1] - closest2[1];
667 let dz = closest1[2] - closest2[2];
668 let distance_squared = dx * dx + dy * dy + dz * dz;
669
670 const EPSILON: f64 = 1e-10;
672 distance_squared < EPSILON
673}
674
675#[allow(dead_code)]
686pub fn sphere_triangle3d_collision(sphere: &Sphere, triangle: &Triangle3D) -> bool {
687 let edge1 = [
691 triangle.v2[0] - triangle.v1[0],
692 triangle.v2[1] - triangle.v1[1],
693 triangle.v2[2] - triangle.v1[2],
694 ];
695
696 let edge2 = [
697 triangle.v3[0] - triangle.v1[0],
698 triangle.v3[1] - triangle.v1[1],
699 triangle.v3[2] - triangle.v1[2],
700 ];
701
702 let normal = [
704 edge1[1] * edge2[2] - edge1[2] * edge2[1],
705 edge1[2] * edge2[0] - edge1[0] * edge2[2],
706 edge1[0] * edge2[1] - edge1[1] * edge2[0],
707 ];
708
709 let normal_length =
711 (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
712 if normal_length < 1e-10 {
713 return false; }
715
716 let normalized_normal = [
717 normal[0] / normal_length,
718 normal[1] / normal_length,
719 normal[2] / normal_length,
720 ];
721
722 let dist_to_plane = (sphere.center[0] - triangle.v1[0]) * normalized_normal[0]
724 + (sphere.center[1] - triangle.v1[1]) * normalized_normal[1]
725 + (sphere.center[2] - triangle.v1[2]) * normalized_normal[2];
726
727 if dist_to_plane.abs() > sphere.radius {
729 return false;
730 }
731
732 let projected_center = [
734 sphere.center[0] - dist_to_plane * normalized_normal[0],
735 sphere.center[1] - dist_to_plane * normalized_normal[1],
736 sphere.center[2] - dist_to_plane * normalized_normal[2],
737 ];
738
739 let inside_triangle = point_triangle3d_collision(&projected_center, triangle);
741
742 if inside_triangle {
743 return true;
745 }
746
747 let edges = [
749 LineSegment3D {
750 start: triangle.v1,
751 end: triangle.v2,
752 },
753 LineSegment3D {
754 start: triangle.v2,
755 end: triangle.v3,
756 },
757 LineSegment3D {
758 start: triangle.v3,
759 end: triangle.v1,
760 },
761 ];
762
763 for edge in &edges {
764 let edge_vec = [
766 edge.end[0] - edge.start[0],
767 edge.end[1] - edge.start[1],
768 edge.end[2] - edge.start[2],
769 ];
770
771 let sphere_vec = [
773 sphere.center[0] - edge.start[0],
774 sphere.center[1] - edge.start[1],
775 sphere.center[2] - edge.start[2],
776 ];
777
778 let edge_length_squared =
780 edge_vec[0] * edge_vec[0] + edge_vec[1] * edge_vec[1] + edge_vec[2] * edge_vec[2];
781
782 let t = (sphere_vec[0] * edge_vec[0]
784 + sphere_vec[1] * edge_vec[1]
785 + sphere_vec[2] * edge_vec[2])
786 / edge_length_squared;
787
788 let t_clamped = t.clamp(0.0, 1.0);
790
791 let closest_point = [
793 edge.start[0] + t_clamped * edge_vec[0],
794 edge.start[1] + t_clamped * edge_vec[1],
795 edge.start[2] + t_clamped * edge_vec[2],
796 ];
797
798 let dx = sphere.center[0] - closest_point[0];
800 let dy = sphere.center[1] - closest_point[1];
801 let dz = sphere.center[2] - closest_point[2];
802 let distance_squared = dx * dx + dy * dy + dz * dz;
803
804 if distance_squared <= sphere.radius * sphere.radius {
806 return true;
807 }
808 }
809
810 let vertices = [triangle.v1, triangle.v2, triangle.v3];
812
813 for &vertex in &vertices {
814 let dx = sphere.center[0] - vertex[0];
815 let dy = sphere.center[1] - vertex[1];
816 let dz = sphere.center[2] - vertex[2];
817 let distance_squared = dx * dx + dy * dy + dz * dz;
818
819 if distance_squared <= sphere.radius * sphere.radius {
820 return true;
821 }
822 }
823
824 false
825}
826
827#[allow(dead_code)]
843pub fn ray_sphere_collision(
844 ray_origin: &[f64; 3],
845 ray_direction: &[f64; 3],
846 sphere: &Sphere,
847) -> Option<(f64, [f64; 3])> {
848 let oc = [
850 ray_origin[0] - sphere.center[0],
851 ray_origin[1] - sphere.center[1],
852 ray_origin[2] - sphere.center[2],
853 ];
854
855 let a = ray_direction[0] * ray_direction[0]
860 + ray_direction[1] * ray_direction[1]
861 + ray_direction[2] * ray_direction[2];
862
863 let b = 2.0 * (ray_direction[0] * oc[0] + ray_direction[1] * oc[1] + ray_direction[2] * oc[2]);
865
866 let c = oc[0] * oc[0] + oc[1] * oc[1] + oc[2] * oc[2] - sphere.radius * sphere.radius;
868
869 let discriminant = b * b - 4.0 * a * c;
871
872 if discriminant < 0.0 {
873 None
875 } else {
876 let t = (-b - discriminant.sqrt()) / (2.0 * a);
878
879 if t < 0.0 {
881 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
883 if t2 < 0.0 {
884 None
885 } else {
886 let hit_point = [
888 ray_origin[0] + t2 * ray_direction[0],
889 ray_origin[1] + t2 * ray_direction[1],
890 ray_origin[2] + t2 * ray_direction[2],
891 ];
892 Some((t2, hit_point))
893 }
894 } else {
895 let hit_point = [
897 ray_origin[0] + t * ray_direction[0],
898 ray_origin[1] + t * ray_direction[1],
899 ray_origin[2] + t * ray_direction[2],
900 ];
901 Some((t, hit_point))
902 }
903 }
904}
905
906#[allow(dead_code)]
918pub fn ray_box3d_collision(
919 ray_origin: &[f64; 3],
920 ray_direction: &[f64; 3],
921 box3d: &Box3D,
922) -> Option<(f64, f64, [f64; 3])> {
923 let mut tmin = (box3d.min[0] - ray_origin[0]) / ray_direction[0];
925 let mut tmax = (box3d.max[0] - ray_origin[0]) / ray_direction[0];
926
927 if tmin > tmax {
928 std::mem::swap(&mut tmin, &mut tmax);
929 }
930
931 let mut tymin = (box3d.min[1] - ray_origin[1]) / ray_direction[1];
932 let mut tymax = (box3d.max[1] - ray_origin[1]) / ray_direction[1];
933
934 if tymin > tymax {
935 std::mem::swap(&mut tymin, &mut tymax);
936 }
937
938 if tmin > tymax || tymin > tmax {
939 return None;
940 }
941
942 if tymin > tmin {
943 tmin = tymin;
944 }
945
946 if tymax < tmax {
947 tmax = tymax;
948 }
949
950 let mut tzmin = (box3d.min[2] - ray_origin[2]) / ray_direction[2];
951 let mut tzmax = (box3d.max[2] - ray_origin[2]) / ray_direction[2];
952
953 if tzmin > tzmax {
954 std::mem::swap(&mut tzmin, &mut tzmax);
955 }
956
957 if tmin > tzmax || tzmin > tmax {
958 return None;
959 }
960
961 if tzmin > tmin {
962 tmin = tzmin;
963 }
964
965 if tzmax < tmax {
966 tmax = tzmax;
967 }
968
969 if tmax < 0.0 {
971 return None;
972 }
973
974 let t = if tmin < 0.0 { tmax } else { tmin };
976
977 let hit_point = [
979 ray_origin[0] + t * ray_direction[0],
980 ray_origin[1] + t * ray_direction[1],
981 ray_origin[2] + t * ray_direction[2],
982 ];
983
984 Some((tmin, tmax, hit_point))
985}
986
987#[allow(dead_code)]
999pub fn ray_triangle3d_collision(
1000 ray_origin: &[f64; 3],
1001 ray_direction: &[f64; 3],
1002 triangle: &Triangle3D,
1003) -> Option<(f64, [f64; 3], [f64; 3])> {
1004 let edge1 = [
1008 triangle.v2[0] - triangle.v1[0],
1009 triangle.v2[1] - triangle.v1[1],
1010 triangle.v2[2] - triangle.v1[2],
1011 ];
1012
1013 let edge2 = [
1014 triangle.v3[0] - triangle.v1[0],
1015 triangle.v3[1] - triangle.v1[1],
1016 triangle.v3[2] - triangle.v1[2],
1017 ];
1018
1019 let h = [
1021 ray_direction[1] * edge2[2] - ray_direction[2] * edge2[1],
1022 ray_direction[2] * edge2[0] - ray_direction[0] * edge2[2],
1023 ray_direction[0] * edge2[1] - ray_direction[1] * edge2[0],
1024 ];
1025
1026 let a = edge1[0] * h[0] + edge1[1] * h[1] + edge1[2] * h[2];
1028
1029 if a.abs() < 1e-10 {
1031 return None;
1032 }
1033
1034 let f = 1.0 / a;
1036
1037 let s = [
1039 ray_origin[0] - triangle.v1[0],
1040 ray_origin[1] - triangle.v1[1],
1041 ray_origin[2] - triangle.v1[2],
1042 ];
1043
1044 let u = f * (s[0] * h[0] + s[1] * h[1] + s[2] * h[2]);
1046
1047 if !(0.0..=1.0).contains(&u) {
1049 return None;
1050 }
1051
1052 let q = [
1054 s[1] * edge1[2] - s[2] * edge1[1],
1055 s[2] * edge1[0] - s[0] * edge1[2],
1056 s[0] * edge1[1] - s[1] * edge1[0],
1057 ];
1058
1059 let v = f * (ray_direction[0] * q[0] + ray_direction[1] * q[1] + ray_direction[2] * q[2]);
1061
1062 if v < 0.0 || u + v > 1.0 {
1064 return None;
1065 }
1066
1067 let t = f * (edge2[0] * q[0] + edge2[1] * q[1] + edge2[2] * q[2]);
1069
1070 if t < 0.0 {
1072 return None;
1073 }
1074
1075 let hit_point = [
1077 ray_origin[0] + t * ray_direction[0],
1078 ray_origin[1] + t * ray_direction[1],
1079 ray_origin[2] + t * ray_direction[2],
1080 ];
1081
1082 let barycentric = [u, v, 1.0 - u - v];
1084
1085 Some((t, hit_point, barycentric))
1086}
1087
1088pub trait GJKShape {
1096 fn support(&self, direction: &[f64; 3]) -> [f64; 3];
1098}
1099
1100impl GJKShape for Sphere {
1102 fn support(&self, direction: &[f64; 3]) -> [f64; 3] {
1103 let length = (direction[0] * direction[0]
1105 + direction[1] * direction[1]
1106 + direction[2] * direction[2])
1107 .sqrt();
1108 if length < 1e-10 {
1109 return self.center;
1110 }
1111
1112 let normalized = [
1113 direction[0] / length,
1114 direction[1] / length,
1115 direction[2] / length,
1116 ];
1117
1118 [
1119 self.center[0] + self.radius * normalized[0],
1120 self.center[1] + self.radius * normalized[1],
1121 self.center[2] + self.radius * normalized[2],
1122 ]
1123 }
1124}
1125
1126impl GJKShape for Box3D {
1128 fn support(&self, direction: &[f64; 3]) -> [f64; 3] {
1129 [
1130 if direction[0] >= 0.0 {
1131 self.max[0]
1132 } else {
1133 self.min[0]
1134 },
1135 if direction[1] >= 0.0 {
1136 self.max[1]
1137 } else {
1138 self.min[1]
1139 },
1140 if direction[2] >= 0.0 {
1141 self.max[2]
1142 } else {
1143 self.min[2]
1144 },
1145 ]
1146 }
1147}
1148
1149#[derive(Debug, Clone)]
1151struct GJKSimplex {
1152 points: Vec<[f64; 3]>,
1153}
1154
1155impl GJKSimplex {
1156 fn new() -> Self {
1157 Self {
1158 points: Vec::with_capacity(4),
1159 }
1160 }
1161
1162 fn add_point(&mut self, point: [f64; 3]) {
1163 self.points.push(point);
1164 }
1165
1166 fn size(&self) -> usize {
1167 self.points.len()
1168 }
1169
1170 fn get_point(&self, index: usize) -> Option<[f64; 3]> {
1171 self.points.get(index).copied()
1172 }
1173
1174 #[allow(dead_code)]
1175 fn clear(&mut self) {
1176 self.points.clear();
1177 }
1178
1179 fn set_points(&mut self, points: Vec<[f64; 3]>) {
1180 self.points = points;
1181 }
1182}
1183
1184#[allow(dead_code)]
1186fn support_minkowski_difference<T1: GJKShape, T2: GJKShape>(
1187 shape1: &T1,
1188 shape2: &T2,
1189 direction: &[f64; 3],
1190) -> [f64; 3] {
1191 let support1 = shape1.support(direction);
1192 let neg_direction = [-direction[0], -direction[1], -direction[2]];
1193 let support2 = shape2.support(&neg_direction);
1194
1195 [
1196 support1[0] - support2[0],
1197 support1[1] - support2[1],
1198 support1[2] - support2[2],
1199 ]
1200}
1201
1202#[allow(dead_code)]
1204fn dot_product(a: &[f64; 3], b: &[f64; 3]) -> f64 {
1205 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
1206}
1207
1208#[allow(dead_code)]
1209fn cross_product(a: &[f64; 3], b: &[f64; 3]) -> [f64; 3] {
1210 [
1211 a[1] * b[2] - a[2] * b[1],
1212 a[2] * b[0] - a[0] * b[2],
1213 a[0] * b[1] - a[1] * b[0],
1214 ]
1215}
1216
1217#[allow(dead_code)]
1218fn subtract_vectors(a: &[f64; 3], b: &[f64; 3]) -> [f64; 3] {
1219 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
1220}
1221
1222#[allow(dead_code)]
1223fn negate_vector(a: &[f64; 3]) -> [f64; 3] {
1224 [-a[0], -a[1], -a[2]]
1225}
1226
1227#[allow(dead_code)]
1229fn handle_line_simplex(simplex: &mut GJKSimplex, direction: &mut [f64; 3]) -> bool {
1230 let a = simplex.get_point(1).unwrap(); let b = simplex.get_point(0).unwrap(); let ab = subtract_vectors(&b, &a);
1234 let ao = negate_vector(&a);
1235
1236 if dot_product(&ab, &ao) > 0.0 {
1237 *direction = cross_product(&cross_product(&ab, &ao), &ab);
1239 } else {
1240 simplex.set_points(vec![a]);
1242 *direction = ao;
1243 }
1244
1245 false }
1247
1248#[allow(dead_code)]
1250fn handle_triangle_simplex(simplex: &mut GJKSimplex, direction: &mut [f64; 3]) -> bool {
1251 let a = simplex.get_point(2).unwrap(); let b = simplex.get_point(1).unwrap();
1253 let c = simplex.get_point(0).unwrap();
1254
1255 let ab = subtract_vectors(&b, &a);
1256 let ac = subtract_vectors(&c, &a);
1257 let ao = negate_vector(&a);
1258
1259 let abc = cross_product(&ab, &ac);
1260
1261 if dot_product(&cross_product(&abc, &ac), &ao) > 0.0 {
1262 if dot_product(&ac, &ao) > 0.0 {
1263 simplex.set_points(vec![c, a]);
1265 *direction = cross_product(&cross_product(&ac, &ao), &ac);
1266 } else {
1267 return handle_line_simplex_from_points(simplex, direction, a, b);
1268 }
1269 } else if dot_product(&cross_product(&ab, &abc), &ao) > 0.0 {
1270 return handle_line_simplex_from_points(simplex, direction, a, b);
1271 } else if dot_product(&abc, &ao) > 0.0 {
1272 *direction = abc;
1274 } else {
1275 simplex.set_points(vec![b, c, a]);
1277 *direction = negate_vector(&abc);
1278 }
1279
1280 false }
1282
1283#[allow(dead_code)]
1285fn handle_line_simplex_from_points(
1286 simplex: &mut GJKSimplex,
1287 direction: &mut [f64; 3],
1288 a: [f64; 3],
1289 b: [f64; 3],
1290) -> bool {
1291 let ab = subtract_vectors(&b, &a);
1292 let ao = negate_vector(&a);
1293
1294 if dot_product(&ab, &ao) > 0.0 {
1295 simplex.set_points(vec![b, a]);
1296 *direction = cross_product(&cross_product(&ab, &ao), &ab);
1297 } else {
1298 simplex.set_points(vec![a]);
1299 *direction = ao;
1300 }
1301
1302 false
1303}
1304
1305#[allow(dead_code)]
1307fn handle_tetrahedron_simplex(simplex: &mut GJKSimplex, direction: &mut [f64; 3]) -> bool {
1308 let a = simplex.get_point(3).unwrap(); let b = simplex.get_point(2).unwrap();
1310 let c = simplex.get_point(1).unwrap();
1311 let d = simplex.get_point(0).unwrap();
1312
1313 let ab = subtract_vectors(&b, &a);
1314 let ac = subtract_vectors(&c, &a);
1315 let ad = subtract_vectors(&d, &a);
1316 let ao = negate_vector(&a);
1317
1318 let abc = cross_product(&ab, &ac);
1319 let acd = cross_product(&ac, &ad);
1320 let adb = cross_product(&ad, &ab);
1321
1322 if dot_product(&abc, &ao) > 0.0 {
1324 simplex.set_points(vec![c, b, a]);
1325 return handle_triangle_simplex(simplex, direction);
1326 }
1327
1328 if dot_product(&acd, &ao) > 0.0 {
1329 simplex.set_points(vec![d, c, a]);
1330 return handle_triangle_simplex(simplex, direction);
1331 }
1332
1333 if dot_product(&adb, &ao) > 0.0 {
1334 simplex.set_points(vec![b, d, a]);
1335 return handle_triangle_simplex(simplex, direction);
1336 }
1337
1338 true
1340}
1341
1342#[allow(dead_code)]
1344fn handle_simplex(simplex: &mut GJKSimplex, direction: &mut [f64; 3]) -> bool {
1345 match simplex.size() {
1346 2 => handle_line_simplex(simplex, direction),
1347 3 => handle_triangle_simplex(simplex, direction),
1348 4 => handle_tetrahedron_simplex(simplex, direction),
1349 _ => false,
1350 }
1351}
1352
1353#[allow(dead_code)]
1384pub fn gjk_collision_detection<T1: GJKShape, T2: GJKShape>(
1385 shape1: &T1,
1386 shape2: &T2,
1387 max_iterations: usize,
1388) -> bool {
1389 let mut direction = [1.0, 0.0, 0.0];
1391
1392 let initial_support = support_minkowski_difference(shape1, shape2, &direction);
1394
1395 if dot_product(&initial_support, &direction) <= 0.0 {
1397 return false;
1398 }
1399
1400 let mut simplex = GJKSimplex::new();
1401 simplex.add_point(initial_support);
1402
1403 direction = negate_vector(&initial_support);
1405
1406 for _ in 0..max_iterations {
1407 let support_point = support_minkowski_difference(shape1, shape2, &direction);
1408
1409 if dot_product(&support_point, &direction) <= 0.0 {
1411 return false;
1412 }
1413
1414 simplex.add_point(support_point);
1415
1416 if handle_simplex(&mut simplex, &mut direction) {
1418 return true; }
1420 }
1421
1422 false }
1424
1425#[allow(dead_code)]
1436pub fn gjk_sphere_sphere_collision(sphere1: &Sphere, sphere2: &Sphere) -> bool {
1437 gjk_collision_detection(sphere1, sphere2, 64)
1438}
1439
1440#[allow(dead_code)]
1451pub fn gjk_box_box_collision(box1: &Box3D, box2: &Box3D) -> bool {
1452 gjk_collision_detection(box1, box2, 64)
1453}
1454
1455#[allow(dead_code)]
1466pub fn gjk_sphere_box_collision(sphere: &Sphere, bbox: &Box3D) -> bool {
1467 gjk_collision_detection(sphere, bbox, 64)
1468}