using System;
using System.Collections.Generic;
using System.Threading;
namespace OpenNest;
///
/// Migrated built-in placement strategy for the whole-job runner. Reuses
/// iterative shrink-fill/pack geometry with the same run-scoped bookkeeping as :
/// remaining demand is read from the request and placement counts are derived from returned placements.
///
///
/// A private per requirement is created once per solve and reused across trials
/// (safe: the engine mutates per-trial and canonical copies, never the
/// shared Drawing). Identity is by Drawing reference. Each trial gets a fresh private .
///
public sealed class StripPlateNester : IPlateNester
{
private readonly Func engineFactory;
private readonly Dictionary drawingsById = new(StringComparer.Ordinal);
private readonly Dictionary idByDrawing = new(ReferenceEqualityComparer.Instance);
public StripPlateNester() : this(static plate => new StripNestEngine(plate))
{
}
/// Injectable for tests; defaults to .
public StripPlateNester(Func engineFactory)
{
this.engineFactory = engineFactory ?? throw new ArgumentNullException(nameof(engineFactory));
}
public PlateCandidate Place(PlatePlacementRequest request, IProgress progress = null,
CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(request);
token.ThrowIfCancellationRequested();
var plate = DrawingJobMapper.CreatePlate(request.Stock);
var items = new List(request.Parts.Count);
foreach (var requirement in request.Parts)
{
if (!drawingsById.TryGetValue(requirement.Id, out var drawing))
{
drawing = DrawingJobMapper.CreateDrawing(requirement);
drawingsById.Add(requirement.Id, drawing);
idByDrawing.Add(drawing, requirement.Id);
}
items.Add(new NestItem
{
Drawing = drawing,
Quantity = requirement.Quantity,
Priority = requirement.Priority,
StepAngle = DrawingJobMapper.LegacyStep(requirement.Rotation),
RotationStart = requirement.Rotation.Start,
RotationEnd = requirement.Rotation.End
});
}
var engine = engineFactory(plate) ?? throw new InvalidOperationException("Engine factory returned null.");
var legacyProgress = CandidateProgressBridge.Create(progress, request.Stock.Id);
var parts = engine.Nest(items, legacyProgress, token);
token.ThrowIfCancellationRequested();
if (parts == null) throw new InvalidOperationException("Engine returned null placements.");
var placements = new List(parts.Count);
foreach (var part in parts)
{
if (part?.BaseDrawing == null || !idByDrawing.TryGetValue(part.BaseDrawing, out var id))
throw new InvalidOperationException("Placement does not reference a known requirement drawing.");
placements.Add(new NestJobPlacement(id, 0, part.Location.X, part.Location.Y, part.Rotation));
}
return new PlateCandidate(placements);
}
}