Files
OpenNest/OpenNest.Engine.Tests/NestingEngines/PlanAssert.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

42 lines
1.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using OpenNest.Engine.Jobs;
namespace OpenNest.Engine.Tests.NestingEngines;
/// <summary>Checks an engine's whole-job plan reports (<see cref="NestJobStage.PlanImproved"/>).</summary>
internal static class PlanAssert
{
/// <summary>
/// Solves with and without a progress sink and asserts that plans are reported as proposals
/// with no committed counts, before any commit; that the last plan is the returned layout;
/// and that reporting does not change the layout. Returns the reported plans in order.
/// </summary>
public static IReadOnlyList<NestJobResult> ReportedAndEndWithTheResult(Func<INestingEngine> engine, NestJob job)
{
var reports = new List<NestJobProgress>();
var result = engine().Solve(job, new Capture(reports.Add));
var silent = engine().Solve(job);
var plans = reports.Where(r => r.Stage == NestJobStage.PlanImproved).ToList();
Assert.NotEmpty(plans);
Assert.All(plans, p => Assert.Equal((0, 0), (p.CommittedPlates, p.CommittedParts)));
Assert.All(plans, p => Assert.NotNull(p.Plan));
var firstCommit = reports.FindIndex(r => r.Stage == NestJobStage.PlateCommitted);
if (firstCommit >= 0)
Assert.True(reports.FindLastIndex(r => r.Stage == NestJobStage.PlanImproved) < firstCommit);
Assert.Equal(Layout(result), Layout(plans[^1].Plan));
Assert.Equal(Layout(silent), Layout(result));
return plans.Select(p => p.Plan).ToList();
}
public static IEnumerable<(int Plate, string StockId, NestJobPlacement Pose)> Layout(NestJobResult result) =>
result.Plates.SelectMany(p => p.Placements.Select(pose => (p.PlateIndex, p.StockId, pose)));
private sealed class Capture(Action<NestJobProgress> report) : IProgress<NestJobProgress>
{
public void Report(NestJobProgress value) => report(value);
}
}