From be8b499880ace9c0a2c577b755d26e0012a3eb40 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 7 Mar 2026 01:01:41 -0500 Subject: [PATCH] feat: add NestEngine.FillLinear with 4-config rotation/axis optimization Co-Authored-By: Claude Opus 4.6 --- OpenNest.Engine/NestEngine.cs | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/OpenNest.Engine/NestEngine.cs b/OpenNest.Engine/NestEngine.cs index 7d38fcf..d2dda17 100644 --- a/OpenNest.Engine/NestEngine.cs +++ b/OpenNest.Engine/NestEngine.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using OpenNest.Converters; using OpenNest.Geometry; using OpenNest.Math; using OpenNest.RectanglePacking; @@ -107,6 +109,77 @@ namespace OpenNest return parts.Count > 0; } + public bool FillLinear(NestItem item) + { + var workArea = Plate.WorkArea(); + var bestRotation = FindBestRotation(item); + + var engine = new FillLinear(workArea, Plate.PartSpacing); + + // Try 4 configurations: 2 rotations x 2 axes. + var configs = new[] + { + engine.Fill(item.Drawing, bestRotation, NestDirection.Horizontal), + engine.Fill(item.Drawing, bestRotation, NestDirection.Vertical), + engine.Fill(item.Drawing, bestRotation + Angle.HalfPI, NestDirection.Horizontal), + engine.Fill(item.Drawing, bestRotation + Angle.HalfPI, NestDirection.Vertical) + }; + + // Pick the configuration with the most parts. + List best = null; + + foreach (var config in configs) + { + if (best == null || config.Count > best.Count) + best = config; + } + + if (best == null || best.Count == 0) + return false; + + // Limit to requested quantity if specified. + if (item.Quantity > 0 && best.Count > item.Quantity) + best = best.Take(item.Quantity).ToList(); + + Plate.Parts.AddRange(best); + return true; + } + + private double FindBestRotation(NestItem item) + { + var entities = ConvertProgram.ToGeometry(item.Drawing.Program) + .Where(e => e.Layer != SpecialLayers.Rapid); + + var shapes = Helper.GetShapes(entities); + + if (shapes.Count == 0) + return 0; + + // Find the largest shape (outer profile). + Shape largest = shapes[0]; + double largestArea = largest.Area(); + + for (int i = 1; i < shapes.Count; i++) + { + var area = shapes[i].Area(); + if (area > largestArea) + { + largest = shapes[i]; + largestArea = area; + } + } + + BoundingRectangleResult result; + + if (item.RotationStart.IsEqualTo(0) && item.RotationEnd.IsEqualTo(0)) + result = largest.FindBestRotation(); + else + result = largest.FindBestRotation(item.RotationStart, item.RotationEnd); + + // Negate the angle to align the minimum bounding rectangle with the axes. + return -result.Angle; + } + private List ConvertToParts(Bin bin, List items) { var parts = new List();