feat: use direction-specific engines in StripNestEngine

Height shrink now uses HorizontalRemnantEngine (minimizes Y-extent)
and width shrink uses VerticalRemnantEngine (minimizes X-extent).
IterativeShrinkFiller accepts an optional widthFillFunc so each
shrink axis can use a different fill engine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 23:09:02 -04:00
parent 92b17b2963
commit 07012033c7
2 changed files with 17 additions and 8 deletions
@@ -31,7 +31,8 @@ namespace OpenNest.Engine.Fill
double spacing, double spacing,
CancellationToken token = default, CancellationToken token = default,
IProgress<NestProgress> progress = null, IProgress<NestProgress> progress = null,
int plateNumber = 0) int plateNumber = 0,
Func<NestItem, Box, List<Part>> widthFillFunc = null)
{ {
if (items == null || items.Count == 0) if (items == null || items.Count == 0)
return new IterativeShrinkResult(); return new IterativeShrinkResult();
@@ -72,6 +73,8 @@ namespace OpenNest.Engine.Fill
// include them in progress reports. // include them in progress reports.
var placedSoFar = new List<Part>(); var placedSoFar = new List<Part>();
var wFillFunc = widthFillFunc ?? fillFunc;
Func<NestItem, Box, List<Part>> shrinkWrapper = (ni, box) => Func<NestItem, Box, List<Part>> shrinkWrapper = (ni, box) =>
{ {
var target = ni.Quantity > 0 ? ni.Quantity : 0; var target = ni.Quantity > 0 ? ni.Quantity : 0;
@@ -84,7 +87,7 @@ namespace OpenNest.Engine.Fill
Parallel.Invoke( Parallel.Invoke(
() => heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token, () => heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token,
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar), targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar),
() => widthResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Width, token, () => widthResult = ShrinkFiller.Shrink(wFillFunc, ni, box, spacing, ShrinkAxis.Width, token,
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar) targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar)
); );
+12 -6
View File
@@ -77,17 +77,23 @@ namespace OpenNest
// Phase 1: Iterative shrink-fill for multi-quantity items. // Phase 1: Iterative shrink-fill for multi-quantity items.
if (fillItems.Count > 0) if (fillItems.Count > 0)
{ {
// Pass progress through so the UI shows intermediate results // Use direction-specific engines: height shrink benefits from
// during the initial BestFitCache computation and fill phases. // minimizing Y-extent, width shrink from minimizing X-extent.
Func<NestItem, Box, List<Part>> fillFunc = (ni, b) => Func<NestItem, Box, List<Part>> heightFillFunc = (ni, b) =>
{ {
var inner = new DefaultNestEngine(Plate); var inner = new HorizontalRemnantEngine(Plate);
return inner.Fill(ni, b, progress, token);
};
Func<NestItem, Box, List<Part>> widthFillFunc = (ni, b) =>
{
var inner = new VerticalRemnantEngine(Plate);
return inner.Fill(ni, b, progress, token); return inner.Fill(ni, b, progress, token);
}; };
var shrinkResult = IterativeShrinkFiller.Fill( var shrinkResult = IterativeShrinkFiller.Fill(
fillItems, workArea, fillFunc, Plate.PartSpacing, token, fillItems, workArea, heightFillFunc, Plate.PartSpacing, token,
progress, PlateNumber); progress, PlateNumber, widthFillFunc);
allParts.AddRange(shrinkResult.Parts); allParts.AddRange(shrinkResult.Parts);