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.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user