mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 12:35:07 -05:00
Slice 3 of common-line cutting. On costly plate (N26-295: 3/16 RA310S at about $2,500 a sheet) pairing parts one kerf apart fits the part the spacing cannot: the last plate's 38 parts fit on one 48x120 with Auto Nest only with pairs. - Drawing Properties gains "Allow common line" (Drawing.AllowCommonLine, off by default because customer edge-quality needs vary). The nest's CommonLineUsage (Off / When it adds parts / Whenever available) is set in Auto Nest and seeded from Tools > Nest Defaults; both persist (nest.json, defaults.json). - NestPipeline runs the ordinary solve, then, with a level on, trials where eligible parts are offered as kerf-gap pair compounds. Placed pairs expand into parts plus explicit NestJobCommonLine groups, are validated again, and CommonLineRanking keeps a trial only by the level: count first, then sheets; "when it adds parts" keeps the plain layout on a tie, "whenever" also takes a shorter cut or less material, never at the cost of a part. Engines, FillScore and their comparers are unchanged; Off asks the engine exactly what it did. - NestLayoutCheck exempts members only through a sheet's explicit group list and only when the job allows common lines; materializing and committing create Plate.CommonLines. - Server nesting writes common-line jobs as version 3 and replays the recorded trial answers, so the desktop binds the same groups. - Fill Area honors the level through a PlateFillService overload. - The benchmark gains --common-lines <level> --kerf <k>. Benchmark (synthetic nests + N26-295, --parallel 1): with no flag and with --common-lines off every engine matches the base commit; with a level on no layout becomes invalid and three runs save a sheet at equal nested area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.6 KiB
C#
58 lines
2.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Engine.Jobs.Adapters;
|
|
|
|
/// <summary>
|
|
/// Binds one result sheet's poses to the caller's own drawings. The pose semantics match
|
|
/// <see cref="NestResultMaterializer"/>: rotate about the snapshot origin, then translate.
|
|
/// Every pose must be finite and refer to a mapped drawing. Malformed sheets are rejected
|
|
/// before binding any parts; the pipeline reports these violations without invoking binding.
|
|
/// </summary>
|
|
public static class NestResultBinder
|
|
{
|
|
public static IReadOnlyList<Part> Bind(
|
|
NestJobPlateResult sheet,
|
|
IReadOnlyDictionary<string, Drawing> drawingsByPartId
|
|
)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(sheet);
|
|
ArgumentNullException.ThrowIfNull(drawingsByPartId);
|
|
foreach (var pose in sheet.Placements)
|
|
{
|
|
if (pose.PartId == null || !drawingsByPartId.TryGetValue(pose.PartId, out var drawing) || drawing == null)
|
|
throw new ArgumentException("Result contains a requirement not present in the drawing map.", nameof(sheet));
|
|
if (!double.IsFinite(pose.X) || !double.IsFinite(pose.Y) || !double.IsFinite(pose.Rotation))
|
|
throw new ArgumentException("Result contains a nonfinite placement pose.", nameof(sheet));
|
|
}
|
|
var parts = new List<Part>(sheet.Placements.Count);
|
|
foreach (var pose in sheet.Placements)
|
|
{
|
|
var part = new Part(drawingsByPartId[pose.PartId]);
|
|
part.Rotate(pose.Rotation);
|
|
part.Location = new Vector(pose.X, pose.Y);
|
|
part.UpdateBounds();
|
|
parts.Add(part);
|
|
}
|
|
return parts;
|
|
}
|
|
|
|
/// <summary>The sheet's common-line groups over <paramref name="parts"/>, which
|
|
/// <see cref="Bind"/> returned for that sheet (same order).</summary>
|
|
public static IReadOnlyList<CommonLineGroup> BindCommonLines(NestJobPlateResult sheet, IReadOnlyList<Part> parts)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(sheet);
|
|
ArgumentNullException.ThrowIfNull(parts);
|
|
var groups = new List<CommonLineGroup>(sheet.CommonLines.Count);
|
|
foreach (var group in sheet.CommonLines)
|
|
{
|
|
if (group.Placements.Count < 2 || group.Placements.Any(i => i < 0 || i >= parts.Count))
|
|
throw new ArgumentException("A common-line group names a placement the sheet does not have.", nameof(sheet));
|
|
groups.Add(new CommonLineGroup(group.Placements.Select(i => parts[i]), group.Kerf));
|
|
}
|
|
return groups;
|
|
}
|
|
}
|