From 7c4eac546058234f6e1a07eef296dd94f7515bd7 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 15 Mar 2026 17:41:40 -0400 Subject: [PATCH] refactor: extract ShapeBuilder from Helper Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Converters/ConvertGeometry.cs | 2 +- OpenNest.Core/Drawing.cs | 2 +- OpenNest.Core/Geometry/ShapeBuilder.cs | 150 ++++++++++++++++++++ OpenNest.Core/Geometry/ShapeProfile.cs | 2 +- OpenNest.Core/Helper.cs | 149 +------------------ OpenNest.Core/Timing.cs | 2 +- OpenNest.Engine/BestFit/BestFitFinder.cs | 2 +- OpenNest.Engine/BestFit/PairEvaluator.cs | 4 +- OpenNest.Engine/RotationAnalysis.cs | 4 +- OpenNest.Gpu/GpuPairEvaluator.cs | 2 +- OpenNest.Gpu/PartBitmap.cs | 4 +- OpenNest/Actions/ActionSetSequence.cs | 2 +- OpenNest/LayoutPart.cs | 2 +- 13 files changed, 168 insertions(+), 159 deletions(-) create mode 100644 OpenNest.Core/Geometry/ShapeBuilder.cs diff --git a/OpenNest.Core/Converters/ConvertGeometry.cs b/OpenNest.Core/Converters/ConvertGeometry.cs index 13cc52a..6c95e56 100644 --- a/OpenNest.Core/Converters/ConvertGeometry.cs +++ b/OpenNest.Core/Converters/ConvertGeometry.cs @@ -9,7 +9,7 @@ namespace OpenNest.Converters { public static Program ToProgram(IList geometry) { - var shapes = Helper.GetShapes(geometry); + var shapes = ShapeBuilder.GetShapes(geometry); if (shapes.Count == 0) return null; diff --git a/OpenNest.Core/Drawing.cs b/OpenNest.Core/Drawing.cs index 080e72f..2adc038 100644 --- a/OpenNest.Core/Drawing.cs +++ b/OpenNest.Core/Drawing.cs @@ -65,7 +65,7 @@ namespace OpenNest public void UpdateArea() { var geometry = ConvertProgram.ToGeometry(Program).Where(entity => entity.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(geometry); + var shapes = ShapeBuilder.GetShapes(geometry); if (shapes.Count == 0) return; diff --git a/OpenNest.Core/Geometry/ShapeBuilder.cs b/OpenNest.Core/Geometry/ShapeBuilder.cs new file mode 100644 index 0000000..8ae4cae --- /dev/null +++ b/OpenNest.Core/Geometry/ShapeBuilder.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Diagnostics; +using OpenNest.Math; + +namespace OpenNest.Geometry +{ + public static class ShapeBuilder + { + public static List GetShapes(IEnumerable entities) + { + var lines = new List(); + var arcs = new List(); + var circles = new List(); + var shapes = new List(); + + var entities2 = new Queue(entities); + + while (entities2.Count > 0) + { + var entity = entities2.Dequeue(); + + switch (entity.Type) + { + case EntityType.Arc: + arcs.Add((Arc)entity); + break; + + case EntityType.Circle: + circles.Add((Circle)entity); + break; + + case EntityType.Line: + lines.Add((Line)entity); + break; + + case EntityType.Shape: + var shape = (Shape)entity; + shape.Entities.ForEach(e => entities2.Enqueue(e)); + break; + + default: + Debug.Fail("Unhandled geometry type"); + break; + } + } + + foreach (var circle in circles) + { + var shape = new Shape(); + shape.Entities.Add(circle); + shape.UpdateBounds(); + shapes.Add(shape); + } + + var entityList = new List(); + + entityList.AddRange(lines); + entityList.AddRange(arcs); + + while (entityList.Count > 0) + { + var next = entityList[0]; + var shape = new Shape(); + shape.Entities.Add(next); + + entityList.RemoveAt(0); + + Vector startPoint = new Vector(); + Entity connected; + + switch (next.Type) + { + case EntityType.Arc: + var arc = (Arc)next; + startPoint = arc.EndPoint(); + break; + + case EntityType.Line: + var line = (Line)next; + startPoint = line.EndPoint; + break; + } + + while ((connected = GetConnected(startPoint, entityList)) != null) + { + shape.Entities.Add(connected); + entityList.Remove(connected); + + switch (connected.Type) + { + case EntityType.Arc: + var arc = (Arc)connected; + startPoint = arc.EndPoint(); + break; + + case EntityType.Line: + var line = (Line)connected; + startPoint = line.EndPoint; + break; + } + } + + shape.UpdateBounds(); + shapes.Add(shape); + } + + return shapes; + } + + internal static Entity GetConnected(Vector pt, IEnumerable geometry) + { + var tol = Tolerance.ChainTolerance; + + foreach (var geo in geometry) + { + switch (geo.Type) + { + case EntityType.Arc: + var arc = (Arc)geo; + + if (arc.StartPoint().DistanceTo(pt) <= tol) + return arc; + + if (arc.EndPoint().DistanceTo(pt) <= tol) + { + arc.Reverse(); + return arc; + } + + break; + + case EntityType.Line: + var line = (Line)geo; + + if (line.StartPoint.DistanceTo(pt) <= tol) + return line; + + if (line.EndPoint.DistanceTo(pt) <= tol) + { + line.Reverse(); + return line; + } + break; + } + } + + return null; + } + } +} diff --git a/OpenNest.Core/Geometry/ShapeProfile.cs b/OpenNest.Core/Geometry/ShapeProfile.cs index 4889df3..69f3067 100644 --- a/OpenNest.Core/Geometry/ShapeProfile.cs +++ b/OpenNest.Core/Geometry/ShapeProfile.cs @@ -16,7 +16,7 @@ namespace OpenNest.Geometry private void Update(List entities) { - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); Perimeter = shapes[0]; Cutouts = new List(); diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs index 101de75..1c0fd75 100644 --- a/OpenNest.Core/Helper.cs +++ b/OpenNest.Core/Helper.cs @@ -11,147 +11,6 @@ namespace OpenNest { public static class Helper { - public static List GetShapes(IEnumerable entities) - { - var lines = new List(); - var arcs = new List(); - var circles = new List(); - var shapes = new List(); - - var entities2 = new Queue(entities); - - while (entities2.Count > 0) - { - var entity = entities2.Dequeue(); - - switch (entity.Type) - { - case EntityType.Arc: - arcs.Add((Arc)entity); - break; - - case EntityType.Circle: - circles.Add((Circle)entity); - break; - - case EntityType.Line: - lines.Add((Line)entity); - break; - - case EntityType.Shape: - var shape = (Shape)entity; - shape.Entities.ForEach(e => entities2.Enqueue(e)); - break; - - default: - Debug.Fail("Unhandled geometry type"); - break; - } - } - - foreach (var circle in circles) - { - var shape = new Shape(); - shape.Entities.Add(circle); - shape.UpdateBounds(); - shapes.Add(shape); - } - - var entityList = new List(); - - entityList.AddRange(lines); - entityList.AddRange(arcs); - - while (entityList.Count > 0) - { - var next = entityList[0]; - var shape = new Shape(); - shape.Entities.Add(next); - - entityList.RemoveAt(0); - - Vector startPoint = new Vector(); - Entity connected; - - switch (next.Type) - { - case EntityType.Arc: - var arc = (Arc)next; - startPoint = arc.EndPoint(); - break; - - case EntityType.Line: - var line = (Line)next; - startPoint = line.EndPoint; - break; - } - - while ((connected = GetConnected(startPoint, entityList)) != null) - { - shape.Entities.Add(connected); - entityList.Remove(connected); - - switch (connected.Type) - { - case EntityType.Arc: - var arc = (Arc)connected; - startPoint = arc.EndPoint(); - break; - - case EntityType.Line: - var line = (Line)connected; - startPoint = line.EndPoint; - break; - } - } - - shape.UpdateBounds(); - shapes.Add(shape); - } - - return shapes; - } - - internal static Entity GetConnected(Vector pt, IEnumerable geometry) - { - var tol = Math.Tolerance.ChainTolerance; - - foreach (var geo in geometry) - { - switch (geo.Type) - { - case EntityType.Arc: - var arc = (Arc)geo; - - if (arc.StartPoint().DistanceTo(pt) <= tol) - return arc; - - if (arc.EndPoint().DistanceTo(pt) <= tol) - { - arc.Reverse(); - return arc; - } - - break; - - case EntityType.Line: - var line = (Line)geo; - - if (line.StartPoint.DistanceTo(pt) <= tol) - return line; - - if (line.EndPoint.DistanceTo(pt) <= tol) - { - line.Reverse(); - return line; - } - break; - } - } - - return null; - } - internal static bool Intersects(Arc arc1, Arc arc2, out List pts) { var c1 = new Circle(arc1.Center, arc1.Radius); @@ -519,7 +378,7 @@ namespace OpenNest public static List GetPartLines(Part part, double chordTolerance = 0.001) { var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); var lines = new List(); foreach (var shape in shapes) @@ -535,7 +394,7 @@ namespace OpenNest public static List GetPartLines(Part part, PushDirection facingDirection, double chordTolerance = 0.001) { var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); var lines = new List(); foreach (var shape in shapes) @@ -551,7 +410,7 @@ namespace OpenNest public static List GetOffsetPartLines(Part part, double spacing, double chordTolerance = 0.001) { var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); var lines = new List(); foreach (var shape in shapes) @@ -575,7 +434,7 @@ namespace OpenNest public static List GetOffsetPartLines(Part part, double spacing, PushDirection facingDirection, double chordTolerance = 0.001) { var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); var lines = new List(); foreach (var shape in shapes) diff --git a/OpenNest.Core/Timing.cs b/OpenNest.Core/Timing.cs index 11b85c8..7e1b401 100644 --- a/OpenNest.Core/Timing.cs +++ b/OpenNest.Core/Timing.cs @@ -11,7 +11,7 @@ namespace OpenNest public static TimingInfo GetTimingInfo(Program pgm) { var entities = ConvertProgram.ToGeometry(pgm); - var shapes = Helper.GetShapes(entities.Where(entity => entity.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(entity => entity.Layer != SpecialLayers.Rapid)); var info = new TimingInfo { PierceCount = shapes.Count }; var last = entities[0]; diff --git a/OpenNest.Engine/BestFit/BestFitFinder.cs b/OpenNest.Engine/BestFit/BestFitFinder.cs index b86bc6a..49a4121 100644 --- a/OpenNest.Engine/BestFit/BestFitFinder.cs +++ b/OpenNest.Engine/BestFit/BestFitFinder.cs @@ -116,7 +116,7 @@ namespace OpenNest.Engine.BestFit { var entities = ConvertProgram.ToGeometry(drawing.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var points = new List(); diff --git a/OpenNest.Engine/BestFit/PairEvaluator.cs b/OpenNest.Engine/BestFit/PairEvaluator.cs index bffc7c6..c48f1f0 100644 --- a/OpenNest.Engine/BestFit/PairEvaluator.cs +++ b/OpenNest.Engine/BestFit/PairEvaluator.cs @@ -103,7 +103,7 @@ namespace OpenNest.Engine.BestFit { var entities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); shapes.ForEach(s => s.Offset(part.Location)); return shapes; } @@ -112,7 +112,7 @@ namespace OpenNest.Engine.BestFit { var entities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var points = new List(); foreach (var shape in shapes) diff --git a/OpenNest.Engine/RotationAnalysis.cs b/OpenNest.Engine/RotationAnalysis.cs index 8aa7aa5..0d7e20f 100644 --- a/OpenNest.Engine/RotationAnalysis.cs +++ b/OpenNest.Engine/RotationAnalysis.cs @@ -17,7 +17,7 @@ namespace OpenNest var entities = ConvertProgram.ToGeometry(item.Drawing.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); if (shapes.Count == 0) return 0; @@ -65,7 +65,7 @@ namespace OpenNest var entities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); foreach (var shape in shapes) { diff --git a/OpenNest.Gpu/GpuPairEvaluator.cs b/OpenNest.Gpu/GpuPairEvaluator.cs index 17c2871..4a6e502 100644 --- a/OpenNest.Gpu/GpuPairEvaluator.cs +++ b/OpenNest.Gpu/GpuPairEvaluator.cs @@ -258,7 +258,7 @@ namespace OpenNest.Gpu { var entities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var points = new List(); foreach (var shape in shapes) diff --git a/OpenNest.Gpu/PartBitmap.cs b/OpenNest.Gpu/PartBitmap.cs index 97b3af3..9b729ef 100644 --- a/OpenNest.Gpu/PartBitmap.cs +++ b/OpenNest.Gpu/PartBitmap.cs @@ -47,7 +47,7 @@ namespace OpenNest.Gpu { var entities = ConvertProgram.ToGeometry(part.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var polygons = new List(); @@ -137,7 +137,7 @@ namespace OpenNest.Gpu { var entities = ConvertProgram.ToGeometry(drawing.Program) .Where(e => e.Layer != SpecialLayers.Rapid); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var polygons = new List(); diff --git a/OpenNest/Actions/ActionSetSequence.cs b/OpenNest/Actions/ActionSetSequence.cs index e76e4e2..fb2ed33 100644 --- a/OpenNest/Actions/ActionSetSequence.cs +++ b/OpenNest/Actions/ActionSetSequence.cs @@ -52,7 +52,7 @@ namespace OpenNest.Actions { var entities = ConvertProgram.ToGeometry(part.Program).Where(e => e.Layer == SpecialLayers.Cut).ToList(); entities.ForEach(entity => entity.Offset(part.Location)); - var shapes = Helper.GetShapes(entities); + var shapes = ShapeBuilder.GetShapes(entities); var shape = new Shape(); shape.Entities.AddRange(shapes); ShapePartPairs.Add(new Pair() { Part = part, Shape = shape }); diff --git a/OpenNest/LayoutPart.cs b/OpenNest/LayoutPart.cs index 94685d6..b66a64f 100644 --- a/OpenNest/LayoutPart.cs +++ b/OpenNest/LayoutPart.cs @@ -134,7 +134,7 @@ namespace OpenNest { var result = new List(); var entities = ConvertProgram.ToGeometry(BasePart.Program); - var shapes = Helper.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); foreach (var shape in shapes) {