From 8ad01bb7f8bb833e2469a0314ae9263d9455d666 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 9 Mar 2026 22:59:06 -0400 Subject: [PATCH] perf: pre-compute directional edges in PartBoundary constructor RotationDirection and direction filtering were recomputed on every GetLines call. Pre-compute edges per PushDirection once in the constructor so GetLines only translates cached edges. Co-Authored-By: Claude Opus 4.6 --- OpenNest.Engine/PartBoundary.cs | 123 ++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/OpenNest.Engine/PartBoundary.cs b/OpenNest.Engine/PartBoundary.cs index 490e901..8aafa56 100644 --- a/OpenNest.Engine/PartBoundary.cs +++ b/OpenNest.Engine/PartBoundary.cs @@ -9,12 +9,17 @@ namespace OpenNest /// Pre-computed offset boundary polygons for a part's geometry. /// Polygons are stored at program-local origin (no location applied) /// and can be efficiently translated to any location when extracting lines. + /// Directional edge filtering is pre-computed once in the constructor. /// public class PartBoundary { private const double ChordTolerance = 0.01; private readonly List _polygons; + private readonly (Vector start, Vector end)[] _leftEdges; + private readonly (Vector start, Vector end)[] _rightEdges; + private readonly (Vector start, Vector end)[] _upEdges; + private readonly (Vector start, Vector end)[] _downEdges; public PartBoundary(Part part, double spacing) { @@ -33,6 +38,59 @@ namespace OpenNest polygon.RemoveSelfIntersections(); _polygons.Add(polygon); } + + PrecomputeDirectionalEdges( + out _leftEdges, out _rightEdges, out _upEdges, out _downEdges); + } + + private void PrecomputeDirectionalEdges( + out (Vector start, Vector end)[] leftEdges, + out (Vector start, Vector end)[] rightEdges, + out (Vector start, Vector end)[] upEdges, + out (Vector start, Vector end)[] downEdges) + { + var left = new List<(Vector, Vector)>(); + var right = new List<(Vector, Vector)>(); + var up = new List<(Vector, Vector)>(); + var down = new List<(Vector, Vector)>(); + + foreach (var polygon in _polygons) + { + var verts = polygon.Vertices; + + if (verts.Count < 3) + { + for (var i = 1; i < verts.Count; i++) + { + var edge = (verts[i - 1], verts[i]); + left.Add(edge); + right.Add(edge); + up.Add(edge); + down.Add(edge); + } + + continue; + } + + var sign = polygon.RotationDirection() == RotationType.CCW ? 1.0 : -1.0; + + for (var i = 1; i < verts.Count; i++) + { + var dx = verts[i].X - verts[i - 1].X; + var dy = verts[i].Y - verts[i - 1].Y; + var edge = (verts[i - 1], verts[i]); + + if (-sign * dy > 0) left.Add(edge); + if ( sign * dy > 0) right.Add(edge); + if (-sign * dx > 0) up.Add(edge); + if ( sign * dx > 0) down.Add(edge); + } + } + + leftEdges = left.ToArray(); + rightEdges = right.ToArray(); + upEdges = up.ToArray(); + downEdges = down.ToArray(); } /// @@ -41,10 +99,11 @@ namespace OpenNest /// public List GetLines(Vector location, PushDirection facingDirection) { - var lines = new List(); + var edges = GetDirectionalEdges(facingDirection); + var lines = new List(edges.Length); - foreach (var polygon in _polygons) - lines.AddRange(TranslateDirectionalLines(polygon, location, facingDirection)); + foreach (var (start, end) in edges) + lines.Add(new Line(start.Offset(location), end.Offset(location))); return lines; } @@ -76,60 +135,16 @@ namespace OpenNest return lines; } - private static List TranslateDirectionalLines( - Polygon polygon, Vector location, PushDirection facingDirection) + private (Vector start, Vector end)[] GetDirectionalEdges(PushDirection direction) { - var verts = polygon.Vertices; - - if (verts.Count < 3) + switch (direction) { - var fallback = new List(); - - if (verts.Count >= 2) - { - var last = verts[0].Offset(location); - - for (var i = 1; i < verts.Count; i++) - { - var current = verts[i].Offset(location); - fallback.Add(new Line(last, current)); - last = current; - } - } - - return fallback; + case PushDirection.Left: return _leftEdges; + case PushDirection.Right: return _rightEdges; + case PushDirection.Up: return _upEdges; + case PushDirection.Down: return _downEdges; + default: return _leftEdges; } - - var sign = polygon.RotationDirection() == RotationType.CCW ? 1.0 : -1.0; - var result = new List(); - var prev = verts[0].Offset(location); - - for (var i = 1; i < verts.Count; i++) - { - var current = verts[i].Offset(location); - - // Use un-translated deltas — translation doesn't affect direction. - var dx = verts[i].X - verts[i - 1].X; - var dy = verts[i].Y - verts[i - 1].Y; - - bool keep; - - switch (facingDirection) - { - case PushDirection.Left: keep = -sign * dy > 0; break; - case PushDirection.Right: keep = sign * dy > 0; break; - case PushDirection.Up: keep = -sign * dx > 0; break; - case PushDirection.Down: keep = sign * dx > 0; break; - default: keep = true; break; - } - - if (keep) - result.Add(new Line(prev, current)); - - prev = current; - } - - return result; } } }