Files
OpenNest/OpenNest.Engine/NestingEngines/CandidateProgress.cs
T
ajandClaude Opus 5.5 00acaeaaf1 feat(engine): report best whole-job plans from Rectangles, Circles and Default
Desktop Auto Nest's "Best plan so far" strip now fills in for every built-in
whole-job engine except FlatBar, not only Irregular.

- Rectangles makes one sheet-by-sheet run, so it reports the finished job as
  its one plan, then replays its plate commits (token checked as before).
- Circles reports when a stock-preference run or a tail re-plan beats its
  best result so far.
- Default scores each candidate plan as it ranks results (valid, fewest
  unplaced, lowest cost, candidate order) and reports it only when it beats
  every plan already shown, so a later candidate never shows a worse plan.
  The chosen result is always the last plan reported.
- Shared ReportPlan/SameLayout helpers on CandidateProgress; Irregular uses
  them too. Reporting never changes the search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:56:38 -04:00

50 lines
2.2 KiB
C#

#nullable enable
using System;
using System.Linq;
using System.Threading;
using OpenNest.Engine.Jobs;
namespace OpenNest.Engine.NestingEngines;
/// <summary>Forwards a candidate run's progress except plate commits, which belong to the result
/// finally chosen; <see cref="ReportCommits"/> reports those once it is.</summary>
internal sealed class CandidateProgress(IProgress<NestJobProgress> inner) : IProgress<NestJobProgress>
{
public void Report(NestJobProgress value)
{
if (value != null && value.Stage != NestJobStage.PlateCommitted)
inner.Report(value);
}
/// <summary>Reports the plate commits of <paramref name="result"/> in order. The token is
/// observed before each commit and after the last, as while building a result.</summary>
public static void ReportCommits(NestJobResult result, IProgress<NestJobProgress>? 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();
}
/// <summary>Reports a whole-job plan as <see cref="NestJobStage.PlanImproved"/>: a proposal
/// with no committed counts. Reporting never changes the search.</summary>
public static void ReportPlan(NestJobResult plan, IProgress<NestJobProgress>? progress) =>
progress?.Report(new NestJobProgress(NestJobStage.PlanImproved, string.Empty, -1, 0, 0, Plan: plan));
/// <summary>True when both results place the same parts at the same poses on the same stock,
/// sheet by sheet.</summary>
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)));
}