fix(engine): allow 0.0005 spacing slack in layout validation

Layouts placed exactly at the part spacing can land ~1e-4 short once
rotated, rounded (e.g. PEP's 4-decimal exports) and snapped to the
Clipper grid, so both validators rejected layouts that were correct in
practice. NestTolerances.SpacingSlack (0.0005, far below anything a
cutting machine resolves) is now subtracted from the spacing by
NestLayoutCheck's inflation and NestJobPlacementValidator's edge-distance
check. The frozen LegacyNestValidator takes the same rule so the
equivalence tests keep comparing like with like.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
aj
2026-09-25 14:19:04 -04:00
co-authored by Claude Opus 5.5
parent d1f3907505
commit 1b862dc1a8
6 changed files with 47 additions and 10 deletions
@@ -15,7 +15,6 @@ namespace OpenNest.Engine.Tests.Jobs;
public class NestJobSpacingValidationTests
{
private const double Spacing = 0.25;
private const double Epsilon = 0.0000001;
private const double Origin = 50;
public static IEnumerable<object[]> Shapes() =>
@@ -98,6 +97,27 @@ public class NestJobSpacingValidationTests
));
}
[Theory]
[InlineData(0.0001, true)]
[InlineData(0.0004, true)]
[InlineData(0.0010, false)]
public void GapsJustUnderSpacingPassWithinTheSlack(double shortfall, bool expected)
{
// A layout placed at the spacing can land ~1e-4 short once rotated and rounded.
var program = TestDrawingFactory.Rectangle(4, 3);
var part = new NestJobPart("part", PartGeometrySnapshot.FromProgram(program), 2);
var job = new NestJob(
new[] { part },
new[] { new NestPlateStock("stock", new Size(200, 200), 1, Spacing) }
);
var first = new NestJobPlacement("part", 0, Origin, Origin, 0);
var second = new NestJobPlacement("part", 1, Origin + 4 + Spacing - shortfall, Origin, 0);
Assert.Equal(expected, IsValid(job, first, second));
var geometry = JobPartGeometry.Read(part.Geometry);
Assert.Equal(expected, NestLayoutCheck.Clears(geometry, first, geometry, second, Spacing));
}
private static bool IsValid(NestJob job, params NestJobPlacement[] placements)
{
try
@@ -123,7 +143,7 @@ public class NestJobSpacingValidationTests
if (Collision.HasOverlap(a[0], b[0], a.Skip(1).ToList(), b.Skip(1).ToList()))
return true;
var limit = Spacing - Epsilon;
var limit = Spacing - NestTolerances.SpacingSlack;
foreach (var left in a)
{
var leftLines = left.ToLines();