mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-29 06:51:06 -04:00
DefaultPlateFiller becomes PlateFiller(plate, policy, layout); PlateFillerBase and StripPlateFiller are gone. PlateLayout.FillThenPack is the old orchestrator path (Fill and both Remnants); PlateLayout.Strips is the old StripPlateFiller.Nest, and Strip is Fill policy plus Strips layout. Lifetimes are unchanged: a strip-layout filler runs every Fill, group Fill and PackArea on a fresh inner filler and gives each strip fresh remnant fillers, so learned angles and PlateNumber never leak into them. The comparer, preferred direction, trim axis and angle recording now read the policy directly. The base class's unsorted two-angle BuildAngles is dropped: only DefaultPlateFiller's pipeline called BuildAngles, and Strip never ran it. Layout-neutral: all 29 FillStackCharacterizationTests pins pass unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
3.9 KiB
C#
116 lines
3.9 KiB
C#
using OpenNest.Engine;
|
|
using OpenNest.Engine.Fill;
|
|
using OpenNest.Engine.Jobs.Placement;
|
|
using OpenNest.Engine.Jobs.Placement.Fillers;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.Engine;
|
|
|
|
public class RemnantEngineTests
|
|
{
|
|
private static Drawing MakeRectDrawing(double w, double h, string name = "rect")
|
|
{
|
|
var pgm = new OpenNest.CNC.Program();
|
|
pgm.Codes.Add(new OpenNest.CNC.RapidMove(new Vector(0, 0)));
|
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, 0)));
|
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, h)));
|
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, h)));
|
|
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, 0)));
|
|
return new Drawing(name, pgm);
|
|
}
|
|
|
|
[Fact]
|
|
public void VerticalRemnantPlateFiller_UsesHorizontalPreferredDirection()
|
|
{
|
|
var plate = new Plate(60, 120);
|
|
var filler = new PlateFiller(plate, PlateFillPolicy.VerticalRemnant);
|
|
Assert.Equal(NestDirection.Horizontal, filler.PreferredDirection);
|
|
}
|
|
|
|
[Fact]
|
|
public void HorizontalRemnantPlateFiller_UsesVerticalPreferredDirection()
|
|
{
|
|
var plate = new Plate(60, 120);
|
|
var filler = new PlateFiller(plate, PlateFillPolicy.HorizontalRemnant);
|
|
Assert.Equal(NestDirection.Vertical, filler.PreferredDirection);
|
|
}
|
|
|
|
[Fact]
|
|
public void VerticalRemnantPlateFiller_Fill_ProducesResults()
|
|
{
|
|
var plate = new Plate(60, 120);
|
|
var filler = new PlateFiller(plate, PlateFillPolicy.VerticalRemnant);
|
|
var item = new NestItem { Drawing = MakeRectDrawing(20, 10) };
|
|
|
|
var parts = filler.Fill(
|
|
item,
|
|
plate.WorkArea(),
|
|
null,
|
|
System.Threading.CancellationToken.None
|
|
);
|
|
|
|
Assert.True(parts.Count > 0, "VerticalRemnantPlateFiller should fill parts");
|
|
}
|
|
|
|
[Fact]
|
|
public void HorizontalRemnantPlateFiller_Fill_ProducesResults()
|
|
{
|
|
var plate = new Plate(60, 120);
|
|
var filler = new PlateFiller(plate, PlateFillPolicy.HorizontalRemnant);
|
|
var item = new NestItem { Drawing = MakeRectDrawing(20, 10) };
|
|
|
|
var parts = filler.Fill(
|
|
item,
|
|
plate.WorkArea(),
|
|
null,
|
|
System.Threading.CancellationToken.None
|
|
);
|
|
|
|
Assert.True(parts.Count > 0, "HorizontalRemnantPlateFiller should fill parts");
|
|
}
|
|
|
|
[Fact]
|
|
public void PlateFillService_ListsBothRemnantStrategies()
|
|
{
|
|
Assert.Contains("Vertical Remnant", PlateFillService.BuiltInStrategies);
|
|
Assert.Contains("Horizontal Remnant", PlateFillService.BuiltInStrategies);
|
|
}
|
|
|
|
[Fact]
|
|
public void VerticalRemnantPlateFiller_ProducesTighterXExtent_ThanDefault()
|
|
{
|
|
var plate = new Plate(60, 120);
|
|
var drawing = MakeRectDrawing(20, 10);
|
|
var item = new NestItem { Drawing = drawing };
|
|
|
|
var defaultFiller = new PlateFiller(plate);
|
|
var remnantFiller = new PlateFiller(plate, PlateFillPolicy.VerticalRemnant);
|
|
|
|
var defaultParts = defaultFiller.Fill(
|
|
item,
|
|
plate.WorkArea(),
|
|
null,
|
|
System.Threading.CancellationToken.None
|
|
);
|
|
var remnantParts = remnantFiller.Fill(
|
|
item,
|
|
plate.WorkArea(),
|
|
null,
|
|
System.Threading.CancellationToken.None
|
|
);
|
|
|
|
Assert.True(defaultParts.Count > 0);
|
|
Assert.True(remnantParts.Count > 0);
|
|
|
|
var defaultXExtent =
|
|
defaultParts.Max(p => p.BoundingBox.Right) - defaultParts.Min(p => p.BoundingBox.Left);
|
|
var remnantXExtent =
|
|
remnantParts.Max(p => p.BoundingBox.Right) - remnantParts.Min(p => p.BoundingBox.Left);
|
|
|
|
Assert.True(
|
|
remnantXExtent <= defaultXExtent + 0.01,
|
|
$"Remnant X-extent ({remnantXExtent:F1}) should be <= default filler ({defaultXExtent:F1})"
|
|
);
|
|
}
|
|
}
|