diff --git a/OpenNest.Tests/RemnantFinderTests.cs b/OpenNest.Tests/RemnantFinderTests.cs new file mode 100644 index 0000000..f77602b --- /dev/null +++ b/OpenNest.Tests/RemnantFinderTests.cs @@ -0,0 +1,125 @@ +using OpenNest.Geometry; + +namespace OpenNest.Tests; + +public class RemnantFinderTests +{ + [Fact] + public void EmptyPlate_ReturnsWholeWorkArea() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + var remnants = finder.FindRemnants(); + + Assert.Single(remnants); + Assert.Equal(100 * 100, remnants[0].Area(), 0.1); + } + + [Fact] + public void SingleObstacle_InCorner_FindsLShapedRemnants() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 40, 40)); + var remnants = finder.FindRemnants(); + + Assert.True(remnants.Count >= 2); + var largest = remnants[0]; + Assert.Equal(60 * 100, largest.Area(), 0.1); + } + + [Fact] + public void SingleObstacle_InCenter_FindsFourRemnants() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(30, 30, 40, 40)); + var remnants = finder.FindRemnants(); + + Assert.True(remnants.Count >= 4); + } + + [Fact] + public void MinDimension_FiltersSmallRemnants() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 95, 100)); + var all = finder.FindRemnants(0); + var filtered = finder.FindRemnants(10); + + Assert.True(all.Count > filtered.Count); + foreach (var r in filtered) + { + Assert.True(r.Width >= 10); + Assert.True(r.Length >= 10); + } + } + + [Fact] + public void ResultsSortedByAreaDescending() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 50, 50)); + var remnants = finder.FindRemnants(); + + for (var i = 1; i < remnants.Count; i++) + Assert.True(remnants[i - 1].Area() >= remnants[i].Area()); + } + + [Fact] + public void AddObstacle_UpdatesResults() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + var before = finder.FindRemnants(); + Assert.Single(before); + + finder.AddObstacle(new Box(0, 0, 50, 50)); + var after = finder.FindRemnants(); + Assert.True(after.Count > 1); + } + + [Fact] + public void ClearObstacles_ResetsToFullWorkArea() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 50, 50)); + finder.ClearObstacles(); + var remnants = finder.FindRemnants(); + + Assert.Single(remnants); + Assert.Equal(100 * 100, remnants[0].Area(), 0.1); + } + + [Fact] + public void FullyCovered_ReturnsEmpty() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 100, 100)); + var remnants = finder.FindRemnants(); + + Assert.Empty(remnants); + } + + [Fact] + public void MultipleObstacles_FindsGapBetween() + { + var finder = new RemnantFinder(new Box(0, 0, 100, 100)); + finder.AddObstacle(new Box(0, 0, 40, 100)); + finder.AddObstacle(new Box(60, 0, 40, 100)); + var remnants = finder.FindRemnants(); + + var gap = remnants.FirstOrDefault(r => + r.Width >= 19.9 && r.Width <= 20.1 && + r.Length >= 99.9); + Assert.NotNull(gap); + } + + [Fact] + public void FromPlate_CreatesFinderWithPartsAsObstacles() + { + var plate = TestHelpers.MakePlate(60, 120, + TestHelpers.MakePartAt(0, 0, 20)); + var finder = RemnantFinder.FromPlate(plate); + var remnants = finder.FindRemnants(); + + Assert.True(remnants.Count >= 1); + Assert.True(remnants[0].Area() < plate.WorkArea().Area()); + } +}