From 9ab6fbbeafb6bb092f393bf0d90435cdbffc2551 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 20 Oct 2026 13:32:50 -0400 Subject: [PATCH] perf(geometry): compact Polygon.Cleanup in one pass Cleanup removed each duplicate vertex with List.RemoveAt from the middle of the list, so a polygon cost O(duplicates x vertices). Shape.ToPolygon emits a duplicate at every entity junction and flattened arcs to 1000 points, so building the Drawing for a large arc-heavy part spent seconds shifting memory: one 1 MB PEP nest took 12.7 s to convert and 11.2 s to read back, a quarter of a 300-nest archive import's total time. The single pass compares each vertex with its original predecessor, as the back-to-front loop did, so tolerance-equal runs collapse identically and the output is unchanged. Co-Authored-By: Claude Opus 5.5 --- OpenNest.Core/Geometry/Polygon.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/OpenNest.Core/Geometry/Polygon.cs b/OpenNest.Core/Geometry/Polygon.cs index a8e66dd6..b5f9bc0c 100644 --- a/OpenNest.Core/Geometry/Polygon.cs +++ b/OpenNest.Core/Geometry/Polygon.cs @@ -519,16 +519,33 @@ namespace OpenNest.Geometry get { return EntityType.Polygon; } } + /// + /// Drops each vertex equal to the one before it in the original sequence. Compacts in + /// place in one pass: removing duplicates one at a time from the middle of the list was + /// O(duplicates x vertices), which dominated loading large drawings whose arcs are + /// flattened to many points. + /// internal void Cleanup() { - for (int i = Vertices.Count - 1; i > 0; i--) + if (Vertices.Count < 2) + return; + + // Compare against the original predecessor, not the last kept vertex, so a run of + // tolerance-equal points collapses exactly as the old back-to-front RemoveAt loop did. + var previous = Vertices[0]; + var kept = 1; + + for (int i = 1; i < Vertices.Count; i++) { var vertex = Vertices[i]; - var nextVertex = Vertices[i - 1]; - if (vertex == nextVertex) - Vertices.RemoveAt(i); + if (vertex != previous) + Vertices[kept++] = vertex; + + previous = vertex; } + + Vertices.RemoveRange(kept, Vertices.Count - kept); } public BoundingRectangleResult FindBestRotation()