From 2881815c7aea3ff7e159367bfa49a491ece0665a Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 15 Mar 2026 17:44:17 -0400 Subject: [PATCH] refactor: extract PartGeometry from Helper Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Helper.cs | 116 ---------------- OpenNest.Core/PartGeometry.cs | 126 ++++++++++++++++++ .../BestFit/RotationSlideStrategy.cs | 4 +- OpenNest.Engine/Compactor.cs | 8 +- 4 files changed, 132 insertions(+), 122 deletions(-) create mode 100644 OpenNest.Core/PartGeometry.cs diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs index e9ba40e..a41b50f 100644 --- a/OpenNest.Core/Helper.cs +++ b/OpenNest.Core/Helper.cs @@ -11,122 +11,6 @@ namespace OpenNest { public static class Helper { - public static List GetPartLines(Part part, double chordTolerance = 0.001) - { - var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); - var lines = new List(); - - foreach (var shape in shapes) - { - var polygon = shape.ToPolygonWithTolerance(chordTolerance); - polygon.Offset(part.Location); - lines.AddRange(polygon.ToLines()); - } - - return lines; - } - - public static List GetPartLines(Part part, PushDirection facingDirection, double chordTolerance = 0.001) - { - var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); - var lines = new List(); - - foreach (var shape in shapes) - { - var polygon = shape.ToPolygonWithTolerance(chordTolerance); - polygon.Offset(part.Location); - lines.AddRange(GetDirectionalLines(polygon, facingDirection)); - } - - return lines; - } - - public static List GetOffsetPartLines(Part part, double spacing, double chordTolerance = 0.001) - { - var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); - var lines = new List(); - - foreach (var shape in shapes) - { - // Add chord tolerance to compensate for inscribed polygon chords - // being inside the actual offset arcs. - var offsetEntity = shape.OffsetEntity(spacing + chordTolerance, OffsetSide.Left) as Shape; - - if (offsetEntity == null) - continue; - - var polygon = offsetEntity.ToPolygonWithTolerance(chordTolerance); - polygon.RemoveSelfIntersections(); - polygon.Offset(part.Location); - lines.AddRange(polygon.ToLines()); - } - - return lines; - } - - public static List GetOffsetPartLines(Part part, double spacing, PushDirection facingDirection, double chordTolerance = 0.001) - { - var entities = ConvertProgram.ToGeometry(part.Program); - var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); - var lines = new List(); - - foreach (var shape in shapes) - { - var offsetEntity = shape.OffsetEntity(spacing + chordTolerance, OffsetSide.Left) as Shape; - - if (offsetEntity == null) - continue; - - var polygon = offsetEntity.ToPolygonWithTolerance(chordTolerance); - polygon.RemoveSelfIntersections(); - polygon.Offset(part.Location); - lines.AddRange(GetDirectionalLines(polygon, facingDirection)); - } - - return lines; - } - - /// - /// Returns only polygon edges whose outward normal faces the specified direction. - /// - private static List GetDirectionalLines(Polygon polygon, PushDirection facingDirection) - { - if (polygon.Vertices.Count < 3) - return polygon.ToLines(); - - var sign = polygon.RotationDirection() == RotationType.CCW ? 1.0 : -1.0; - var lines = new List(); - var last = polygon.Vertices[0]; - - for (int i = 1; i < polygon.Vertices.Count; i++) - { - var current = polygon.Vertices[i]; - var dx = current.X - last.X; - var dy = current.Y - last.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) - lines.Add(new Line(last, current)); - - last = current; - } - - return lines; - } - /// /// Finds the distance from a vertex to a line segment along a push axis. /// Returns double.MaxValue if the ray does not hit the segment. diff --git a/OpenNest.Core/PartGeometry.cs b/OpenNest.Core/PartGeometry.cs new file mode 100644 index 0000000..be5c3d7 --- /dev/null +++ b/OpenNest.Core/PartGeometry.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using System.Linq; +using OpenNest.Converters; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class PartGeometry + { + public static List GetPartLines(Part part, double chordTolerance = 0.001) + { + var entities = ConvertProgram.ToGeometry(part.Program); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var lines = new List(); + + foreach (var shape in shapes) + { + var polygon = shape.ToPolygonWithTolerance(chordTolerance); + polygon.Offset(part.Location); + lines.AddRange(polygon.ToLines()); + } + + return lines; + } + + public static List GetPartLines(Part part, PushDirection facingDirection, double chordTolerance = 0.001) + { + var entities = ConvertProgram.ToGeometry(part.Program); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var lines = new List(); + + foreach (var shape in shapes) + { + var polygon = shape.ToPolygonWithTolerance(chordTolerance); + polygon.Offset(part.Location); + lines.AddRange(GetDirectionalLines(polygon, facingDirection)); + } + + return lines; + } + + public static List GetOffsetPartLines(Part part, double spacing, double chordTolerance = 0.001) + { + var entities = ConvertProgram.ToGeometry(part.Program); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var lines = new List(); + + foreach (var shape in shapes) + { + // Add chord tolerance to compensate for inscribed polygon chords + // being inside the actual offset arcs. + var offsetEntity = shape.OffsetEntity(spacing + chordTolerance, OffsetSide.Left) as Shape; + + if (offsetEntity == null) + continue; + + var polygon = offsetEntity.ToPolygonWithTolerance(chordTolerance); + polygon.RemoveSelfIntersections(); + polygon.Offset(part.Location); + lines.AddRange(polygon.ToLines()); + } + + return lines; + } + + public static List GetOffsetPartLines(Part part, double spacing, PushDirection facingDirection, double chordTolerance = 0.001) + { + var entities = ConvertProgram.ToGeometry(part.Program); + var shapes = ShapeBuilder.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid)); + var lines = new List(); + + foreach (var shape in shapes) + { + var offsetEntity = shape.OffsetEntity(spacing + chordTolerance, OffsetSide.Left) as Shape; + + if (offsetEntity == null) + continue; + + var polygon = offsetEntity.ToPolygonWithTolerance(chordTolerance); + polygon.RemoveSelfIntersections(); + polygon.Offset(part.Location); + lines.AddRange(GetDirectionalLines(polygon, facingDirection)); + } + + return lines; + } + + /// + /// Returns only polygon edges whose outward normal faces the specified direction. + /// + private static List GetDirectionalLines(Polygon polygon, PushDirection facingDirection) + { + if (polygon.Vertices.Count < 3) + return polygon.ToLines(); + + var sign = polygon.RotationDirection() == RotationType.CCW ? 1.0 : -1.0; + var lines = new List(); + var last = polygon.Vertices[0]; + + for (int i = 1; i < polygon.Vertices.Count; i++) + { + var current = polygon.Vertices[i]; + var dx = current.X - last.X; + var dy = current.Y - last.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) + lines.Add(new Line(last, current)); + + last = current; + } + + return lines; + } + } +} diff --git a/OpenNest.Engine/BestFit/RotationSlideStrategy.cs b/OpenNest.Engine/BestFit/RotationSlideStrategy.cs index 395025f..26a6db4 100644 --- a/OpenNest.Engine/BestFit/RotationSlideStrategy.cs +++ b/OpenNest.Engine/BestFit/RotationSlideStrategy.cs @@ -34,8 +34,8 @@ namespace OpenNest.Engine.BestFit var part2Template = Part.CreateAtOrigin(drawing, Part2Rotation); var halfSpacing = spacing / 2; - var part1Lines = Helper.GetOffsetPartLines(part1, halfSpacing); - var part2TemplateLines = Helper.GetOffsetPartLines(part2Template, halfSpacing); + var part1Lines = PartGeometry.GetOffsetPartLines(part1, halfSpacing); + var part2TemplateLines = PartGeometry.GetOffsetPartLines(part2Template, halfSpacing); var bbox1 = part1.BoundingBox; var bbox2 = part2Template.BoundingBox; diff --git a/OpenNest.Engine/Compactor.cs b/OpenNest.Engine/Compactor.cs index 89f4018..364fb26 100644 --- a/OpenNest.Engine/Compactor.cs +++ b/OpenNest.Engine/Compactor.cs @@ -129,12 +129,12 @@ namespace OpenNest continue; movingLines ??= halfSpacing > 0 - ? Helper.GetOffsetPartLines(moving, halfSpacing, direction, ChordTolerance) - : Helper.GetPartLines(moving, direction, ChordTolerance); + ? PartGeometry.GetOffsetPartLines(moving, halfSpacing, direction, ChordTolerance) + : PartGeometry.GetPartLines(moving, direction, ChordTolerance); obstacleLines[i] ??= halfSpacing > 0 - ? Helper.GetOffsetPartLines(obstacleParts[i], halfSpacing, opposite, ChordTolerance) - : Helper.GetPartLines(obstacleParts[i], opposite, ChordTolerance); + ? PartGeometry.GetOffsetPartLines(obstacleParts[i], halfSpacing, opposite, ChordTolerance) + : PartGeometry.GetPartLines(obstacleParts[i], opposite, ChordTolerance); var d = Helper.DirectionalDistance(movingLines, obstacleLines[i], direction); if (d < distance)