refactor(engine): expose the layout validation contract to engines

Engines had to reverse-engineer the benchmark validator: Opus55 assumed a
0.01 arc tolerance (the validator uses 0.001), Gpt6Astra added hand-tuned
paddings and copied the validator's check order, Qwen picked its chord
tolerance to stay under a constant it could not reference.

NestTolerances publishes the validator's arc tolerance, the Clipper grid
and SafeClearanceMargin (with its derivation). NestLayoutCheck moves the
benchmark NestValidator's checks into OpenNest.Engine as a public API
(Clears for a part pair, Violations for a whole result); NestValidator is
now a thin wrapper. Verdicts are unchanged: tests compare ordered
violation lists against a frozen copy of the old validator, and a
tangent-disc stress test covers 432 pairs at the safe margin.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
aj
2026-09-25 08:54:54 -04:00
co-authored by Codex Claude Opus 5.5
parent ec5f57171f
commit 7f63c725e6
8 changed files with 997 additions and 374 deletions
+29
View File
@@ -0,0 +1,29 @@
using System;
using OpenNest.Geometry;
namespace OpenNest.Engine.Jobs;
/// <summary>Shared numerical contract for layout validation.</summary>
public static class NestTolerances
{
/// <summary>Arc chord tolerance used by both validators. The layout check circumscribes
/// perimeter arcs and inscribes cutouts; the placement validator uses inscribed arcs.</summary>
public const double ValidationOutline = 0.001;
/// <summary>Clipper decimal precision (a 1e-4 coordinate grid).</summary>
public const int ClipperPrecision = ClipperBridge.Precision;
/// <summary>Extra pair clearance for an engine whose outline error is bounded by t.
/// Each of two boundaries contributes ValidationOutline from validator flattening,
/// one Clipper grid unit from rounding, and t from engine flattening: therefore
/// 2 * ValidationOutline + 2 * 10^(-ClipperPrecision) + 2 * t.
/// This budget assumes valid material geometry and bounded chord error on both sides.</summary>
/// <exception cref="ArgumentOutOfRangeException">Tolerance is negative or non-finite.</exception>
public static double SafeClearanceMargin(double engineChordTolerance)
{
if (!double.IsFinite(engineChordTolerance) || engineChordTolerance < 0)
throw new ArgumentOutOfRangeException(nameof(engineChordTolerance));
return 2 * ValidationOutline + 2 * System.Math.Pow(10, -ClipperPrecision)
+ 2 * engineChordTolerance;
}
}