diff --git a/OpenNest.Benchmark/NestValidator.cs b/OpenNest.Benchmark/NestValidator.cs
index 934a835..4b0b276 100644
--- a/OpenNest.Benchmark/NestValidator.cs
+++ b/OpenNest.Benchmark/NestValidator.cs
@@ -31,11 +31,34 @@ namespace OpenNest.Benchmark
ValidateQuantities(parts, job, result);
ValidateBounds(parts, plate, result);
+ ValidateAreaBudget(parts, plate, result);
ValidateSpacing(parts, plate.PartSpacing, result);
return result;
}
+ ///
+ /// Hard mathematical backstop: non-overlapping parts confined to the work
+ /// area can never have a combined area greater than the work area itself.
+ /// This catches overlap that the polygon-based ValidateSpacing check can
+ /// miss - Collision.HasOverlap (and Part.Intersects, which uses the same
+ /// algorithm) has been observed to return false negatives on real,
+ /// complex production geometry, so this check does not depend on it.
+ ///
+ private static void ValidateAreaBudget(List parts, Plate plate, ValidationResult result)
+ {
+ var workArea = plate.WorkArea();
+ var budget = workArea.Width * workArea.Length;
+ var placedArea = parts.Sum(p => p.BaseDrawing.Area);
+
+ if (placedArea > budget + Tolerance.Epsilon)
+ {
+ result.Violations.Add(
+ $"Combined placed area ({placedArea:F2}) exceeds the work area ({budget:F2}) - " +
+ "parts must overlap even though the polygon overlap check did not flag a pair");
+ }
+ }
+
private static void ValidateQuantities(List parts, BenchmarkJob job, ValidationResult result)
{
var allowed = job.Requests.ToDictionary(r => r.Drawing.Id, r => r.Quantity);
@@ -132,7 +155,10 @@ namespace OpenNest.Benchmark
if (inflateBy > Tolerance.Epsilon)
perimeter = perimeter.OffsetOutward(inflateBy) ?? perimeter;
- var polygon = perimeter.ToPolygon();
+ // Adaptive tolerance instead of Shape.ToPolygon()'s default (up to 1000
+ // segments per arc) - arc-heavy real parts otherwise produce thousands
+ // of vertices, which is needlessly slow for a spacing check.
+ var polygon = perimeter.ToPolygonWithTolerance(0.01, circumscribe: true);
if (polygon == null)
return null;