From be318bc1c1f6c680d5bc20ccb51e6ff6f6dddd93 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 15 Mar 2026 17:39:52 -0400 Subject: [PATCH] refactor: extract GeometryOptimizer from Helper Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Geometry/GeometryOptimizer.cs | 202 ++++++++++++++++++++ OpenNest.Core/Geometry/Shape.cs | 4 +- OpenNest.Core/Helper.cs | 192 ------------------- OpenNest.IO/DxfImporter.cs | 4 +- 4 files changed, 206 insertions(+), 196 deletions(-) create mode 100644 OpenNest.Core/Geometry/GeometryOptimizer.cs diff --git a/OpenNest.Core/Geometry/GeometryOptimizer.cs b/OpenNest.Core/Geometry/GeometryOptimizer.cs new file mode 100644 index 0000000..db9eedd --- /dev/null +++ b/OpenNest.Core/Geometry/GeometryOptimizer.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using OpenNest.Math; + +namespace OpenNest.Geometry +{ + public static class GeometryOptimizer + { + public static void Optimize(IList arcs) + { + for (int i = 0; i < arcs.Count; ++i) + { + var arc = arcs[i]; + + var coradialArcs = arcs.GetCoradialArs(arc, i); + int index = 0; + + while (index < coradialArcs.Count) + { + Arc arc2 = coradialArcs[index]; + Arc joinArc; + + if (!TryJoinArcs(arc, arc2, out joinArc)) + { + index++; + continue; + } + + coradialArcs.Remove(arc2); + arcs.Remove(arc2); + + arc = joinArc; + index = 0; + } + + arcs[i] = arc; + } + } + + public static void Optimize(IList lines) + { + for (int i = 0; i < lines.Count; ++i) + { + var line = lines[i]; + + var collinearLines = lines.GetCollinearLines(line, i); + var index = 0; + + while (index < collinearLines.Count) + { + Line line2 = collinearLines[index]; + Line joinLine; + + if (!TryJoinLines(line, line2, out joinLine)) + { + index++; + continue; + } + + collinearLines.Remove(line2); + lines.Remove(line2); + + line = joinLine; + index = 0; + } + + lines[i] = line; + } + } + + public static bool TryJoinLines(Line line1, Line line2, out Line lineOut) + { + lineOut = null; + + if (line1 == line2) + return false; + + if (!line1.IsCollinearTo(line2)) + return false; + + bool onPoint = false; + + if (line1.StartPoint == line2.StartPoint) + onPoint = true; + else if (line1.StartPoint == line2.EndPoint) + onPoint = true; + else if (line1.EndPoint == line2.StartPoint) + onPoint = true; + else if (line1.EndPoint == line2.EndPoint) + onPoint = true; + + var t1 = line1.StartPoint.Y > line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; + var t2 = line2.StartPoint.Y > line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; + var b1 = line1.StartPoint.Y < line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; + var b2 = line2.StartPoint.Y < line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; + var l1 = line1.StartPoint.X < line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; + var l2 = line2.StartPoint.X < line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; + var r1 = line1.StartPoint.X > line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; + var r2 = line2.StartPoint.X > line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; + + if (!onPoint) + { + if (t1 < b2 - Tolerance.Epsilon) return false; + if (b1 > t2 + Tolerance.Epsilon) return false; + if (l1 > r2 + Tolerance.Epsilon) return false; + if (r1 < l2 - Tolerance.Epsilon) return false; + } + + var l = l1 < l2 ? l1 : l2; + var r = r1 > r2 ? r1 : r2; + var t = t1 > t2 ? t1 : t2; + var b = b1 < b2 ? b1 : b2; + + if (!line1.IsVertical() && line1.Slope() < 0) + lineOut = new Line(new Vector(l, t), new Vector(r, b)); + else + lineOut = new Line(new Vector(l, b), new Vector(r, t)); + + return true; + } + + public static bool TryJoinArcs(Arc arc1, Arc arc2, out Arc arcOut) + { + arcOut = null; + + if (arc1 == arc2) + return false; + + if (arc1.Center != arc2.Center) + return false; + + if (!arc1.Radius.IsEqualTo(arc2.Radius)) + return false; + + if (arc1.StartAngle > arc1.EndAngle) + arc1.StartAngle -= Angle.TwoPI; + + if (arc2.StartAngle > arc2.EndAngle) + arc2.StartAngle -= Angle.TwoPI; + + if (arc1.EndAngle < arc2.StartAngle || arc1.StartAngle > arc2.EndAngle) + return false; + + var startAngle = arc1.StartAngle < arc2.StartAngle ? arc1.StartAngle : arc2.StartAngle; + var endAngle = arc1.EndAngle > arc2.EndAngle ? arc1.EndAngle : arc2.EndAngle; + + if (startAngle < 0) startAngle += Angle.TwoPI; + if (endAngle < 0) endAngle += Angle.TwoPI; + + arcOut = new Arc(arc1.Center, arc1.Radius, startAngle, endAngle); + + return true; + } + + private static List GetCollinearLines(this IList lines, Line line, int startIndex) + { + var collinearLines = new List(); + + Parallel.For(startIndex, lines.Count, index => + { + var compareLine = lines[index]; + + if (Object.ReferenceEquals(line, compareLine)) + return; + + if (!line.IsCollinearTo(compareLine)) + return; + + lock (collinearLines) + { + collinearLines.Add(compareLine); + } + }); + + return collinearLines; + } + + private static List GetCoradialArs(this IList arcs, Arc arc, int startIndex) + { + var coradialArcs = new List(); + + Parallel.For(startIndex, arcs.Count, index => + { + var compareArc = arcs[index]; + + if (Object.ReferenceEquals(arc, compareArc)) + return; + + if (!arc.IsCoradialTo(compareArc)) + return; + + lock (coradialArcs) + { + coradialArcs.Add(compareArc); + } + }); + + return coradialArcs; + } + } +} diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs index d65d393..9f11945 100644 --- a/OpenNest.Core/Geometry/Shape.cs +++ b/OpenNest.Core/Geometry/Shape.cs @@ -159,8 +159,8 @@ namespace OpenNest.Geometry } } - Helper.Optimize(lines); - Helper.Optimize(arcs); + GeometryOptimizer.Optimize(lines); + GeometryOptimizer.Optimize(arcs); } /// diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs index 60c3ed6..101de75 100644 --- a/OpenNest.Core/Helper.cs +++ b/OpenNest.Core/Helper.cs @@ -11,198 +11,6 @@ namespace OpenNest { public static class Helper { - public static void Optimize(IList arcs) - { - for (int i = 0; i < arcs.Count; ++i) - { - var arc = arcs[i]; - - var coradialArcs = arcs.GetCoradialArs(arc, i); - int index = 0; - - while (index < coradialArcs.Count) - { - Arc arc2 = coradialArcs[index]; - Arc joinArc; - - if (!TryJoinArcs(arc, arc2, out joinArc)) - { - index++; - continue; - } - - coradialArcs.Remove(arc2); - arcs.Remove(arc2); - - arc = joinArc; - index = 0; - } - - arcs[i] = arc; - } - } - - public static void Optimize(IList lines) - { - for (int i = 0; i < lines.Count; ++i) - { - var line = lines[i]; - - var collinearLines = lines.GetCollinearLines(line, i); - var index = 0; - - while (index < collinearLines.Count) - { - Line line2 = collinearLines[index]; - Line joinLine; - - if (!TryJoinLines(line, line2, out joinLine)) - { - index++; - continue; - } - - collinearLines.Remove(line2); - lines.Remove(line2); - - line = joinLine; - index = 0; - } - - lines[i] = line; - } - } - - public static bool TryJoinLines(Line line1, Line line2, out Line lineOut) - { - lineOut = null; - - if (line1 == line2) - return false; - - if (!line1.IsCollinearTo(line2)) - return false; - - bool onPoint = false; - - if (line1.StartPoint == line2.StartPoint) - onPoint = true; - else if (line1.StartPoint == line2.EndPoint) - onPoint = true; - else if (line1.EndPoint == line2.StartPoint) - onPoint = true; - else if (line1.EndPoint == line2.EndPoint) - onPoint = true; - - var t1 = line1.StartPoint.Y > line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; - var t2 = line2.StartPoint.Y > line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; - var b1 = line1.StartPoint.Y < line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; - var b2 = line2.StartPoint.Y < line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; - var l1 = line1.StartPoint.X < line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; - var l2 = line2.StartPoint.X < line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; - var r1 = line1.StartPoint.X > line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; - var r2 = line2.StartPoint.X > line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; - - if (!onPoint) - { - if (t1 < b2 - Tolerance.Epsilon) return false; - if (b1 > t2 + Tolerance.Epsilon) return false; - if (l1 > r2 + Tolerance.Epsilon) return false; - if (r1 < l2 - Tolerance.Epsilon) return false; - } - - var l = l1 < l2 ? l1 : l2; - var r = r1 > r2 ? r1 : r2; - var t = t1 > t2 ? t1 : t2; - var b = b1 < b2 ? b1 : b2; - - if (!line1.IsVertical() && line1.Slope() < 0) - lineOut = new Line(new Vector(l, t), new Vector(r, b)); - else - lineOut = new Line(new Vector(l, b), new Vector(r, t)); - - return true; - } - - public static bool TryJoinArcs(Arc arc1, Arc arc2, out Arc arcOut) - { - arcOut = null; - - if (arc1 == arc2) - return false; - - if (arc1.Center != arc2.Center) - return false; - - if (!arc1.Radius.IsEqualTo(arc2.Radius)) - return false; - - if (arc1.StartAngle > arc1.EndAngle) - arc1.StartAngle -= Angle.TwoPI; - - if (arc2.StartAngle > arc2.EndAngle) - arc2.StartAngle -= Angle.TwoPI; - - if (arc1.EndAngle < arc2.StartAngle || arc1.StartAngle > arc2.EndAngle) - return false; - - var startAngle = arc1.StartAngle < arc2.StartAngle ? arc1.StartAngle : arc2.StartAngle; - var endAngle = arc1.EndAngle > arc2.EndAngle ? arc1.EndAngle : arc2.EndAngle; - - if (startAngle < 0) startAngle += Angle.TwoPI; - if (endAngle < 0) endAngle += Angle.TwoPI; - - arcOut = new Arc(arc1.Center, arc1.Radius, startAngle, endAngle); - - return true; - } - - private static List GetCollinearLines(this IList lines, Line line, int startIndex) - { - var collinearLines = new List(); - - Parallel.For(startIndex, lines.Count, index => - { - var compareLine = lines[index]; - - if (Object.ReferenceEquals(line, compareLine)) - return; - - if (!line.IsCollinearTo(compareLine)) - return; - - lock (collinearLines) - { - collinearLines.Add(compareLine); - } - }); - - return collinearLines; - } - - private static List GetCoradialArs(this IList arcs, Arc arc, int startIndex) - { - var coradialArcs = new List(); - - Parallel.For(startIndex, arcs.Count, index => - { - var compareArc = arcs[index]; - - if (Object.ReferenceEquals(arc, compareArc)) - return; - - if (!arc.IsCoradialTo(compareArc)) - return; - - lock (coradialArcs) - { - coradialArcs.Add(compareArc); - } - }); - - return coradialArcs; - } - public static List GetShapes(IEnumerable entities) { var lines = new List(); diff --git a/OpenNest.IO/DxfImporter.cs b/OpenNest.IO/DxfImporter.cs index 50fcb85..c604a69 100644 --- a/OpenNest.IO/DxfImporter.cs +++ b/OpenNest.IO/DxfImporter.cs @@ -56,8 +56,8 @@ namespace OpenNest.IO } } - Helper.Optimize(lines); - Helper.Optimize(arcs); + GeometryOptimizer.Optimize(lines); + GeometryOptimizer.Optimize(arcs); entities.AddRange(lines); entities.AddRange(arcs);