Files
OpenNest/OpenNest.Tests/Engine/EngineRefactorSmokeTests.cs
T
ajandClaude Opus 5.5 5761eefd37 refactor(engine): merge the plate fillers into one PlateFiller
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>
2026-10-25 23:44:51 -04:00

144 lines
4.8 KiB
C#

using OpenNest.Engine;
using OpenNest.Engine.Jobs.Placement.Fillers;
using OpenNest.Geometry;
namespace OpenNest.Tests.Engine;
public class EngineRefactorSmokeTests
{
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 PlateFiller_FillNestItem_ProducesResults()
{
var plate = new Plate(60, 120);
var filler = new PlateFiller(plate);
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, "PlateFiller should fill parts");
}
[Fact]
public void PlateFiller_FillGroupParts_ProducesResults()
{
var plate = new Plate(60, 120);
var filler = new PlateFiller(plate);
var drawing = MakeRectDrawing(20, 10);
var groupParts = new List<Part> { new Part(drawing) };
var parts = filler.Fill(
groupParts,
plate.WorkArea(),
null,
System.Threading.CancellationToken.None
);
Assert.True(parts.Count > 0, "PlateFiller group fill should produce parts");
}
[Fact]
public void PlateFiller_ForceFullAngleSweep_StillWorks()
{
var plate = new Plate(60, 120);
var filler = new PlateFiller(plate);
filler.ForceFullAngleSweep = true;
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, "ForceFullAngleSweep should still produce results");
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void PlateFiller_StopAtQuantity_StillDeliversExactlyTheQuantity(bool stopAtQuantity)
{
var plate = new Plate(60, 120) { PartSpacing = 0.5 };
var filler = new PlateFiller(plate) { StopAtQuantity = stopAtQuantity };
var item = new NestItem { Drawing = MakeRectDrawing(7, 3), Quantity = 23 };
var parts = filler.Fill(item, plate.WorkArea(), null, System.Threading.CancellationToken.None);
Assert.Equal(23, parts.Count);
plate.Parts.AddRange(parts);
Assert.False(plate.HasOverlappingParts(out _), "Fill overlaps");
var area = plate.WorkArea();
Assert.All(parts, p => Assert.True(
p.BoundingBox.Left >= area.Left - 1e-6 && p.BoundingBox.Right <= area.Right + 1e-6
&& p.BoundingBox.Bottom >= area.Bottom - 1e-6 && p.BoundingBox.Top <= area.Top + 1e-6,
"Part outside the work area"));
}
[Fact]
public void StripLayout_Nest_ProducesResults()
{
var plate = new Plate(60, 120);
var filler = new PlateFiller(plate, PlateFillPolicy.Fill, PlateLayout.Strips);
var items = new List<NestItem>
{
new NestItem { Drawing = MakeRectDrawing(20, 10, "large"), Quantity = 10 },
new NestItem { Drawing = MakeRectDrawing(8, 5, "small"), Quantity = 5 },
};
var parts = filler.Nest(items, null, System.Threading.CancellationToken.None);
Assert.True(parts.Count > 0, "Strip layout should nest parts");
}
[Fact]
public void PlateFiller_Nest_ProducesResults()
{
var plate = new Plate(60, 120);
var filler = new PlateFiller(plate);
var items = new List<NestItem>
{
new NestItem { Drawing = MakeRectDrawing(20, 10, "a"), Quantity = 5 },
new NestItem { Drawing = MakeRectDrawing(15, 8, "b"), Quantity = 3 },
};
var parts = filler.Nest(items, null, System.Threading.CancellationToken.None);
Assert.True(parts.Count > 0, "Base Nest method should place parts");
}
[Fact]
public void BruteForceRunner_StillWorks()
{
var plate = new Plate(60, 120);
var drawing = MakeRectDrawing(20, 10);
var result = OpenNest.Engine.ML.BruteForceRunner.Run(
drawing,
plate,
forceFullAngleSweep: true
);
Assert.NotNull(result);
Assert.True(result.PartCount > 0);
Assert.False(string.IsNullOrWhiteSpace(result.WinnerEngine));
Assert.NotEmpty(result.AngleResults);
}
}