diff --git a/OpenNest.Engine/Fill/ShrinkFiller.cs b/OpenNest.Engine/Fill/ShrinkFiller.cs index f33a762..13a5b75 100644 --- a/OpenNest.Engine/Fill/ShrinkFiller.cs +++ b/OpenNest.Engine/Fill/ShrinkFiller.cs @@ -163,5 +163,20 @@ namespace OpenNest.Engine.Fill ? placedBox.Right - box.X : placedBox.Top - box.Y; } + + /// + /// Keeps the parts nearest to the origin + /// along the given axis, discarding parts farthest from the origin. + /// Returns the input list unchanged if count is already at or below target. + /// + internal static List TrimToCount(List parts, int targetCount, ShrinkAxis axis) + { + if (parts == null || parts.Count <= targetCount) + return parts; + + return axis == ShrinkAxis.Width + ? parts.OrderBy(p => p.BoundingBox.Right).Take(targetCount).ToList() + : parts.OrderBy(p => p.BoundingBox.Top).Take(targetCount).ToList(); + } } } diff --git a/OpenNest.Tests/ShrinkFillerTests.cs b/OpenNest.Tests/ShrinkFillerTests.cs index 8d50f32..9f028d1 100644 --- a/OpenNest.Tests/ShrinkFillerTests.cs +++ b/OpenNest.Tests/ShrinkFillerTests.cs @@ -97,4 +97,71 @@ public class ShrinkFillerTests Assert.NotNull(result); Assert.True(result.Parts.Count > 0); } + + [Fact] + public void TrimToCount_Width_KeepsPartsNearestToOrigin() + { + var parts = new List + { + TestHelpers.MakePartAt(0, 0, 5), // Right = 5 + TestHelpers.MakePartAt(10, 0, 5), // Right = 15 + TestHelpers.MakePartAt(20, 0, 5), // Right = 25 + TestHelpers.MakePartAt(30, 0, 5), // Right = 35 + }; + + var trimmed = ShrinkFiller.TrimToCount(parts, 2, ShrinkAxis.Width); + + Assert.Equal(2, trimmed.Count); + Assert.True(trimmed.All(p => p.BoundingBox.Right <= 15)); + } + + [Fact] + public void TrimToCount_Height_KeepsPartsNearestToOrigin() + { + var parts = new List + { + TestHelpers.MakePartAt(0, 0, 5), // Top = 5 + TestHelpers.MakePartAt(0, 10, 5), // Top = 15 + TestHelpers.MakePartAt(0, 20, 5), // Top = 25 + TestHelpers.MakePartAt(0, 30, 5), // Top = 35 + }; + + var trimmed = ShrinkFiller.TrimToCount(parts, 2, ShrinkAxis.Height); + + Assert.Equal(2, trimmed.Count); + Assert.True(trimmed.All(p => p.BoundingBox.Top <= 15)); + } + + [Fact] + public void TrimToCount_ReturnsInput_WhenCountAtOrBelowTarget() + { + var parts = new List + { + TestHelpers.MakePartAt(0, 0, 5), + TestHelpers.MakePartAt(10, 0, 5), + }; + + var same = ShrinkFiller.TrimToCount(parts, 2, ShrinkAxis.Width); + Assert.Same(parts, same); + + var fewer = ShrinkFiller.TrimToCount(parts, 5, ShrinkAxis.Width); + Assert.Same(parts, fewer); + } + + [Fact] + public void TrimToCount_DoesNotMutateInput() + { + var parts = new List + { + TestHelpers.MakePartAt(0, 0, 5), + TestHelpers.MakePartAt(10, 0, 5), + TestHelpers.MakePartAt(20, 0, 5), + }; + + var originalCount = parts.Count; + var trimmed = ShrinkFiller.TrimToCount(parts, 1, ShrinkAxis.Width); + + Assert.Equal(originalCount, parts.Count); + Assert.Equal(1, trimmed.Count); + } }