From 2632b3dbf7741426388f02cad8c45735bd7dada4 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 12 Mar 2026 08:29:02 -0400 Subject: [PATCH] fix: resolve infinite loop in multi-plate autonest and wire into console - Change while(true) to bounded for-loop (max 100 plates) in MainForm - Use Drawing.Name comparison instead of reference equality for quantity deduction - Add Math.Max(0, ...) guard to prevent negative quantities - Tune SA parameters for faster convergence (cooling=0.995, minTemp=0.1, maxNoImprove=500) - Add --autonest flag to OpenNest.Console for CLI-based NFP autonesting Co-Authored-By: Claude Opus 4.6 --- OpenNest.Console/Program.cs | 40 ++++++++++++++++++++++++--- OpenNest.Engine/SimulatedAnnealing.cs | 6 ++-- OpenNest/Forms/MainForm.cs | 12 ++++---- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/OpenNest.Console/Program.cs b/OpenNest.Console/Program.cs index 75a5258..10ea080 100644 --- a/OpenNest.Console/Program.cs +++ b/OpenNest.Console/Program.cs @@ -20,6 +20,7 @@ var checkOverlaps = false; var noSave = false; var noLog = false; var keepParts = false; +var autoNest = false; for (var i = 0; i < args.Length; i++) { @@ -60,6 +61,9 @@ for (var i = 0; i < args.Length; i++) case "--keep-parts": keepParts = true; break; + case "--autonest": + autoNest = true; + break; case "--help": case "-h": PrintUsage(); @@ -145,11 +149,38 @@ else Console.WriteLine("---"); -// Run fill. +// Run fill or autonest. var sw = Stopwatch.StartNew(); -var engine = new NestEngine(plate); -var item = new NestItem { Drawing = drawing, Quantity = quantity }; -var success = engine.Fill(item); +bool success; + +if (autoNest) +{ + // AutoNest: use all drawings (or specific drawing if --drawing given). + var nestItems = new List(); + + if (drawingName != null) + { + nestItems.Add(new NestItem { Drawing = drawing, Quantity = quantity > 0 ? quantity : 1 }); + } + else + { + foreach (var d in nest.Drawings) + nestItems.Add(new NestItem { Drawing = d, Quantity = quantity > 0 ? quantity : 1 }); + } + + Console.WriteLine($"AutoNest: {nestItems.Count} drawing(s), {nestItems.Sum(i => i.Quantity)} total parts"); + + var parts = NestEngine.AutoNest(nestItems, plate); + plate.Parts.AddRange(parts); + success = parts.Count > 0; +} +else +{ + var engine = new NestEngine(plate); + var item = new NestItem { Drawing = drawing, Quantity = quantity }; + success = engine.Fill(item); +} + sw.Stop(); // Check overlaps. @@ -208,6 +239,7 @@ void PrintUsage() Console.Error.WriteLine(" --spacing Override part spacing"); Console.Error.WriteLine(" --size Override plate size (e.g. 120x60)"); Console.Error.WriteLine(" --output Output nest file path (default: -result.zip)"); + Console.Error.WriteLine(" --autonest Use NFP-based mixed-part autonesting instead of linear fill"); Console.Error.WriteLine(" --keep-parts Don't clear existing parts before filling"); Console.Error.WriteLine(" --check-overlaps Run overlap detection after fill (exit code 1 if found)"); Console.Error.WriteLine(" --no-save Skip saving output file"); diff --git a/OpenNest.Engine/SimulatedAnnealing.cs b/OpenNest.Engine/SimulatedAnnealing.cs index c63f1eb..d2dc4b9 100644 --- a/OpenNest.Engine/SimulatedAnnealing.cs +++ b/OpenNest.Engine/SimulatedAnnealing.cs @@ -13,9 +13,9 @@ namespace OpenNest /// public class SimulatedAnnealing : INestOptimizer { - private const double DefaultCoolingRate = 0.997; - private const double DefaultMinTemperature = 0.01; - private const int DefaultMaxNoImprovement = 2000; + private const double DefaultCoolingRate = 0.995; + private const double DefaultMinTemperature = 0.1; + private const int DefaultMaxNoImprovement = 500; public NestResult Optimize(List items, Box workArea, NfpCache cache, Dictionary> candidateRotations, diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index f3335ab..0cd755f 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -680,8 +680,9 @@ namespace OpenNest.Forms return; var items = form.GetNestItems(); + var maxPlates = 100; - while (true) + for (var plateCount = 0; plateCount < maxPlates; plateCount++) { var remaining = items.Where(i => i.Quantity > 0).ToList(); @@ -698,14 +699,15 @@ namespace OpenNest.Forms break; plate.Parts.AddRange(parts); - activeForm.Nest.UpdateDrawingQuantities(); - // Reduce remaining quantities by how many were placed per drawing. + // Deduct placed quantities using Drawing.Name to avoid reference issues. foreach (var item in remaining) { - var placed = parts.Count(p => p.BaseDrawing == item.Drawing); - item.Quantity -= placed; + var placed = parts.Count(p => p.BaseDrawing.Name == item.Drawing.Name); + item.Quantity = System.Math.Max(0, item.Quantity - placed); } + + activeForm.Nest.UpdateDrawingQuantities(); } }