From 0eb6d5886a23630a841942701fe834430e84fcdc Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 20 Oct 2026 13:32:52 -0400 Subject: [PATCH] perf(geometry): flatten Shape.ToPolygon by chord tolerance by default ToPolygon() used 1000 segments for every arc and circle regardless of size: no more accurate for large arcs and needlessly dense for small ones, which made every Drawing area and canonical-angle computation on an arc-heavy part handle hundreds of thousands of vertices. The parameterless overload now delegates to ToPolygonWithTolerance with DefaultChordTolerance = 0.0001. That is ten times tighter than the spacing and overlap checks' 0.001 because callers use this polygon for area, which an inscribed polygon always under-reads: at 0.001, seven area-sensitive cut-off, splitter and circle-packing tests drifted past their precision; at 0.0001 all pass. ToPolygon(int) keeps the fixed segment count for any caller that wants it. With the one-pass Cleanup, a 300-nest PEP archive import on the forge server went from 46.1 s to 15.5 s. Co-Authored-By: Claude Opus 5.5 --- OpenNest.Core/Geometry/Shape.cs | 20 +++++++++++++++++--- OpenNest.Core/Geometry/SpacingOutline.cs | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs index 21463511..e4cf126a 100644 --- a/OpenNest.Core/Geometry/Shape.cs +++ b/OpenNest.Core/Geometry/Shape.cs @@ -264,10 +264,24 @@ namespace OpenNest.Geometry } /// - /// Converts the shape to a polygon. + /// Maximum chord error, in drawing units, when flattens arcs + /// and circles. Ten times tighter than the spacing/overlap checks' 0.001 because callers + /// use this polygon for area, and an inscribed polygon always reads low. /// - /// - public Polygon ToPolygon(int arcSegments = 1000) + public const double DefaultChordTolerance = 0.0001; + + /// + /// Converts the shape to a polygon, flattening arcs to within + /// . A fixed segment count per arc (formerly 1000) + /// made large arcs no more accurate and small ones needlessly dense: arc-heavy drawings + /// produced hundreds of thousands of vertices every time their area was computed. + /// + public Polygon ToPolygon() => ToPolygonWithTolerance(DefaultChordTolerance); + + /// + /// Converts the shape to a polygon with a fixed number of segments per arc and circle. + /// + public Polygon ToPolygon(int arcSegments) { var polygon = new Polygon(); diff --git a/OpenNest.Core/Geometry/SpacingOutline.cs b/OpenNest.Core/Geometry/SpacingOutline.cs index 8ee0545c..a89e812d 100644 --- a/OpenNest.Core/Geometry/SpacingOutline.cs +++ b/OpenNest.Core/Geometry/SpacingOutline.cs @@ -64,8 +64,8 @@ namespace OpenNest.Geometry public static SpacingOutline Build(ShapeProfile profile, Vector location, double inflateBy) { ArgumentNullException.ThrowIfNull(profile); - // Adaptive tolerance instead of Shape.ToPolygon()'s default (up to 1000 segments per - // arc): arc-heavy real parts otherwise produce thousands of vertices. + // Flatten by chord tolerance, not a fixed segment count: arc-heavy real parts + // otherwise produce thousands of vertices. var region = ClipperBridge.OffsetForValidation( profile, inflateBy > Tolerance.Epsilon ? inflateBy : 0,