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();