using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Threading;
namespace OpenNest.Engine.Fill
{
///
/// Result returned by .
///
public class IterativeShrinkResult
{
public List Parts { get; set; } = new List();
public List Leftovers { get; set; } = new List();
}
///
/// Composes and with
/// dual-direction shrink selection. Wraps the caller's fill function in a
/// closure that tries both and
/// , picks the better ,
/// and passes the wrapper to .
///
public static class IterativeShrinkFiller
{
public static IterativeShrinkResult Fill(
List items,
Box workArea,
Func> fillFunc,
double spacing,
CancellationToken token = default)
{
if (items == null || items.Count == 0)
return new IterativeShrinkResult();
// RemnantFiller.FillItems skips items with Quantity == 0 (its localQty
// check treats them as done). Convert unlimited items (Quantity <= 0)
// to an estimated max capacity so they are actually processed.
var workItems = new List(items.Count);
foreach (var item in items)
{
if (item.Quantity <= 0)
{
var bbox = item.Drawing.Program.BoundingBox();
var estimatedMax = bbox.Area() > 0
? (int)(workArea.Area() / bbox.Area()) * 2
: 1000;
workItems.Add(new NestItem
{
Drawing = item.Drawing,
Quantity = System.Math.Max(1, estimatedMax),
Priority = item.Priority,
StepAngle = item.StepAngle,
RotationStart = item.RotationStart,
RotationEnd = item.RotationEnd
});
}
else
{
workItems.Add(item);
}
}
var filler = new RemnantFiller(workArea, spacing);
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 heightScore = FillScore.Compute(heightResult.Parts, box);
var widthScore = FillScore.Compute(widthResult.Parts, box);
return widthScore > heightScore ? widthResult.Parts : heightResult.Parts;
};
var placed = filler.FillItems(workItems, shrinkWrapper, token);
// Build leftovers: compare placed count to original quantities.
// RemnantFiller.FillItems does NOT mutate NestItem.Quantity.
var leftovers = new List();
foreach (var item in items)
{
var placedCount = 0;
foreach (var p in placed)
{
if (p.BaseDrawing.Name == item.Drawing.Name)
placedCount++;
}
if (item.Quantity <= 0)
continue; // unlimited items are always "satisfied" — no leftover
var remaining = item.Quantity - placedCount;
if (remaining > 0)
{
leftovers.Add(new NestItem
{
Drawing = item.Drawing,
Quantity = remaining,
Priority = item.Priority,
StepAngle = item.StepAngle,
RotationStart = item.RotationStart,
RotationEnd = item.RotationEnd
});
}
}
return new IterativeShrinkResult { Parts = placed, Leftovers = leftovers };
}
}
}