mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-11 05:21:08 -05:00
fix(engine): keep Strip layouts clear of clamp keep-outs
The Strip whole-job engine laid its strips, settled them and picked its pack remnant from the bare work area, never adding the plate's clamp keep-outs the Lattice orchestration uses. On a clamped sheet it put parts over the clamp, the runner's layout check rejected the candidate, and Strip produced nothing (N26-2A5 rev 5: door stop at (21.09,1.50) over clamp-1). NestStrips now treats InteractiveClamps.ClearanceRectangles as occupied space for the iterative shrink strips (through RemnantFiller), settles with the clamp obstacles when there are zones, and finds the pack remnant around them even before any strip is placed. Unclamped plates take the old path unchanged. A FixedStrategyNestingEngine theory runs all four fixed strategies on a clamped 26 x 26 sheet; only Strip failed it before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6894fcfade
commit
7dc471bbf8
3 files changed
+44
-5
No files matched your search
@@ -51,6 +51,36 @@ public class FixedStrategyNestingEngineTests
|
||||
Assert.Single(result.Plates);
|
||||
}
|
||||
|
||||
public static TheoryData<string> Strategies =>
|
||||
new() { "Lattice", "Strip", "Vertical Remnant", "Horizontal Remnant" };
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Strategies))]
|
||||
public void ClampedSheet_KeepsTheClampClearance(string strategy)
|
||||
{
|
||||
// N26-2A5 rev 5: a 26 x 26 sheet with one rail clamp on its front edge. Strip used to
|
||||
// lay its strips and pack its leftovers across the clamp, so the runner's validation
|
||||
// threw every layout away.
|
||||
var clamp = new ClampZone("clamp-1", new Box(19.25, 0, 1.5, 0.625));
|
||||
var stock = new NestPlateStock("sheet", new Size(26, 26), 1, 0.25,
|
||||
new Spacing(0.25, 0.25, 0.25, 0.25), 1, null, [clamp]);
|
||||
var parts = new[]
|
||||
{
|
||||
new NestJobPart("stop",
|
||||
PartGeometrySnapshot.FromProgram(TestDrawingFactory.Rectangle(3, 1.5)), 14),
|
||||
new NestJobPart("plate",
|
||||
PartGeometrySnapshot.FromProgram(TestDrawingFactory.Rectangle(4, 2.5)), 14),
|
||||
};
|
||||
var job = new NestJob(parts, new[] { stock }, new NestJobOptions(strategy));
|
||||
|
||||
var result = new FixedStrategyNestingEngine(strategy).Solve(job);
|
||||
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
Assert.Equal(28, result.Plates.Sum(p => p.Placements.Count));
|
||||
// NestLayoutCheck holds every part the clamp clearance away from the zone.
|
||||
LayoutAssert.Valid(job, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsNullOrWhitespaceStrategyAtConstruction()
|
||||
{
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace OpenNest.Engine.Fill
|
||||
CancellationToken token = default,
|
||||
IProgress<NestProgress> progress = null,
|
||||
int plateNumber = 0,
|
||||
Func<NestItem, Box, List<Part>> widthFillFunc = null
|
||||
Func<NestItem, Box, List<Part>> widthFillFunc = null,
|
||||
IEnumerable<Box> keepOuts = null
|
||||
)
|
||||
{
|
||||
if (items == null || items.Count == 0)
|
||||
@@ -71,7 +72,7 @@ namespace OpenNest.Engine.Fill
|
||||
}
|
||||
}
|
||||
|
||||
var filler = new RemnantFiller(workArea, spacing);
|
||||
var filler = new RemnantFiller(workArea, spacing, keepOuts);
|
||||
|
||||
// Track parts placed by previous items so ShrinkFiller can
|
||||
// include them in progress reports.
|
||||
|
||||
@@ -515,6 +515,9 @@ internal class PlateFiller
|
||||
return new List<Part>();
|
||||
|
||||
var workArea = Plate.WorkArea();
|
||||
// Clamp keep-outs (zones grown by the clamp clearance) are occupied space for the
|
||||
// strips, the settle and the pack remnant alike, as in the Lattice orchestration.
|
||||
var clampKeepOuts = InteractiveClamps.ClearanceRectangles(Plate);
|
||||
var fillItems = items
|
||||
.Where(item => item.Quantity != 1)
|
||||
.OrderByDescending(item => item.Priority)
|
||||
@@ -546,11 +549,15 @@ internal class PlateFiller
|
||||
token,
|
||||
progress,
|
||||
PlateNumber,
|
||||
widthFillFunc
|
||||
widthFillFunc,
|
||||
clampKeepOuts
|
||||
);
|
||||
|
||||
allParts.AddRange(shrinkResult.Parts);
|
||||
Compactor.Settle(allParts, workArea, Plate.PartSpacing);
|
||||
if (clampKeepOuts.Count > 0)
|
||||
Compactor.Settle(allParts, Plate, workArea);
|
||||
else
|
||||
Compactor.Settle(allParts, workArea, Plate.PartSpacing);
|
||||
packItems.AddRange(shrinkResult.Leftovers);
|
||||
}
|
||||
|
||||
@@ -558,11 +565,12 @@ internal class PlateFiller
|
||||
if (packItems.Count > 0 && !token.IsCancellationRequested)
|
||||
{
|
||||
var packArea = workArea;
|
||||
if (allParts.Count > 0)
|
||||
if (allParts.Count > 0 || clampKeepOuts.Count > 0)
|
||||
{
|
||||
var obstacles = allParts
|
||||
.Select(part => part.BoundingBox.Offset(Plate.PartSpacing))
|
||||
.ToList();
|
||||
obstacles.AddRange(clampKeepOuts);
|
||||
var finder = new RemnantFinder(workArea, obstacles);
|
||||
var remnants = finder.FindRemnants();
|
||||
packArea = remnants.Count > 0 ? remnants[0] : new Box(0, 0, 0, 0);
|
||||
|
||||
Reference in new issue
Block a user