mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 16:31:07 -04:00
feat(irregular): treat clamp zones as static NFP obstacles
This commit is contained in:
1 parent
de9a087027
commit
c374e02614
6 files changed
+340
-8
No files matched your search
@@ -0,0 +1,52 @@
|
||||
#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 bottom edge spacing, 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 = System.Math.Max(0, stock.EdgeSpacing.Bottom);
|
||||
for (var i = 0; i < rectangles.Length; i++)
|
||||
{
|
||||
var box = stock.ClampZones[i].Bounds;
|
||||
var left = box.Left - clearance;
|
||||
var bottom = box.Bottom - clearance;
|
||||
var right = box.Right + clearance;
|
||||
var top = 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),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => rectangles.Length;
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -35,8 +35,8 @@ internal sealed record SheetFill(NestPlateStock Stock, IReadOnlyList<Placed> Par
|
||||
///
|
||||
/// For every (part type, orientation) still in play the packer keeps the exact set of legal
|
||||
/// reference points: the inner-fit rectangle of the work area minus the no-fit polygons of
|
||||
/// everything already placed. Each placement subtracts one translated NFP from each region,
|
||||
/// so regions only shrink, and a region that empties is retired for the rest of the sheet.
|
||||
/// static clamps and everything already placed. Each placement subtracts one translated NFP
|
||||
/// from each region, so regions only shrink; an empty region is retired for the sheet.
|
||||
///
|
||||
/// Choice rule, applied over all types and orientations at once (not in a fixed order):
|
||||
/// 1. Gap fill - if any part fits without pushing the packing front forward, place the
|
||||
@@ -62,6 +62,7 @@ internal sealed class FrontierPacker
|
||||
private readonly Box work;
|
||||
private readonly WorkCounter counter;
|
||||
private readonly BlockCatalog? blocks;
|
||||
private readonly ClampObstacles? clamps;
|
||||
|
||||
public FrontierPacker(
|
||||
IReadOnlyList<PartType> types,
|
||||
@@ -71,10 +72,12 @@ internal sealed class FrontierPacker
|
||||
PackAxis axis,
|
||||
double beta,
|
||||
WorkCounter counter,
|
||||
BlockCatalog? blocks = null
|
||||
BlockCatalog? blocks = null,
|
||||
ClampObstacles? clamps = null
|
||||
)
|
||||
{
|
||||
this.blocks = blocks;
|
||||
this.clamps = stock.ClampZones.Count == 0 ? null : clamps ?? new ClampObstacles(stock);
|
||||
this.counter = counter;
|
||||
this.types = types;
|
||||
this.nfps = nfps;
|
||||
@@ -97,6 +100,16 @@ internal sealed class FrontierPacker
|
||||
if (!stock.Fits(o.Width, o.Height))
|
||||
return false;
|
||||
var region = new Region(o, work, single);
|
||||
if (clamps != null)
|
||||
for (var i = 0; i < clamps.Count; i++)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
var nfp = clamps.Get(i, o);
|
||||
token.ThrowIfCancellationRequested();
|
||||
region.Subtract(nfp, 0, 0);
|
||||
counter.Add(1);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
states.Add(region);
|
||||
byOrientation[o] = region;
|
||||
return true;
|
||||
|
||||
@@ -118,6 +118,7 @@ public sealed class IrregularNestingEngine : INestingEngine
|
||||
|
||||
private const int MaxTail = 3;
|
||||
private readonly Dictionary<double, NoFitCache> caches = new();
|
||||
private readonly Dictionary<NestPlateStock, ClampObstacles> clamps = new();
|
||||
private readonly Dictionary<double, IReadOnlyDictionary<int, IReadOnlyList<PairPose>>> pairs = new();
|
||||
private readonly Dictionary<double, BlockCatalog> blocks = new();
|
||||
private readonly IReadOnlyDictionary<string, NestJobPart> parts = job.Parts.ToDictionary(p => p.Id, StringComparer.Ordinal);
|
||||
@@ -198,6 +199,15 @@ public sealed class IrregularNestingEngine : INestingEngine
|
||||
return cache;
|
||||
}
|
||||
|
||||
private ClampObstacles? ClampsFor(NestPlateStock stock)
|
||||
{
|
||||
if (stock.ClampZones.Count == 0)
|
||||
return null;
|
||||
if (!clamps.TryGetValue(stock, out var found))
|
||||
clamps[stock] = found = new ClampObstacles(stock);
|
||||
return found;
|
||||
}
|
||||
|
||||
public bool PairFits(PartType type)
|
||||
{
|
||||
if (type.Part.Quantity < 2 || type.Orientations.Count == 0)
|
||||
@@ -265,7 +275,8 @@ public sealed class IrregularNestingEngine : INestingEngine
|
||||
if (stock.Quantity is int available && used[stock.Id] >= available)
|
||||
continue;
|
||||
progress?.Report(new NestJobProgress(NestJobStage.EvaluatingCandidate, stock.Id, sheets.Count, 0, 0));
|
||||
var packer = new FrontierPacker(types, CacheFor(stock), PairsFor(stock), stock, axis, beta, Work, BlocksFor(stock));
|
||||
var packer = new FrontierPacker(types, CacheFor(stock), PairsFor(stock), stock, axis, beta, Work,
|
||||
BlocksFor(stock), ClampsFor(stock));
|
||||
var fill = packer.Fill(remaining, token);
|
||||
sheetPacked?.Invoke();
|
||||
if (fill.Parts.Count > 0)
|
||||
|
||||
Reference in new issue
Block a user