fix(engine): use local quantity tracking in StripNestEngine remnant loop

The iterative remnant fill was mutating shared NestItem.Quantity objects,
causing the second TryOrientation call (left) to see depleted quantities
from the first call (bottom). Use a local dictionary instead so both
orientations start with the full quantities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 13:23:32 -04:00
parent 3d23943b69
commit a41a08c9af

View File

@@ -261,6 +261,12 @@ namespace OpenNest
var finder = new RemnantFinder(workArea, obstacles); var finder = new RemnantFinder(workArea, obstacles);
var madeProgress = true; var madeProgress = true;
// Track quantities locally so we don't mutate the shared NestItem objects.
// TryOrientation is called twice (bottom, left) with the same items.
var localQty = new Dictionary<string, int>();
foreach (var item in effectiveRemainder)
localQty[item.Drawing.Name] = item.Quantity;
while (madeProgress && !token.IsCancellationRequested) while (madeProgress && !token.IsCancellationRequested)
{ {
madeProgress = false; madeProgress = false;
@@ -274,7 +280,8 @@ namespace OpenNest
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
break; break;
if (item.Quantity == 0) var qty = localQty[item.Drawing.Name];
if (qty == 0)
continue; continue;
var itemBbox = item.Drawing.Program.BoundingBox(); var itemBbox = item.Drawing.Program.BoundingBox();
@@ -287,13 +294,13 @@ namespace OpenNest
var remnantInner = new DefaultNestEngine(Plate); var remnantInner = new DefaultNestEngine(Plate);
var remnantParts = remnantInner.Fill( var remnantParts = remnantInner.Fill(
new NestItem { Drawing = item.Drawing, Quantity = item.Quantity }, new NestItem { Drawing = item.Drawing, Quantity = qty },
box, remnantProgress, token); box, remnantProgress, token);
if (remnantParts != null && remnantParts.Count > 0) if (remnantParts != null && remnantParts.Count > 0)
{ {
allParts.AddRange(remnantParts); allParts.AddRange(remnantParts);
item.Quantity = System.Math.Max(0, item.Quantity - remnantParts.Count); localQty[item.Drawing.Name] = System.Math.Max(0, qty - remnantParts.Count);
// Update obstacles and re-discover remnants // Update obstacles and re-discover remnants
foreach (var p in remnantParts) foreach (var p in remnantParts)