From 6d66636e3d519bce2ff3e7934c8a016a2160db2b Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 20 Mar 2026 00:06:58 -0400 Subject: [PATCH] refactor: replace ShrinkFiller shrink loop with TrimToCount Co-Authored-By: Claude Sonnet 4.6 --- OpenNest.Engine/Fill/ShrinkFiller.cs | 49 ++++------------------------ OpenNest.Tests/ShrinkFillerTests.cs | 21 +----------- 2 files changed, 8 insertions(+), 62 deletions(-) diff --git a/OpenNest.Engine/Fill/ShrinkFiller.cs b/OpenNest.Engine/Fill/ShrinkFiller.cs index 13a5b75..3856305 100644 --- a/OpenNest.Engine/Fill/ShrinkFiller.cs +++ b/OpenNest.Engine/Fill/ShrinkFiller.cs @@ -16,9 +16,8 @@ namespace OpenNest.Engine.Fill } /// - /// Fills a box then iteratively shrinks one axis by the spacing amount - /// until the part count drops. Returns the tightest box that still fits - /// the target number of parts. + /// Fills a box and trims excess parts by removing those farthest from + /// the origin along the shrink axis. /// public static class ShrinkFiller { @@ -28,22 +27,17 @@ namespace OpenNest.Engine.Fill double spacing, ShrinkAxis axis, CancellationToken token = default, - int maxIterations = 20, int targetCount = 0, IProgress progress = null, int plateNumber = 0, List placedParts = null) { - // If a target count is specified, estimate a smaller starting box - // to avoid an expensive full-area fill. var startBox = box; if (targetCount > 0) startBox = EstimateStartBox(item, box, spacing, axis, targetCount); var parts = fillFunc(item, startBox); - // If estimate was too aggressive and we got fewer than target, - // fall back to the full box. if (targetCount > 0 && startBox != box && (parts == null || parts.Count < targetCount)) { @@ -53,47 +47,18 @@ namespace OpenNest.Engine.Fill if (parts == null || parts.Count == 0) return new ShrinkResult { Parts = parts ?? new List(), Dimension = 0 }; - // Shrink target: if a target count was given and we got at least that many, - // shrink to fit targetCount (not the full count). This produces a tighter box. - // If we got fewer than target, shrink to maintain what we have. var shrinkTarget = targetCount > 0 ? System.Math.Min(targetCount, parts.Count) : parts.Count; - var bestParts = parts; - var bestDim = MeasureDimension(parts, box, axis); + if (parts.Count > shrinkTarget) + parts = TrimToCount(parts, shrinkTarget, axis); - ReportShrinkProgress(progress, plateNumber, placedParts, bestParts, box, axis, bestDim); + var dim = MeasureDimension(parts, box, axis); - for (var i = 0; i < maxIterations; i++) - { - if (token.IsCancellationRequested) - break; + ReportShrinkProgress(progress, plateNumber, placedParts, parts, box, axis, dim); - var trialDim = bestDim - spacing; - if (trialDim <= 0) - break; - - var trialBox = axis == ShrinkAxis.Width - ? new Box(box.X, box.Y, trialDim, box.Length) - : new Box(box.X, box.Y, box.Width, trialDim); - - // Report the trial box before the fill so the UI updates the - // work area border immediately rather than after the fill completes. - ReportShrinkProgress(progress, plateNumber, placedParts, bestParts, trialBox, axis, trialDim); - - var trialParts = fillFunc(item, trialBox); - - if (trialParts == null || trialParts.Count < shrinkTarget) - break; - - bestParts = trialParts; - bestDim = MeasureDimension(trialParts, box, axis); - - ReportShrinkProgress(progress, plateNumber, placedParts, bestParts, trialBox, axis, bestDim); - } - - return new ShrinkResult { Parts = bestParts, Dimension = bestDim }; + return new ShrinkResult { Parts = parts, Dimension = dim }; } private static void ReportShrinkProgress( diff --git a/OpenNest.Tests/ShrinkFillerTests.cs b/OpenNest.Tests/ShrinkFillerTests.cs index 9f028d1..93b3b5d 100644 --- a/OpenNest.Tests/ShrinkFillerTests.cs +++ b/OpenNest.Tests/ShrinkFillerTests.cs @@ -17,7 +17,7 @@ public class ShrinkFillerTests } [Fact] - public void Shrink_ReducesDimension_UntilCountDrops() + public void Shrink_FillsAndReturnsDimension() { var drawing = MakeSquareDrawing(10); var item = new NestItem { Drawing = drawing }; @@ -59,25 +59,6 @@ public class ShrinkFillerTests Assert.True(result.Dimension <= 100); } - [Fact] - public void Shrink_RespectsMaxIterations() - { - var callCount = 0; - Func> fillFunc = (ni, b) => - { - callCount++; - return new List { TestHelpers.MakePartAt(0, 0, 5) }; - }; - - var item = new NestItem { Drawing = MakeSquareDrawing(5) }; - var box = new Box(0, 0, 100, 100); - - ShrinkFiller.Shrink(fillFunc, item, box, 1.0, ShrinkAxis.Height, maxIterations: 3); - - // 1 initial + up to 3 shrink iterations = max 4 calls - Assert.True(callCount <= 4); - } - [Fact] public void Shrink_RespectsCancellation() {