From 13e93c3534026010bc59400280c32eac1aff2fad Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 6 Mar 2026 22:44:04 -0500 Subject: [PATCH] feat: add ConvexHull class with Andrew's monotone chain algorithm Co-Authored-By: Claude Opus 4.6 --- OpenNest.Core/Geometry/ConvexHull.cs | 60 ++++++++++++++++++++++++++++ OpenNest.Core/OpenNest.Core.csproj | 1 + 2 files changed, 61 insertions(+) create mode 100644 OpenNest.Core/Geometry/ConvexHull.cs diff --git a/OpenNest.Core/Geometry/ConvexHull.cs b/OpenNest.Core/Geometry/ConvexHull.cs new file mode 100644 index 0000000..062b20b --- /dev/null +++ b/OpenNest.Core/Geometry/ConvexHull.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Geometry +{ + public static class ConvexHull + { + public static Polygon Compute(IList points) + { + if (points.Count < 3) + { + var result = new Polygon(); + result.Vertices.AddRange(points); + return result; + } + + var sorted = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList(); + + var lower = new List(); + + foreach (var p in sorted) + { + while (lower.Count >= 2 && Cross(lower[lower.Count - 2], lower[lower.Count - 1], p) <= 0) + lower.RemoveAt(lower.Count - 1); + + lower.Add(p); + } + + var upper = new List(); + + for (int i = sorted.Count - 1; i >= 0; i--) + { + var p = sorted[i]; + + while (upper.Count >= 2 && Cross(upper[upper.Count - 2], upper[upper.Count - 1], p) <= 0) + upper.RemoveAt(upper.Count - 1); + + upper.Add(p); + } + + // Remove last point of each half because it's repeated + lower.RemoveAt(lower.Count - 1); + upper.RemoveAt(upper.Count - 1); + + lower.AddRange(upper); + + var hull = new Polygon(); + hull.Vertices.AddRange(lower); + hull.Close(); + hull.UpdateBounds(); + + return hull; + } + + private static double Cross(Vector o, Vector a, Vector b) + { + return (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X); + } + } +} diff --git a/OpenNest.Core/OpenNest.Core.csproj b/OpenNest.Core/OpenNest.Core.csproj index 979d3bc..fd6bdf9 100644 --- a/OpenNest.Core/OpenNest.Core.csproj +++ b/OpenNest.Core/OpenNest.Core.csproj @@ -64,6 +64,7 @@ +