diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs
index a2d9792..ab32d66 100644
--- a/OpenNest.Core/Geometry/Shape.cs
+++ b/OpenNest.Core/Geometry/Shape.cs
@@ -298,6 +298,7 @@ namespace OpenNest.Geometry
polygon.Close();
polygon.Cleanup();
+ polygon.UpdateBounds();
return polygon;
}
@@ -341,6 +342,7 @@ namespace OpenNest.Geometry
polygon.Close();
polygon.Cleanup();
+ polygon.UpdateBounds();
return polygon;
}
diff --git a/OpenNest.Core/Part.cs b/OpenNest.Core/Part.cs
index 312c83c..d4df8d8 100644
--- a/OpenNest.Core/Part.cs
+++ b/OpenNest.Core/Part.cs
@@ -20,6 +20,13 @@ namespace OpenNest
public class Part : IPart, IBoundable
{
+ ///
+ /// Chord tolerance used to polygonize arcs/circles for overlap testing in
+ /// . Matches the tolerance used elsewhere for
+ /// geometry-sensitive checks (e.g. ).
+ ///
+ private const double IntersectsChordTolerance = 0.001;
+
private Vector location;
private bool ownsProgram;
private double preLeadInRotation;
@@ -247,8 +254,8 @@ namespace OpenNest
if (perimeter1 == null || perimeter2 == null)
return false;
- var polygon1 = perimeter1.ToPolygon();
- var polygon2 = perimeter2.ToPolygon();
+ var polygon1 = perimeter1.ToPolygonWithTolerance(IntersectsChordTolerance);
+ var polygon2 = perimeter2.ToPolygonWithTolerance(IntersectsChordTolerance);
if (polygon1 == null || polygon2 == null)
return false;
diff --git a/OpenNest.Engine/BestFit/PairEvaluator.cs b/OpenNest.Engine/BestFit/PairEvaluator.cs
index 0e6b899..c54ec5e 100644
--- a/OpenNest.Engine/BestFit/PairEvaluator.cs
+++ b/OpenNest.Engine/BestFit/PairEvaluator.cs
@@ -13,6 +13,15 @@ namespace OpenNest.Engine.BestFit
{
private const double ChordTolerance = 0.01;
+ ///
+ /// Tighter chord tolerance for the overlap check only. Rounded-corner arcs
+ /// polygonized at the coarser can "cut the corner"
+ /// enough to hide a genuine but tiny sliver overlap between two candidates —
+ /// this needs to match the precision Part.Intersects uses elsewhere so BestFit's
+ /// Keep decision agrees with the same overlap check callers rely on downstream.
+ ///
+ private const double OverlapChordTolerance = 0.001;
+
public List EvaluateAll(List candidates)
{
if (candidates.Count == 0)
@@ -49,10 +58,18 @@ namespace OpenNest.Engine.BestFit
part2.Location = candidate.Part2Offset;
part2.UpdateBounds();
- // Overlap check — perimeter vs perimeter
+ // Overlap check — perimeter vs perimeter. Uses Collision.HasOverlap (full polygon
+ // clip) rather than Shape.Intersects (edge-crossing only), which misses containment-
+ // style overlaps where one perimeter's boundary never crosses the other's.
var shape1 = GetPerimeterShape(part1);
var shape2 = GetPerimeterShape(part2);
- var overlaps = shape1 != null && shape2 != null && shape1.Intersects(shape2, out _);
+ var overlaps =
+ shape1 != null
+ && shape2 != null
+ && Collision.HasOverlap(
+ shape1.ToPolygonWithTolerance(OverlapChordTolerance),
+ shape2.ToPolygonWithTolerance(OverlapChordTolerance)
+ );
// Convex hull vertices from perimeter polygons only
var allPoints = GetPartVertices(part1);