feat: add scrap zone identification to MultiPlateNester
Adds IsScrapRemnant(), FindScrapZones(), and FindViableRemnants() to MultiPlateNester. A remnant is scrap only when both dimensions fall below the minimum remnant size threshold (AND logic, not OR). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest
|
||||
@@ -56,5 +57,40 @@ namespace OpenNest
|
||||
|
||||
return PartClass.Small;
|
||||
}
|
||||
|
||||
public static bool IsScrapRemnant(Box remnant, double minRemnantSize)
|
||||
{
|
||||
return remnant.Width < minRemnantSize && remnant.Length < minRemnantSize;
|
||||
}
|
||||
|
||||
public static List<Box> FindScrapZones(Plate plate, double minRemnantSize)
|
||||
{
|
||||
var finder = RemnantFinder.FromPlate(plate);
|
||||
var remnants = finder.FindRemnants();
|
||||
|
||||
var scrap = new List<Box>();
|
||||
foreach (var remnant in remnants)
|
||||
{
|
||||
if (IsScrapRemnant(remnant, minRemnantSize))
|
||||
scrap.Add(remnant);
|
||||
}
|
||||
|
||||
return scrap;
|
||||
}
|
||||
|
||||
public static List<Box> FindViableRemnants(Plate plate, double minRemnantSize)
|
||||
{
|
||||
var finder = RemnantFinder.FromPlate(plate);
|
||||
var remnants = finder.FindRemnants();
|
||||
|
||||
var viable = new List<Box>();
|
||||
foreach (var remnant in remnants)
|
||||
{
|
||||
if (!IsScrapRemnant(remnant, minRemnantSize))
|
||||
viable.Add(remnant);
|
||||
}
|
||||
|
||||
return viable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,4 +103,47 @@ public class MultiPlateNesterTests
|
||||
var result = MultiPlateNester.Classify(bb, workArea);
|
||||
Assert.Equal(PartClass.Small, result);
|
||||
}
|
||||
|
||||
// --- Task 5: Scrap Zone Identification ---
|
||||
|
||||
[Fact]
|
||||
public void IsScrapRemnant_BothDimensionsBelowThreshold_ReturnsTrue()
|
||||
{
|
||||
var remnant = new Box(0, 0, 10, 8);
|
||||
Assert.True(MultiPlateNester.IsScrapRemnant(remnant, 12.0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsScrapRemnant_OneDimensionAboveThreshold_ReturnsFalse()
|
||||
{
|
||||
// 11 x 120 — narrow but long, should be preserved
|
||||
var remnant = new Box(0, 0, 11, 120);
|
||||
Assert.False(MultiPlateNester.IsScrapRemnant(remnant, 12.0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsScrapRemnant_BothDimensionsAboveThreshold_ReturnsFalse()
|
||||
{
|
||||
var remnant = new Box(0, 0, 20, 30);
|
||||
Assert.False(MultiPlateNester.IsScrapRemnant(remnant, 12.0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindScrapZones_ReturnsOnlyScrapRemnants()
|
||||
{
|
||||
// 96x48 plate with a 70x40 part placed at origin
|
||||
var plate = new Plate(96, 48) { PartSpacing = 0.25 };
|
||||
var drawing = MakeDrawing("big", 70, 40);
|
||||
var part = new Part(drawing);
|
||||
plate.Parts.Add(part);
|
||||
|
||||
var scrap = MultiPlateNester.FindScrapZones(plate, 12.0);
|
||||
|
||||
// All returned zones should have both dims < 12
|
||||
foreach (var zone in scrap)
|
||||
{
|
||||
Assert.True(zone.Width < 12.0 && zone.Length < 12.0,
|
||||
$"Zone {zone.Width:F1}x{zone.Length:F1} is not scrap — at least one dimension >= 12");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user