#nullable enable using System; using System.Linq; using System.Threading; using OpenNest.Engine.Jobs; namespace OpenNest.Engine.NestingEngines; /// Forwards a candidate run's progress except plate commits, which belong to the result /// finally chosen; reports those once it is. internal sealed class CandidateProgress(IProgress inner) : IProgress { public void Report(NestJobProgress value) { if (value != null && value.Stage != NestJobStage.PlateCommitted) inner.Report(value); } /// Reports the plate commits of in order. The token is /// observed before each commit and after the last, as while building a result. public static void ReportCommits(NestJobResult result, IProgress? progress, CancellationToken token = default) { if (progress == null) return; var parts = 0; for (var i = 0; i < result.Plates.Count; i++) { token.ThrowIfCancellationRequested(); var plate = result.Plates[i]; parts += plate.Placements.Count; progress.Report(new NestJobProgress(NestJobStage.PlateCommitted, plate.StockId, plate.PlateIndex, i + 1, parts)); } token.ThrowIfCancellationRequested(); } /// Reports a whole-job plan as : a proposal /// with no committed counts. Reporting never changes the search. public static void ReportPlan(NestJobResult plan, IProgress? progress) => progress?.Report(new NestJobProgress(NestJobStage.PlanImproved, string.Empty, -1, 0, 0, Plan: plan)); /// True when both results place the same parts at the same poses on the same stock, /// sheet by sheet. public static bool SameLayout(NestJobResult a, NestJobResult b) => ReferenceEquals(a, b) || (a.Plates.Count == b.Plates.Count && a.Plates.Zip(b.Plates).All(p => p.First.StockId == p.Second.StockId && p.First.Placements.SequenceEqual(p.Second.Placements))); }