From 89a4e6b9819c9de272af03ba5e7ba3b4a3574660 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 6 Apr 2026 13:48:57 -0400 Subject: [PATCH] feat: add MultiPlateNester with sorting logic Implements static MultiPlateNester.SortItems with BoundingBoxArea and Size sort orders, covered by two passing xUnit tests. Co-Authored-By: Claude Sonnet 4.6 --- OpenNest.Engine/MultiPlateNester.cs | 35 ++++++++++ .../Engine/MultiPlateNesterTests.cs | 65 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 OpenNest.Engine/MultiPlateNester.cs create mode 100644 OpenNest.Tests/Engine/MultiPlateNesterTests.cs diff --git a/OpenNest.Engine/MultiPlateNester.cs b/OpenNest.Engine/MultiPlateNester.cs new file mode 100644 index 0000000..dcf15f5 --- /dev/null +++ b/OpenNest.Engine/MultiPlateNester.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest +{ + public static class MultiPlateNester + { + public static List SortItems(List items, PartSortOrder sortOrder) + { + switch (sortOrder) + { + case PartSortOrder.BoundingBoxArea: + return items + .OrderByDescending(i => + { + var bb = i.Drawing.Program.BoundingBox(); + return bb.Width * bb.Length; + }) + .ToList(); + + case PartSortOrder.Size: + return items + .OrderByDescending(i => + { + var bb = i.Drawing.Program.BoundingBox(); + return System.Math.Max(bb.Width, bb.Length); + }) + .ToList(); + + default: + return items.ToList(); + } + } + } +} diff --git a/OpenNest.Tests/Engine/MultiPlateNesterTests.cs b/OpenNest.Tests/Engine/MultiPlateNesterTests.cs new file mode 100644 index 0000000..4e31bb0 --- /dev/null +++ b/OpenNest.Tests/Engine/MultiPlateNesterTests.cs @@ -0,0 +1,65 @@ +using OpenNest.Geometry; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace OpenNest.Tests.Engine; + +public class MultiPlateNesterTests +{ + private static Drawing MakeDrawing(string name, double width, double length) + { + var program = new OpenNest.CNC.Program(); + program.Codes.Add(new OpenNest.CNC.RapidMove(new Vector(0, 0))); + program.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(width, 0))); + program.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(width, length))); + program.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, length))); + program.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, 0))); + var drawing = new Drawing(name, program); + drawing.UpdateArea(); + return drawing; + } + + private static NestItem MakeItem(string name, double width, double length, int qty = 1) + { + return new NestItem + { + Drawing = MakeDrawing(name, width, length), + Quantity = qty, + }; + } + + [Fact] + public void SortByBoundingBoxArea_OrdersLargestFirst() + { + var items = new List + { + MakeItem("small", 10, 10), + MakeItem("large", 40, 60), + MakeItem("medium", 20, 30), + }; + + var sorted = MultiPlateNester.SortItems(items, PartSortOrder.BoundingBoxArea); + + Assert.Equal("large", sorted[0].Drawing.Name); + Assert.Equal("medium", sorted[1].Drawing.Name); + Assert.Equal("small", sorted[2].Drawing.Name); + } + + [Fact] + public void SortBySize_OrdersByLongestDimension() + { + var items = new List + { + MakeItem("short-wide", 50, 20), // longest = 50 + MakeItem("tall-narrow", 10, 80), // longest = 80 + MakeItem("square", 30, 30), // longest = 30 + }; + + var sorted = MultiPlateNester.SortItems(items, PartSortOrder.Size); + + Assert.Equal("tall-narrow", sorted[0].Drawing.Name); + Assert.Equal("short-wide", sorted[1].Drawing.Name); + Assert.Equal("square", sorted[2].Drawing.Name); + } +}