fix(rectangles): place parts within the layout check's work-area slack

Parts drawn a few millionths over their sheet's work area (for example a
36.125006 panel on a 36.125 sheet with no edge spacing) pass NestLayoutCheck,
which allows Tolerance.Epsilon of overhang, but the Rectangles engine refused
them and left them unplaced. The engine now allows 90% of that slack: a box
that exceeds the sheet by no more than the allowance packs as exactly the
sheet's size, so it spans the axis and the overhang lands only past the edge.

The check's bounds slack is named NestTolerances.WorkAreaSlack (same value,
no behavior change) so the engine and the test layout assertion share it.

Rectangle-lane benchmark (91 strict + 77 boxable jobs): every layout valid;
4 jobs that left panels unplaced now place them (strict complete 86 -> 88,
boxable complete 74 -> 75, one more boxable job places 2 more parts); no other
job changed.
This commit is contained in:
aj
2026-09-30 22:58:41 -04:00
parent 4b6e131cec
commit 7ce53008f1
6 changed files with 65 additions and 9 deletions
@@ -152,6 +152,36 @@ public class RectanglesNestingEngineTests
LayoutAssert.Valid(job, result);
Assert.Equal(NestJobStatus.Complete, result.Status);
}
[Fact]
public void PartAFewMillionthsWiderThanTheWorkAreaIsPlaced()
{
// Exported panels are often drawn a few millionths over their sheet's work area; the
// layout check accepts that overhang, so the engine must place them. The chamfer keeps the
// material area inside the sheet's area budget, as a real panel's corner radii do.
var panel = Polyline((0, 0), (36.125006, 0), (36.125006, 74), (35.125006, 75), (0, 75));
var job = Job(new[] { Part("panel", panel, 1) },
new[] { Stock("s", 75, 36.125, spacing: 0.25, quantity: 1) });
var result = new RectanglesNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Equal(NestJobStatus.Complete, result.Status);
}
[Fact]
public void PartBeyondTheOverhangAllowanceStaysUnplaced()
{
var panel = Polyline((0, 0), (36.12502, 0), (36.12502, 74), (35.12502, 75), (0, 75));
var job = Job(new[] { Part("panel", panel, 1) },
new[] { Stock("s", 75, 36.125, spacing: 0.25, quantity: 1) });
var result = new RectanglesNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Empty(result.Plates);
Assert.Equal(1, Assert.Single(result.Fulfillment).Unplaced);
}
}
public sealed class RectanglesContractTests : EngineContractTests<RectanglesNestingEngine> { }