using System.Collections.Generic; using System.IO; using System.Linq; using OpenNest.Engine.Jobs; using OpenNest.Engine.Jobs.Adapters; using OpenNest.Geometry; namespace OpenNest.Benchmark { /// /// One request to nest a specific drawing, with the quantity and rotation /// constraints pulled from its source .nest file. /// 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; } } /// /// An immutable specification for one benchmark job: the full set of /// drawings/quantities that must be nested, and the pool of sheet sizes the /// engine may draw from while doing it. A single run may use several /// plates - possibly of different sizes - to place everything, the same /// way a real production job spreads across whatever plates it needs /// rather than being handed one fixed-size sheet. /// public class BenchmarkJob { public string SourceFile { get; init; } public List CandidateSizes { get; init; } public Spacing EdgeSpacing { get; init; } public double PartSpacing { get; init; } public int Quadrant { get; init; } /// Saved source-nest setting; manifests have no saved salvage rate and use zero. public double SalvageRate { get; init; } /// Original hand-authored placements, if the source was a .nest with any real parts. public List<(Plate Plate, List Parts)> BaselinePlateRuns { get; init; } public List Requests { get; init; } public string Name => Path.GetFileNameWithoutExtension(SourceFile); public int TotalRequestedQuantity => Requests.Sum(r => r.Quantity); /// Sheet area charged per unplaced part: the largest candidate /// sheet. Any single part that fits the stock at all fits on one such /// sheet, so placing a part is never scored worse than leaving it out. public double UnplacedPartPenalty => CandidateSizes.Count == 0 ? 0 : CandidateSizes.Max(s => s.Width * s.Length); /// /// Builds the whole-job request this job represents: one NestJobPart per /// requested drawing, and one NestPlateStock per candidate sheet size /// (unlimited quantity - the engine under test decides how many of each /// size it actually uses, and how demand splits across plates). The /// engine owns its own multi-plate/size strategy; this harness no /// longer picks plate sizes on the engine's behalf. /// public NestJob BuildNestJob( int maxPlates, double? salvageRate = null, double? minimumSalvageDimension = null ) { var parts = Requests.Select(r => DrawingJobMapper.FromDrawing(r.Drawing.Id.ToString(), r.Drawing, r.Quantity) ); var stock = CandidateSizes.Select(size => new NestPlateStock( size.ToString(1), size, null, PartSpacing, EdgeSpacing, Quadrant )); return new NestJob( parts, stock, new NestJobOptions( "Default", maxPlates, salvageRate ?? SalvageRate, minimumSalvageDimension ?? 0 ) ); } } }