mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-03 19:21:35 -05:00
NestPipeline.Run now bundles parts that fit other parts' enclosed cutouts into their frames before the selected engine, for desktop, Console, MCP and API alike. A valid complete bundled trial is kept; a refused trial falls back to the ordinary solve (reason on RefusedCutoutTrial), and an incomplete trial is compared with it, keeping whichever valid proposal places more. An incomplete but valid trial is now keepable like any incomplete result. Expanded sheets list each frame's inserts before the frame, so the committed order cuts every insert before the cutout that releases its slug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
3.4 KiB
C#
76 lines
3.4 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.Engine;
|
|
using OpenNest.Engine.Jobs;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.IO;
|
|
|
|
namespace OpenNest.Tests.Engine;
|
|
|
|
public class CutoutPipelinePersistenceTests
|
|
{
|
|
[Fact]
|
|
public void ExpandedMultiSheetProposalCommitsAndReloadsAsSeparateDrawings()
|
|
{
|
|
var frame = new OpenNest.Shapes.RingShape { OuterDiameter = 20, InnerDiameter = 10 }
|
|
.GetDrawing();
|
|
frame.Name = "neutral-frame";
|
|
var program = new Program();
|
|
program.MoveTo(0, 0);
|
|
program.LineTo(6, 0);
|
|
program.LineTo(6, 6);
|
|
program.LineTo(0, 6);
|
|
program.LineTo(0, 0);
|
|
var insert = new Drawing("neutral-insert", program);
|
|
var request = new NestPipelineRequest("Irregular", new[] {
|
|
new NestItem { Drawing = frame, Quantity = 2 },
|
|
new NestItem { Drawing = insert, Quantity = 2 } },
|
|
new[] { new NestPlateStock("sheet", new Size(22, 22), 2, 0.25) },
|
|
new NestJobOptions(maxPlates: 2));
|
|
var ordinary = NestPipeline.RunWithoutCutouts(request);
|
|
Assert.Contains(ordinary.Raw.Fulfillment, f => f.Unplaced > 0);
|
|
var result = NestPipeline.Run(request);
|
|
Assert.Empty(result.RefusedCutoutTrial);
|
|
Assert.True(result.IsValid, string.Join("; ", result.Violations));
|
|
Assert.Equal(2, result.Raw.Plates.Count);
|
|
Assert.All(result.Raw.Plates, p => Assert.Equal(new[] { "part-1", "part-0" },
|
|
p.Placements.Select(x => x.PartId)));
|
|
|
|
var nest = new Nest();
|
|
nest.Drawings.Add(frame);
|
|
nest.Drawings.Add(insert);
|
|
using (var manager = new PlateManager(nest))
|
|
{
|
|
var committed = NestPipelineCommit.ApplyToEmptyPlates(result, manager);
|
|
Assert.Equal(2, committed.Sum(p => p.Quantity));
|
|
Assert.All(committed, plate => Assert.Equal(new[] { insert, frame },
|
|
plate.Parts.Select(p => p.BaseDrawing)));
|
|
}
|
|
Assert.Equal(2, frame.Quantity.Nested);
|
|
Assert.Equal(2, insert.Quantity.Nested);
|
|
using var stream = new MemoryStream();
|
|
Assert.True(new NestWriter(nest).Write(stream));
|
|
var loaded = new NestReader(new MemoryStream(stream.ToArray())).Read();
|
|
Assert.Equal(2, loaded.Plates.Sum(p => p.Quantity));
|
|
for (var i = 0; i < loaded.Plates.Count; i++)
|
|
{
|
|
var expected = nest.Plates[i];
|
|
var actual = loaded.Plates[i];
|
|
Assert.Equal(expected.Quantity, actual.Quantity);
|
|
Assert.Equal(expected.Size, actual.Size);
|
|
Assert.Equal(expected.PartSpacing, actual.PartSpacing);
|
|
Assert.Equal(expected.Quadrant, actual.Quadrant);
|
|
Assert.Equal(expected.EdgeSpacing, actual.EdgeSpacing);
|
|
Assert.Equal(new[] { "neutral-insert", "neutral-frame" },
|
|
actual.Parts.Select(p => p.BaseDrawing.Name));
|
|
for (var j = 0; j < actual.Parts.Count; j++)
|
|
{
|
|
Assert.Equal(expected.Parts[j].Location.X, actual.Parts[j].Location.X, 6);
|
|
Assert.Equal(expected.Parts[j].Location.Y, actual.Parts[j].Location.Y, 6);
|
|
Assert.Equal(expected.Parts[j].Rotation, actual.Parts[j].Rotation, 6);
|
|
}
|
|
}
|
|
Assert.Equal(2, loaded.Drawings.Single(d => d.Name == "neutral-frame").Quantity.Nested);
|
|
Assert.Equal(2, loaded.Drawings.Single(d => d.Name == "neutral-insert").Quantity.Nested);
|
|
}
|
|
}
|