From e1b6752ede989e138ea84112b89e053e8fbfed5e Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 27 Mar 2026 14:13:21 -0400 Subject: [PATCH] fix: improve overlap detection to ignore touch points and add bounding box pre-filtering Part.Intersects now filters out intersection points that coincide with vertices of both perimeters (shared corners/endpoints), which are touch points rather than actual crossings. Plate.HasOverlappingParts adds a bounding box pre-filter requiring overlap region to exceed Epsilon in both dimensions before performing expensive shape intersection checks. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Part.cs | 49 +++++++++++++++++++++++++++++++++++++++++- OpenNest.Core/Plate.cs | 14 ++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/OpenNest.Core/Part.cs b/OpenNest.Core/Part.cs index b47793c..1bfdd7f 100644 --- a/OpenNest.Core/Part.cs +++ b/OpenNest.Core/Part.cs @@ -1,6 +1,7 @@ using OpenNest.CNC; using OpenNest.Converters; using OpenNest.Geometry; +using OpenNest.Math; using System.Collections.Generic; using System.Linq; @@ -173,7 +174,53 @@ namespace OpenNest perimeter1.Offset(Location); perimeter2.Offset(part.Location); - return perimeter1.Intersects(perimeter2, out pts); + if (!perimeter1.Intersects(perimeter2, out var rawPts)) + return false; + + // Exclude intersection points that coincide with vertices of BOTH + // perimeters — these are touch points (shared corners/endpoints), + // not actual crossings where one shape enters the other's interior. + var verts1 = CollectVertices(perimeter1); + var verts2 = CollectVertices(perimeter2); + + foreach (var pt in rawPts) + { + if (IsNearAnyVertex(pt, verts1) && IsNearAnyVertex(pt, verts2)) + continue; + pts.Add(pt); + } + + return pts.Count > 0; + } + + private static List CollectVertices(Geometry.Shape shape) + { + var verts = new List(); + foreach (var entity in shape.Entities) + { + switch (entity) + { + case Geometry.Line line: + verts.Add(line.StartPoint); + verts.Add(line.EndPoint); + break; + case Geometry.Arc arc: + verts.Add(arc.StartPoint()); + verts.Add(arc.EndPoint()); + break; + } + } + return verts; + } + + private static bool IsNearAnyVertex(Vector pt, List vertices) + { + foreach (var v in vertices) + { + if (pt.X.IsEqualTo(v.X) && pt.Y.IsEqualTo(v.Y)) + return true; + } + return false; } public double Left diff --git a/OpenNest.Core/Plate.cs b/OpenNest.Core/Plate.cs index 665f7e6..9d974ae 100644 --- a/OpenNest.Core/Plate.cs +++ b/OpenNest.Core/Plate.cs @@ -601,10 +601,24 @@ namespace OpenNest for (var i = 0; i < realParts.Count; i++) { var part1 = realParts[i]; + var b1 = part1.BoundingBox; for (var j = i + 1; j < realParts.Count; j++) { var part2 = realParts[j]; + var b2 = part2.BoundingBox; + + // Skip pairs whose bounding boxes don't meaningfully overlap. + // Floating-point rounding can produce sub-epsilon overlaps for + // parts that are merely edge-touching, so require the overlap + // region to exceed Epsilon in both dimensions. + var overlapX = System.Math.Min(b1.Right, b2.Right) + - System.Math.Max(b1.Left, b2.Left); + var overlapY = System.Math.Min(b1.Top, b2.Top) + - System.Math.Max(b1.Bottom, b2.Bottom); + + if (overlapX <= Math.Tolerance.Epsilon || overlapY <= Math.Tolerance.Epsilon) + continue; if (part1.Intersects(part2, out var pts2)) pts.AddRange(pts2);