Files
OpenNest/OpenNest.Benchmark/BenchmarkJob.cs
T
aj 6a0fba0fec Add OpenNest.Benchmark: generic head-to-head engine comparison harness
Loads any .nest file (or folder of them) via NestReader and nests every
drawing with quantity > 0 using each registered NestEngineBase, so it
works sight-unseen against arbitrary real jobs without any hardcoded
geometry. Optionally sweeps a fixed --sheet-sizes list instead of each
file's own plate size.

- BenchmarkJob/JobLoader build immutable job specs; a fresh Plate and
  NestItem list is created per (job, engine) run so state never leaks
  between engines or jobs.
- NestValidator rejects a layout if any part falls outside the work
  area, any two parts are closer than PartSpacing (checked via each
  part's own world-space polygon inflated by the spacing, so it holds
  for arbitrary concave/holed geometry, not just bounding boxes), or a
  drawing gets more parts than requested.
- Scoring matches Plate.Utilization() (placed area / full sheet area);
  ties among fully-placed layouts break on the smaller used bounding
  box (more usable remnant).
- Report prints a per-job ranked breakdown plus a per-engine summary
  (wins, avg utilization, time), and can write a flat CSV.

Verified end-to-end against a synthetic .nest file (not committed)
against the four built-in engines; caught a genuine out-of-work-area
bug in StripNestEngine in the process.
2026-09-15 18:31:36 -04:00

66 lines
2.2 KiB
C#

using OpenNest.Geometry;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenNest.Benchmark
{
/// <summary>
/// One request to nest a specific drawing, with the quantity and rotation
/// constraints pulled from its source .nest file.
/// </summary>
public class DrawingRequest
{
public Drawing Drawing { get; init; }
public int Quantity { get; init; }
public int Priority { get; init; }
public double StepAngle { get; init; }
public double RotationStart { get; init; }
public double RotationEnd { get; init; }
}
/// <summary>
/// An immutable specification for one benchmark job: a set of drawings/quantities
/// to be nested onto a plate of a given size. Every engine under test gets a fresh
/// Plate and NestItem list built from this spec via CreatePlate()/CreateItems(),
/// so one engine's run can never leak mutated state into another's.
/// </summary>
public class BenchmarkJob
{
public string SourceFile { get; init; }
public string SheetSizeLabel { get; init; }
public Size PlateSize { get; init; }
public Spacing EdgeSpacing { get; init; }
public double PartSpacing { get; init; }
public int Quadrant { get; init; }
public List<DrawingRequest> Requests { get; init; }
public string Name => $"{Path.GetFileNameWithoutExtension(SourceFile)} [{SheetSizeLabel}]";
public int TotalRequestedQuantity => Requests.Sum(r => r.Quantity);
public Plate CreatePlate()
{
return new Plate(PlateSize)
{
EdgeSpacing = EdgeSpacing,
PartSpacing = PartSpacing,
Quadrant = Quadrant,
};
}
public List<NestItem> CreateItems()
{
return Requests.Select(r => new NestItem
{
Drawing = r.Drawing,
Quantity = r.Quantity,
Priority = r.Priority,
StepAngle = r.StepAngle,
RotationStart = r.RotationStart,
RotationEnd = r.RotationEnd,
}).ToList();
}
}
}