From f1fd211ba5dbece99332593b6584de4d752eef26 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 6 Apr 2026 14:11:10 -0400 Subject: [PATCH] fix: small parts use FindScrapZones not FindAllRemnants Small parts must only go into scrap zones (both dims < minRemnantSize) to preserve viable remnants. The implementer had inverted this, giving small parts access to all remnants. Also fixed the test to verify remnant preservation behavior and removed unused FindAllRemnants helper. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Engine/MultiPlateNester.cs | 10 ++-------- OpenNest.Tests/Engine/MultiPlateNesterTests.cs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/OpenNest.Engine/MultiPlateNester.cs b/OpenNest.Engine/MultiPlateNester.cs index e380ecb..82a0821 100644 --- a/OpenNest.Engine/MultiPlateNester.cs +++ b/OpenNest.Engine/MultiPlateNester.cs @@ -262,9 +262,9 @@ namespace OpenNest if (classification == PartClass.Large) continue; // Large parts don't go on existing plates — they create new ones. - // Get all remnants and try to place in them. + // Small parts only go into scrap zones; medium parts into viable remnants. var remnants = classification == PartClass.Small - ? FindAllRemnants(pr.Plate) + ? FindScrapZones(pr.Plate, minRemnantSize) : FindViableRemnants(pr.Plate, minRemnantSize); foreach (var zone in remnants) @@ -435,12 +435,6 @@ namespace OpenNest return false; } - private static List FindAllRemnants(Plate plate) - { - var finder = RemnantFinder.FromPlate(plate); - return finder.FindRemnants(); - } - private static NestItem CloneItem(NestItem item) { return new NestItem diff --git a/OpenNest.Tests/Engine/MultiPlateNesterTests.cs b/OpenNest.Tests/Engine/MultiPlateNesterTests.cs index 3a6ec3d..495c425 100644 --- a/OpenNest.Tests/Engine/MultiPlateNesterTests.cs +++ b/OpenNest.Tests/Engine/MultiPlateNesterTests.cs @@ -236,8 +236,11 @@ public class MultiPlateNesterTests } [Fact] - public void Nest_SmallPartsGoIntoScrapZones() + public void Nest_SmallPartsDontConsumeViableRemnants() { + // 96x48 plate with 80x40 big part leaves viable remnants (strips > 12" in one dim). + // Small parts should NOT consume those viable remnants — they should go to + // a separate plate instead, preserving the remnant for future use. var template = new Plate(96, 48) { PartSpacing = 0.25, Quadrant = 1 }; template.EdgeSpacing = new Spacing(); @@ -258,10 +261,14 @@ public class MultiPlateNesterTests progress: null, token: CancellationToken.None); - // Small parts should be placed on the same plate as the big part - // (in scrap zones), not on a new plate. - Assert.Equal(1, result.Plates.Count); - Assert.True(result.Plates[0].Parts.Count > 1); + // Big part on plate 1, tiny parts on plate 2 (viable remnant preserved). + Assert.Equal(2, result.Plates.Count); + + // First plate should have only the big part. + var bigPlate = result.Plates.First(p => p.Parts.Any( + part => part.BaseDrawing.Name == "big")); + var tinyOnBigPlate = bigPlate.Parts.Count(p => p.BaseDrawing.Name == "tiny"); + Assert.Equal(0, tinyOnBigPlate); } [Fact]