1#![allow(clippy::needless_return)]
158
159pub mod polygon;
160pub mod circle;
161pub mod aabb;
162pub mod capsule;
163pub mod parallelogram;
164
165pub mod line;
166
167pub trait Shape
170{
171
172 fn position(&self) -> (f32, f32);
174
175 fn set_position(&mut self, position: (f32, f32));
177
178 fn num_axes(&self) -> usize;
181
182 fn get_axis(&self, index: usize, target: (f32, f32)) -> (f32, f32);
186
187 fn project(&self, axis: (f32, f32), normalize: bool) -> (f32, f32);
191
192 fn needs_closest(&self, index: usize) -> bool;
195
196 fn get_closest(&self, target: (f32, f32)) -> (f32, f32);
199
200 fn point(&self, index: usize) -> (f32, f32);
202
203}
204
205pub trait Rotate
208{
209
210 fn rotate(&mut self, angle: f32);
213
214 fn rotate_sincos(&mut self, sin: f32, cos: f32);
218
219}
220
221#[macro_export]
223macro_rules! rotate
224{
225
226 ($s: expr, $c: expr, $v: expr) =>
227 {
228
229 ($c * $v.0 - $s * $v.1, $s * $v.0 + $c * $v.1 )
230
231 };
232
233}
234
235pub fn sat_overlap(left: &(impl Shape + ?Sized), right: &(impl Shape + ?Sized)) -> bool
254{
255
256 if !shape_overlap(left, right, false).0
257 {
258
259 return false;
260
261 }
262
263 if !shape_overlap(right, left, false).0
264 {
265
266 return false;
267
268 }
269
270 return true;
271
272}
273
274pub fn sat_collision(left: &(impl Shape + ?Sized), right: &(impl Shape + ?Sized)) -> (f32, f32)
309{
310
311 let l_overlap = shape_overlap(left, right, true);
312 let r_overlap = shape_overlap(right, left, true);
313
314 let r_flipped = (true, r_overlap.1, (-r_overlap.2.0, -r_overlap.2.1));
316
317 let overlap = if l_overlap.1 < r_flipped.1 { l_overlap } else { r_flipped };
318
319 return (overlap.1 * overlap.2.0, overlap.1 * overlap.2.1);
320
321}
322
323pub fn contains_point(shape: &(impl Shape + ?Sized), point: (f32, f32)) -> bool
343{
344
345 let polygon = polygon::Polygon::from_vertices(point, vec![(0.0, 0.0)]);
346
347 return shape_overlap(shape, &polygon, false).0;
348
349}
350
351fn shape_overlap(axes: &(impl Shape + ?Sized), projected: &(impl Shape + ?Sized), normalize: bool) -> (bool, f32, (f32, f32))
352{
353
354 let mut min_overlap = f32::MAX;
355 let mut min_axis = (0.0, 0.0);
356
357 let num_axes = axes.num_axes();
358 for i in 0..num_axes
359 {
360
361 let closest = if axes.needs_closest(i) { projected.get_closest(axes.point(i)) } else { (0.0, 0.0) };
362 let mut axis = axes.get_axis(i, closest);
363
364 if normalize
367 {
368
369 let length = f32::sqrt((axis.0 * axis.0) + (axis.1 * axis.1));
370
371 if length > f32::EPSILON
372 {
373
374 axis = (axis.0 / length, axis.1 / length);
375
376 }
377
378 }
379
380 let (min_l, max_l) = axes.project(axis, normalize);
381 let (min_r, max_r) = projected.project(axis, normalize);
382
383 if min_l > max_r - f32::EPSILON || min_r > max_l - f32::EPSILON
385 {
386
387 return (false, 0.0, (0.0, 0.0));
388
389 }
390
391 let overlap = f32::min(max_l - min_r, max_r - min_l);
392 if overlap < min_overlap
393 {
394
395 min_overlap = overlap;
396 min_axis = axis;
397
398 }
399
400 }
401
402 let axes_position = axes.position();
404 let projected_position = projected.position();
405 let difference = (projected_position.0 - axes_position.0, projected_position.1 - axes_position.1);
406 if (difference.0 * min_axis.0 + difference.1 * min_axis.1) < -f32::EPSILON
407 {
408
409 min_axis.0 *= -1.0;
410 min_axis.1 *= -1.0;
411
412 }
413
414 return (true, min_overlap, min_axis);
415
416}
417
418fn project(position: (f32, f32), axis: (f32, f32), points: &[(f32, f32)]) -> (f32, f32)
419{
420
421 let mut min = f32::MAX;
422 let mut max = f32::MIN;
423
424 for (x, y) in points
425 {
426
427 let position = (position.0 + *x, position.1 + *y);
428
429 let projection = (position.0 * axis.0) + (position.1 * axis.1);
430
431 min = f32::min(min, projection);
432 max = f32::max(max, projection);
433
434 }
435
436 return (min, max);
437
438}
439
440fn closest(position: (f32, f32), target: (f32, f32), points: &[(f32, f32)]) -> (f32, f32)
441{
442
443 let mut point = (0.0, 0.0);
444 let mut min = f32::MAX;
445
446 for (x, y) in points
447 {
448
449 let position = (position.0 + *x, position.1 + *y);
450 let dist_square = (position.0 - target.0) * (position.0 - target.0) + (position.1 - target.1) * (position.1 - target.1);
451
452 if dist_square < min
453 {
454
455 point = position;
456 min = dist_square;
457
458 }
459
460 }
461
462 return point;
463
464}
465
466#[allow(dead_code)]
467fn float_equal(left: f32, right: f32) -> bool
468{
469
470 return (left - right).abs() < 0.00001;
471
472}
473
474#[cfg(test)]
475mod sat_tests
476{
477
478 use super::*;
479 use super::prelude::*;
480
481 #[test]
482 fn test_sat_overlap()
483 {
484
485 let square = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]);
487 let triangle = Polygon::from_vertices((2.0, 2.0), vec![(-1.0, 1.0), (0.0, -1.0), (1.0, 1.0)]);
488 let pentagon = Polygon::from_vertices((-3.0, 0.0), vec![(2.0, 0.0), (4.0, 1.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]);
489 let triangle2 = Polygon::from_vertices((4.0, 3.0), vec![(-2.0, 1.0), (-1.0, -2.0), (2.0, 0.0)]);
490
491 assert!(sat_overlap(&pentagon, &pentagon));
492 assert!(sat_overlap(&square, &triangle));
493 assert!(sat_overlap(&triangle, &square));
494 assert!(sat_overlap(&square, &pentagon));
495 assert!(sat_overlap(&pentagon, &square));
496 assert!(sat_overlap(&square, &triangle2));
497 assert!(sat_overlap(&triangle2, &square));
498 assert!(sat_overlap(&triangle, &triangle2));
499 assert!(sat_overlap(&triangle, &triangle2));
500
501 let circle1 = Circle::new((-1.0, -1.0), 2.0);
503 let circle2 = Circle::new((-2.0, -1.0), 1.1);
504
505 assert!(sat_overlap(&circle1, &square));
506 assert!(sat_overlap(&circle2, &pentagon));
507 assert!(sat_overlap(&pentagon, &circle1));
508 assert!(sat_overlap(&circle1, &circle2));
509
510 }
511
512 #[test]
513 fn test_sat_no_overlap()
514 {
515
516 let triangle = Polygon::from_vertices((2.0, 2.0), vec![(-1.0, 1.0), (0.0, -1.0), (1.0, 1.0)]);
518 let pentagon = Polygon::from_vertices((-3.0, 0.0), vec![(2.0, 0.0), (4.0, 1.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]);
519 let triangle2 = Polygon::from_vertices((4.0, 3.0), vec![(-2.0, 1.0), (-1.0, -2.0), (2.0, 0.0)]);
520
521 assert!(!sat_overlap(&pentagon, &triangle));
522 assert!(!sat_overlap(&triangle, &pentagon));
523 assert!(!sat_overlap(&pentagon, &triangle2));
524 assert!(!sat_overlap(&triangle2, &pentagon));
525
526 let circle1 = Circle::new((-1.0, -1.0), 2.0);
528 let circle2 = Circle::new((-2.0, -1.0), 1.1);
529 let circle3 = Circle::new((2.0, 5.0), 1.0);
530
531 assert!(!sat_overlap(&triangle, &circle1));
532 assert!(!sat_overlap(&circle2, &triangle2));
533 assert!(!sat_overlap(&circle1, &circle3));
534 assert!(!sat_overlap(&circle3, &circle2));
535
536 }
537
538 #[test]
539 fn test_sat_collision()
540 {
541
542 let square = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]);
544 let rectangle = Polygon::from_vertices((1.0, -2.0), vec![(0.0, 0.0), (1.0, 0.0), (1.0, 2.1), (0.0, 2.1)]);
545 let triangle = Polygon::from_vertices((0.0, 0.0), vec![(0.5, 0.6), (0.0, -1.0), (-0.5, 0.6)]);
546 let triangle2 = Polygon::from_vertices((-0.4, -0.4), vec![(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]);
547
548 let resolution = sat_collision(&square, &rectangle);
549 assert!(float_equal(resolution.0, 0.0));
550 assert!(float_equal(resolution.1, -0.1));
551
552 let resolution1 = sat_collision(&rectangle, &square);
553 assert!(float_equal(resolution1.0, 0.0));
554 assert!(float_equal(resolution1.1, 0.1));
555
556 let resolution2 = sat_collision(&square, &triangle);
557 assert!(float_equal(resolution2.0, -0.5));
558 assert!(float_equal(resolution2.1, 0.0));
559
560 let resolution3 = sat_collision(&square, &triangle2);
561 assert!(float_equal((resolution3.0 * -1.0) + (resolution3.1), 0.0)); let circle = Circle::new((2.0, -0.5), 1.0);
565 let circle2 = Circle::new((0.0, -0.5), 1.1);
566
567 let resolution4 = sat_collision(&circle, &square);
568 assert!(float_equal(resolution4.0, 0.0));
569 assert!(float_equal(resolution4.1, 0.5));
570
571 let resolution5 = sat_collision(&circle, &triangle2);
572 assert!(float_equal(resolution5.0, 0.0));
573 assert!(float_equal(resolution5.1, 0.0));
574
575 let resolution6 = sat_collision(&circle2, &circle);
576 assert!(float_equal(resolution6.0, 0.1));
577 assert!(float_equal(resolution6.1, 0.0));
578
579 }
580
581 #[test]
582 fn test_capsule_collision()
583 {
584
585 let capsule = Capsule::new((0.0, 0.0), (0.0, 2.0), 2.0);
586
587 let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
588 let rectangle = AABB::new((-4.0, 4.0), 2.5, 0.5);
589 let circle1 = Circle::new((2.0, -2.5), 1.0);
590 let circle2 = Circle::new((3.0, 0.0), 1.5);
591
592 assert!(sat_overlap(&capsule, &circle1));
593 assert!(!sat_overlap(&rectangle, &capsule));
594
595 let resolution1 = sat_collision(&circle2, &capsule);
596 let resolution2 = sat_collision(&capsule, &triangle);
597
598 assert!(float_equal(resolution1.0, -0.5));
599 assert!(float_equal(resolution1.1, 0.0));
600 assert!(float_equal(resolution2.0, 0.0));
601 assert!(float_equal(resolution2.1, 1.0));
602
603 }
604
605 #[test]
606 fn test_parallelogram_collision()
607 {
608
609 let gram = Parallelogram::new((0.0, -0.5), (2.0, 1.0), (-1.0, -1.0));
610
611 let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
612 let rectangle = AABB::new((-1.0, 0.0), 4.0, 2.0);
613 let circle = Circle::new((2.0, -2.5), 1.0);
614
615 assert!(!sat_overlap(&gram, &triangle));
616 assert!(!sat_overlap(&gram, &circle));
617
618 assert!(sat_overlap(&gram, &rectangle));
619
620 let resolution = sat_collision(&rectangle, &gram);
621 println!("{:?}", resolution);
622
623 assert!(float_equal(resolution.0, 0.0));
624 assert!(float_equal(resolution.1, -0.5));
625
626 }
627
628 #[test]
629 fn test_contains_point()
630 {
631
632 let capsule = Capsule::new((0.0, 0.0), (0.0, 2.0), 2.0);
633 let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
634 let rectangle = AABB::new((-4.0, 4.0), 2.5, 0.5);
635 let circle = Circle::new((2.0, -2.5), 1.0);
636
637 assert!(contains_point(&capsule, (1.0, 1.0)));
638 assert!(contains_point(&triangle, (0.0, 5.0)));
639 assert!(contains_point(&rectangle, (-2.0, 4.1)));
640 assert!(contains_point(&circle, (2.5, -3.0)));
641
642
643 assert!(!contains_point(&capsule, (2.0, 4.0)));
644 assert!(!contains_point(&triangle, (0.0, 0.0)));
645 assert!(!contains_point(&rectangle, (-4.0, 3.9)));
646 assert!(!contains_point(&circle, (1.5, -3.5)));
647
648 }
649
650 #[test]
651 fn test_rotate()
652 {
653
654 let mut triangle = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (2.0, 0.0), (0.0, 1.0)]);
655 let mut capsule = Capsule::new((2.0, 1.0), (2.0, 0.0), 4.0);
656 let mut gram = Parallelogram::new((3.0, 4.0), (2.0, 1.0), (-1.0, 1.0));
657
658 capsule.rotate(std::f32::consts::FRAC_PI_4);
659
660 assert!(float_equal(capsule.arm().0, 2.0 / f32::sqrt(2.0)));
661 assert!(float_equal(capsule.arm().1, 2.0 / f32::sqrt(2.0)));
662 assert!(float_equal(capsule.perp().0, -4.0 / f32::sqrt(2.0)));
663 assert!(float_equal(capsule.perp().1, 4.0 / f32::sqrt(2.0)));
664
665 let sin = f32::sin(std::f32::consts::PI);
666 let cos = f32::cos(std::f32::consts::PI);
667
668 triangle.rotate_sincos(sin, cos);
669
670 assert!(float_equal(triangle.vertices[1].0, -2.0));
671 assert!(float_equal(triangle.vertices[1].1, 0.0));
672 assert!(float_equal(triangle.vertices[2].0, 0.0));
673 assert!(float_equal(triangle.vertices[2].1, -1.0));
674
675 gram.rotate_sincos(sin, cos);
676
677 assert!(float_equal(gram.u.0, -2.0));
678 assert!(float_equal(gram.u.1, -1.0));
679 assert!(float_equal(gram.v.0, 1.0));
680 assert!(float_equal(gram.v.1, -1.0));
681
682 }
683
684}
685
686pub mod prelude
687{
688
689 pub use crate::{sat_overlap, sat_collision, contains_point};
690
691 pub use crate::Shape;
692 pub use crate::Rotate;
693
694 pub use crate::polygon::Polygon;
695 pub use crate::circle::Circle;
696 pub use crate::aabb::AABB;
697 pub use crate::capsule::Capsule;
698 pub use crate::parallelogram::Parallelogram;
699
700 pub use crate::line::intersects_line;
701 pub use crate::line::intersects_ray;
702 pub use crate::line::intersects_segment;
703
704}