diff --git a/OpenNest.Tests/CollisionTests.cs b/OpenNest.Tests/CollisionTests.cs index 12074cb..153dd2c 100644 --- a/OpenNest.Tests/CollisionTests.cs +++ b/OpenNest.Tests/CollisionTests.cs @@ -1,5 +1,6 @@ using OpenNest.Geometry; using OpenNest.Math; +using System.Collections.Generic; namespace OpenNest.Tests; @@ -85,6 +86,99 @@ public class CollisionTests Assert.True(result.OverlapArea > 0.49 && result.OverlapArea < 0.51); } + /// + /// Square A has a hole. Square B overlaps only the hole area. + /// This should NOT be a collision — B fits inside A's cutout. + /// + [Fact] + public void Check_OverlapInsideHole_ReturnsNone() + { + var a = MakeSquare(0, 0, 4, 4); + var holeA = new List { MakeSquare(1, 1, 3, 3) }; + + // B fits entirely inside the hole + var b = MakeSquare(1.5, 1.5, 2.5, 2.5); + + var result = Collision.Check(a, b, holesA: holeA); + + Assert.False(result.Overlaps); + } + + /// + /// Square A has a hole. Square B partially overlaps the hole and + /// partially overlaps solid material. Should still be a collision. + /// + [Fact] + public void Check_PartialOverlapWithHole_StillOverlaps() + { + var a = MakeSquare(0, 0, 4, 4); + var holeA = new List { MakeSquare(1, 1, 3, 3) }; + + // B extends beyond the hole into solid material + var b = MakeSquare(2, 2, 5, 5); + + var result = Collision.Check(a, b, holesA: holeA); + + // Hole subtraction uses a conservative approach (keeps partial overlaps), + // so we only verify that a collision is still detected for solid material. + Assert.True(result.Overlaps); + } + + /// + /// HasOverlap with holes returns false when overlap is inside cutout. + /// + [Fact] + public void HasOverlap_InsideHole_ReturnsFalse() + { + var a = MakeSquare(0, 0, 4, 4); + var holeA = new List { MakeSquare(1, 1, 3, 3) }; + var b = MakeSquare(1.5, 1.5, 2.5, 2.5); + + Assert.False(Collision.HasOverlap(a, b, holesA: holeA)); + } + + [Fact] + public void CheckAll_MultiplePolygons_FindsAllOverlaps() + { + var a = MakeSquare(0, 0, 1, 1); + var b = MakeSquare(0.5, 0, 1.5, 1); // overlaps A + var c = MakeSquare(5, 5, 6, 6); // overlaps nobody + + var results = Collision.CheckAll(new List { a, b, c }); + + Assert.Single(results); + Assert.True(results[0].Overlaps); + } + + [Fact] + public void CheckAll_NoOverlaps_ReturnsEmpty() + { + var a = MakeSquare(0, 0, 1, 1); + var b = MakeSquare(3, 3, 4, 4); + + var results = Collision.CheckAll(new List { a, b }); + + Assert.Empty(results); + } + + [Fact] + public void HasAnyOverlap_WithOverlap_ReturnsTrue() + { + var a = MakeSquare(0, 0, 1, 1); + var b = MakeSquare(0.5, 0, 1.5, 1); + + Assert.True(Collision.HasAnyOverlap(new List { a, b })); + } + + [Fact] + public void HasAnyOverlap_NoOverlap_ReturnsFalse() + { + var a = MakeSquare(0, 0, 1, 1); + var b = MakeSquare(3, 3, 4, 4); + + Assert.False(Collision.HasAnyOverlap(new List { a, b })); + } + private static Polygon MakeSquare(double left, double bottom, double right, double top) { var p = new Polygon();