mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-09 14:25:52 -05:00
The Irregular engine grows each sheet from one side, so parts that fit only when the outer ones hug opposite edges never met: ten one-off brackets (four tapered 112-inch bars, four gussets, two small plates) stacked three bars on one sheet and took three 60 x 120 sheets where two hold them. After the search, a sheet-reduction pass splits a complete single-stock plan over one sheet fewer and packs each sheet edge first: large parts to the corners (flush, most outline on the edges, thinnest across the sheet), small parts into the gaps last, and one key part into the pocket between. Clamped sides move in past the keep-outs so edge parts line up on one straight edge. When no sampled orientation fits the key, ContactFit turns it by its contacts (each overlap a linear constraint on dx, dy and rotation) instead of sampling more angles; the exact no-fit free region then confirms the angle, so spacing and validation are unchanged. That job now nests on two sheets, the middle bar at 4.4 degrees; the repository benchmark jobs are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.6 KiB
C#
65 lines
2.6 KiB
C#
#nullable enable
|
|
using Clipper2Lib;
|
|
using OpenNest.Engine.Jobs;
|
|
|
|
namespace OpenNest.Engine.NestingEngines.Irregular;
|
|
|
|
/// <summary>
|
|
/// Static, clearance-inflated rectangles prepared once per stock per solve. Kept out of
|
|
/// the part catalog and its spacing cache: clamp clearance is the stock's clamp clearance, not
|
|
/// part spacing, and two stocks at the same part spacing may have different clamps.
|
|
/// </summary>
|
|
internal sealed class ClampObstacles
|
|
{
|
|
private readonly Orientation[] rectangles;
|
|
private readonly NoFitCache nfps = new(0);
|
|
|
|
public ClampObstacles(NestPlateStock stock)
|
|
{
|
|
rectangles = new Orientation[stock.ClampZones.Count];
|
|
var clearance = stock.ClampClearance;
|
|
for (var i = 0; i < rectangles.Length; i++)
|
|
{
|
|
var box = stock.ClampZones[i].Bounds;
|
|
var (left, right) = EncloseOnGrid(box.Left - clearance, box.Right + clearance);
|
|
var (bottom, top) = EncloseOnGrid(box.Bottom - clearance, box.Top + clearance);
|
|
rectangles[i] = new Orientation
|
|
{
|
|
// Real part types are nonnegative; the private cache cannot alias them.
|
|
TypeIndex = -1,
|
|
Index = i,
|
|
Rotation = 0,
|
|
Tolerance = 0,
|
|
MinX = left,
|
|
MinY = bottom,
|
|
MaxX = right,
|
|
MaxY = top,
|
|
Outline = new PathD
|
|
{
|
|
new(left, bottom), new(right, bottom), new(right, top), new(left, top),
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
// NoFitCache quantizes before adding its safety margin. Enclose the keep-out
|
|
// on that existing lattice so a sub-grid rectangle cannot disappear first.
|
|
private static (double Lower, double Upper) EncloseOnGrid(double lower, double upper)
|
|
{
|
|
var scale = System.Math.Pow(10, NoFitCache.Precision);
|
|
var first = System.Math.Floor(lower * scale);
|
|
// Positive dimensions can be smaller than a coordinate ulp (upper == lower).
|
|
var last = System.Math.Max(System.Math.Ceiling(upper * scale), first + 1);
|
|
return (first / scale, last / scale);
|
|
}
|
|
|
|
public int Count => rectangles.Length;
|
|
|
|
/// <summary>The keep-out rectangle, clearance included: a part's outline must stay outside it.</summary>
|
|
public PathD Outline(int index) => rectangles[index].Outline;
|
|
|
|
// No additional physical spacing. NoFitCache retains the established arc/grid safety
|
|
// margin and covered-hole certification for the moving orientation and rectangle.
|
|
public Nfp Get(int index, Orientation moving) => nfps.Get(rectangles[index], moving);
|
|
}
|