From a9aaab8337dfcab08f377ba549be26f647d5e5c0 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 14 Mar 2026 12:41:03 -0400 Subject: [PATCH] refactor: use ShapeProfile perimeter for boundary and intersection Replace shape-list iteration with ShapeProfile.Perimeter in both Part.Intersects and PartBoundary, simplifying the logic and ensuring only the outermost contour is used for collision detection. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Part.cs | 32 +++++++++++++------------------- OpenNest.Engine/PartBoundary.cs | 28 ++++++++++++++++------------ 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/OpenNest.Core/Part.cs b/OpenNest.Core/Part.cs index e5e4adc..56598f4 100644 --- a/OpenNest.Core/Part.cs +++ b/OpenNest.Core/Part.cs @@ -149,31 +149,25 @@ namespace OpenNest pts = new List(); var entities1 = ConvertProgram.ToGeometry(Program) - .Where(e => e.Layer != SpecialLayers.Rapid); + .Where(e => e.Layer != SpecialLayers.Rapid) + .ToList(); var entities2 = ConvertProgram.ToGeometry(part.Program) - .Where(e => e.Layer != SpecialLayers.Rapid); + .Where(e => e.Layer != SpecialLayers.Rapid) + .ToList(); - var shapes1 = Helper.GetShapes(entities1); - var shapes2 = Helper.GetShapes(entities2); + if (entities1.Count == 0 || entities2.Count == 0) + return false; - shapes1.ForEach(shape => shape.Offset(Location)); - shapes2.ForEach(shape => shape.Offset(part.Location)); + var perimeter1 = new ShapeProfile(entities1).Perimeter; + var perimeter2 = new ShapeProfile(entities2).Perimeter; - for (int i = 0; i < shapes1.Count; i++) - { - var shape1 = shapes1[i]; + if (perimeter1 == null || perimeter2 == null) + return false; - for (int j = 0; j < shapes2.Count; j++) - { - var shape2 = shapes2[j]; - List pts2; + perimeter1.Offset(Location); + perimeter2.Offset(part.Location); - if (shape1.Intersects(shape2, out pts2)) - pts.AddRange(pts2); - } - } - - return pts.Count > 0; + return perimeter1.Intersects(perimeter2, out pts); } public double Left diff --git a/OpenNest.Engine/PartBoundary.cs b/OpenNest.Engine/PartBoundary.cs index 4ab96fc..2545b93 100644 --- a/OpenNest.Engine/PartBoundary.cs +++ b/OpenNest.Engine/PartBoundary.cs @@ -23,22 +23,26 @@ namespace OpenNest public PartBoundary(Part part, double spacing) { - var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = Helper.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var entities = ConvertProgram.ToGeometry(part.Program) + .Where(e => e.Layer != SpecialLayers.Rapid) + .ToList(); + + var definedShape = new ShapeProfile(entities); + var perimeter = definedShape.Perimeter; _polygons = new List(); - foreach (var shape in shapes) + if (perimeter != null) { - var offsetEntity = shape.OffsetEntity(spacing, OffsetSide.Left) as Shape; + var offsetEntity = perimeter.OffsetEntity(spacing, OffsetSide.Left) as Shape; - if (offsetEntity == null) - continue; - - // Circumscribe arcs so polygon vertices are always outside - // the true arc — guarantees the boundary never under-estimates. - var polygon = offsetEntity.ToPolygonWithTolerance(PolygonTolerance, circumscribe: true); - polygon.RemoveSelfIntersections(); - _polygons.Add(polygon); + if (offsetEntity != null) + { + // Circumscribe arcs so polygon vertices are always outside + // the true arc — guarantees the boundary never under-estimates. + var polygon = offsetEntity.ToPolygonWithTolerance(PolygonTolerance, circumscribe: true); + polygon.RemoveSelfIntersections(); + _polygons.Add(polygon); + } } PrecomputeDirectionalEdges(