feat(engine): Opus55 frontier-advance NFP nesting engine
Rename the OpenNest.Engine.Sonnet5 scaffold to OpenNest.Engine.Opus55 and implement an independent whole-job INestingEngine (no built-in engine, registry, or best-fit internals are called or copied). - PartCatalog: snapshot perimeter -> polygon per allowed orientation, with adaptive chord tolerance and MBR-aligned rotations for Automatic parts. - NoFitCache: spacing footprints and cached Clipper2 Minkowski NFPs (convex fast path; concave sweep plus both containment terms). - FrontierPacker: per-(type, orientation) free regions (inner-fit rectangle minus NFPs), updated incrementally; gap-fill-largest, else least front advance per area^beta. - Engine: look-ahead stock choice by estimated whole-job net area, six deterministic variants, tail re-plan of the last 1-3 sheets. - Tests judged by OpenNest.Benchmark's NestValidator, including an NFP containment regression guard. P260805-10.nest (219 parts), all 9 stock sizes: 219/219 valid, 27 sheets, 91.7% utilization, ~7 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using OpenNest.Engine.Jobs;
|
||||
|
||||
namespace OpenNest.Engine.Opus55;
|
||||
|
||||
/// <summary>
|
||||
/// The objective the engine optimizes: sheet area consumed, less the salvage credit for the
|
||||
/// single largest full-width or full-length edge offcut the job's options allow. Packing toward
|
||||
/// one edge (see <see cref="PackAxis"/>) is what makes that offcut large.
|
||||
/// </summary>
|
||||
internal static class SheetEconomics
|
||||
{
|
||||
public static double SheetArea(NestPlateStock stock) => stock.Size.Width * stock.Size.Length;
|
||||
|
||||
public static double NetArea(NestJobOptions options, SheetFill fill)
|
||||
{
|
||||
var area = SheetArea(fill.Stock);
|
||||
var minimum = options.MinimumSalvageDimension;
|
||||
if (options.SalvageRate <= 0 || minimum <= 0 || fill.Parts.Count == 0)
|
||||
return area;
|
||||
|
||||
var work = FrontierPacker.WorkArea(fill.Stock);
|
||||
var gap = fill.Stock.PartSpacing;
|
||||
var left = fill.Parts.Min(p => p.Left);
|
||||
var right = fill.Parts.Max(p => p.Right);
|
||||
var bottom = fill.Parts.Min(p => p.Bottom);
|
||||
var top = fill.Parts.Max(p => p.Top);
|
||||
var offcuts = new[]
|
||||
{
|
||||
// Box.Length is the X extent, Box.Width the Y extent.
|
||||
(work.Length, bottom - work.Bottom - gap),
|
||||
(work.Length, work.Top - top - gap),
|
||||
(left - work.Left - gap, work.Width),
|
||||
(work.Right - right - gap, work.Width),
|
||||
};
|
||||
var salvage = 0.0;
|
||||
foreach (var (a, b) in offcuts)
|
||||
if (a >= minimum && b >= minimum)
|
||||
salvage = System.Math.Max(salvage, a * b);
|
||||
return area - options.SalvageRate * salvage;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user