From 0e3bf3ccaaccc7056c9f9ed45e5c51a9a043237d Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 10 Mar 2026 23:19:42 -0400 Subject: [PATCH] feat: try fewer rows in FillRecursive when remainder strip yields more parts When the greedy maximum-row tiling leaves a thin remainder, tries removing the last row and re-filling the larger strip. Picks whichever total is higher. Fixes cases where e.g. 4 rows + 11 remainder = 47 beats 5 rows = 45. Co-Authored-By: Claude Opus 4.6 --- OpenNest.Engine/FillLinear.cs | 41 +++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/OpenNest.Engine/FillLinear.cs b/OpenNest.Engine/FillLinear.cs index 431947d..c6cae63 100644 --- a/OpenNest.Engine/FillLinear.cs +++ b/OpenNest.Engine/FillLinear.cs @@ -338,15 +338,23 @@ namespace OpenNest var rowPattern = new Pattern(); rowPattern.Parts.AddRange(result); rowPattern.UpdateBounds(); - var gridResult = FillRecursive(rowPattern, PerpendicularAxis(direction), depth + 1); + var perpAxis = PerpendicularAxis(direction); + var gridResult = FillRecursive(rowPattern, perpAxis, depth + 1); // Fill the remaining strip (after the last full row/column) // with individual parts from the seed pattern. - var remaining = FillRemainingStrip(gridResult, pattern, PerpendicularAxis(direction), direction); + var remaining = FillRemainingStrip(gridResult, pattern, perpAxis, direction); if (remaining.Count > 0) gridResult.AddRange(remaining); + // Try one fewer row/column — the larger remainder strip may + // fit more parts than the extra row contained. + var fewerResult = TryFewerRows(gridResult, rowPattern, pattern, perpAxis, direction); + + if (fewerResult != null && fewerResult.Count > gridResult.Count) + return fewerResult; + return gridResult; } @@ -359,6 +367,35 @@ namespace OpenNest return result; } + /// + /// Tries removing the last row/column from the grid and re-filling the + /// larger remainder strip. Returns null if this doesn't improve the total. + /// + private List TryFewerRows( + List fullResult, Pattern rowPattern, Pattern seedPattern, + NestDirection tiledAxis, NestDirection primaryAxis) + { + var rowPartCount = rowPattern.Parts.Count; + + // Need at least 2 rows for this to make sense (remove 1, keep 1+). + if (fullResult.Count < rowPartCount * 2) + return null; + + // Remove the last row's worth of parts. + var fewerParts = new List(fullResult.Count - rowPartCount); + + for (var i = 0; i < fullResult.Count - rowPartCount; i++) + fewerParts.Add(fullResult[i]); + + var remaining = FillRemainingStrip(fewerParts, seedPattern, tiledAxis, primaryAxis); + + if (remaining.Count <= rowPartCount) + return null; + + fewerParts.AddRange(remaining); + return fewerParts; + } + /// /// After tiling full rows/columns, fills the remaining strip with individual /// parts. The strip is the leftover space along the tiled axis between the