diff --git a/OpenNest.Engine/Fill/IterativeShrinkFiller.cs b/OpenNest.Engine/Fill/IterativeShrinkFiller.cs index e34a5e7..37255ba 100644 --- a/OpenNest.Engine/Fill/IterativeShrinkFiller.cs +++ b/OpenNest.Engine/Fill/IterativeShrinkFiller.cs @@ -28,7 +28,9 @@ namespace OpenNest.Engine.Fill Box workArea, Func> fillFunc, double spacing, - CancellationToken token = default) + CancellationToken token = default, + IProgress progress = null, + int plateNumber = 0) { if (items == null || items.Count == 0) return new IterativeShrinkResult(); @@ -65,16 +67,37 @@ namespace OpenNest.Engine.Fill var filler = new RemnantFiller(workArea, spacing); + // Track parts placed by previous items so ShrinkFiller can + // include them in progress reports. + var placedSoFar = new List(); + Func> shrinkWrapper = (ni, box) => { var target = ni.Quantity > 0 ? ni.Quantity : 0; - var heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token, targetCount: target); - var widthResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Width, token, targetCount: target); + var heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token, + targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar); + var widthResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Width, token, + targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar); var heightScore = FillScore.Compute(heightResult.Parts, box); var widthScore = FillScore.Compute(widthResult.Parts, box); - return widthScore > heightScore ? widthResult.Parts : heightResult.Parts; + var best = widthScore > heightScore ? widthResult.Parts : heightResult.Parts; + + // Report the winner as overall best so the UI shows it as settled. + if (progress != null && best != null && best.Count > 0) + { + var allParts = new List(placedSoFar.Count + best.Count); + allParts.AddRange(placedSoFar); + allParts.AddRange(best); + NestEngineBase.ReportProgress(progress, NestPhase.Custom, plateNumber, + allParts, box, $"Shrink: {best.Count} parts placed", isOverallBest: true); + } + + // Accumulate for the next item's progress reports. + placedSoFar.AddRange(best); + + return best; }; var placed = filler.FillItems(workItems, shrinkWrapper, token); diff --git a/OpenNest.Engine/Fill/ShrinkFiller.cs b/OpenNest.Engine/Fill/ShrinkFiller.cs index 84a86f2..f33a762 100644 --- a/OpenNest.Engine/Fill/ShrinkFiller.cs +++ b/OpenNest.Engine/Fill/ShrinkFiller.cs @@ -29,7 +29,10 @@ namespace OpenNest.Engine.Fill ShrinkAxis axis, CancellationToken token = default, int maxIterations = 20, - int targetCount = 0) + 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. @@ -60,6 +63,8 @@ namespace OpenNest.Engine.Fill var bestParts = parts; var bestDim = MeasureDimension(parts, box, axis); + ReportShrinkProgress(progress, plateNumber, placedParts, bestParts, box, axis, bestDim); + for (var i = 0; i < maxIterations; i++) { if (token.IsCancellationRequested) @@ -73,6 +78,10 @@ namespace OpenNest.Engine.Fill ? 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) @@ -80,11 +89,35 @@ namespace OpenNest.Engine.Fill bestParts = trialParts; bestDim = MeasureDimension(trialParts, box, axis); + + ReportShrinkProgress(progress, plateNumber, placedParts, bestParts, trialBox, axis, bestDim); } return new ShrinkResult { Parts = bestParts, Dimension = bestDim }; } + private static void ReportShrinkProgress( + IProgress progress, int plateNumber, + List placedParts, List bestParts, + Box workArea, ShrinkAxis axis, double dim) + { + if (progress == null) + return; + + var allParts = placedParts != null && placedParts.Count > 0 + ? new List(placedParts.Count + bestParts.Count) + : new List(bestParts.Count); + + if (placedParts != null && placedParts.Count > 0) + allParts.AddRange(placedParts); + allParts.AddRange(bestParts); + + var desc = $"Shrink {axis}: {bestParts.Count} parts, dim={dim:F1}"; + + NestEngineBase.ReportProgress(progress, NestPhase.Custom, plateNumber, + allParts, workArea, desc); + } + /// /// Uses FillBestFit (fast rectangle packing) to estimate a starting box /// that fits roughly the target count. Scales the shrink axis proportionally diff --git a/OpenNest.Engine/StripNestEngine.cs b/OpenNest.Engine/StripNestEngine.cs index 573e3cf..675307c 100644 --- a/OpenNest.Engine/StripNestEngine.cs +++ b/OpenNest.Engine/StripNestEngine.cs @@ -77,14 +77,17 @@ namespace OpenNest // Phase 1: Iterative shrink-fill for multi-quantity items. if (fillItems.Count > 0) { + // Inner fills are silent — ShrinkFiller manages progress reporting + // to avoid overlapping layouts from per-angle/per-strategy reports. Func> fillFunc = (ni, b) => { var inner = new DefaultNestEngine(Plate); - return inner.Fill(ni, b, progress, token); + return inner.Fill(ni, b, null, token); }; var shrinkResult = IterativeShrinkFiller.Fill( - fillItems, workArea, fillFunc, Plate.PartSpacing, token); + fillItems, workArea, fillFunc, Plate.PartSpacing, token, + progress, PlateNumber); allParts.AddRange(shrinkResult.Parts);