using OpenNest.CNC;
using OpenNest.Converters;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.Jobs.Adapters;
using OpenNest.Geometry;
using OpenNest.TabbedCutouts;
namespace OpenNest.Engine.Tests.Jobs;
///
/// A 20 x 20 frame with a 10 x 10 hole and a 6 x 6 insert on a 22 x 22 sheet: the insert only
/// fits inside the hole, which a tabbed cutout's attached slug fills.
///
[Collection("Cutout pipeline registry")]
public class TabbedCutoutNestingTests
{
private static Program Frame(bool tabbed)
{
var hole = new Entity[] { new Line(5, 5, 15, 5), new Line(15, 5, 15, 15), new Line(15, 15, 5, 15), new Line(5, 15, 5, 5) };
var entities = new List
{
new Line(0, 0, 20, 0), new Line(20, 0, 20, 20), new Line(20, 20, 0, 20), new Line(0, 20, 0, 0),
};
entities.AddRange(hole);
if (tabbed)
entities = TabbedCutoutBuilder.Apply(entities,
[new TabbedCutout { EdgeIds = hole.Select(e => e.Id).ToList() }], out _);
return ConvertGeometry.ToProgram(ShapeProfile.NormalizeEntities(entities));
}
private static NestPipelineRequest Request(string engine, bool tabbed) =>
new(engine,
new[] { new NestItem { Drawing = new Drawing("frame", Frame(tabbed)), Quantity = 1 },
new NestItem { Drawing = new Drawing("insert", TestDrawingFactory.Rectangle(6, 6)), Quantity = 1 } },
new[] { new NestPlateStock("sheet", new Size(22, 22), 1, 0.25) },
new NestJobOptions(maxPlates: 1));
[Fact]
public void TabbedHole_IsNoCutoutForNesting_AndItsSlugCountsAsMaterial()
{
var geometry = JobPartGeometry.Read(DrawingJobMapper.FromDrawing("frame", new Drawing("frame", Frame(true)), 1).Geometry);
Assert.Empty(geometry.Cutouts);
Assert.Equal(400, geometry.MaterialArea, 6);
Assert.Single(JobPartGeometry.Read(
DrawingJobMapper.FromDrawing("frame", new Drawing("frame", Frame(false)), 1).Geometry).Cutouts);
}
[Fact]
public void TabbedHole_KeepsItsHoleForAreaAndCutting()
{
var drawing = new Drawing("frame", Frame(true));
Assert.Equal(300, drawing.Area, 6);
// Nesting sees only the frame's outline: the hole's pieces and tabs are gone.
var nesting = ConvertProgram.ToGeometry(drawing.Program).ForNesting();
Assert.Equal(4, nesting.Count);
Assert.Equal(400, new ShapeProfile(nesting).Perimeter.Area(), 6);
}
[Fact]
public void UntabbedEntities_PassThroughUnchanged()
{
var material = ConvertProgram.ToGeometry(Frame(false));
Assert.Equal(material.Where(e => SpecialLayers.IsMaterial(e.Layer)), material.ForNesting());
}
[Theory]
[InlineData("Irregular")]
[InlineData("Default")]
public void NoPartIsNestedInsideATabbedHole(string engine)
{
var plain = NestPipeline.Run(Request(engine, tabbed: false));
Assert.Equal(NestJobStatus.Complete, plain.Status);
var result = NestPipeline.Run(Request(engine, tabbed: true));
Assert.True(result.IsValid, string.Join("; ", result.Violations));
Assert.Equal(NestJobStatus.Incomplete, result.Status);
Assert.DoesNotContain(result.Plates.SelectMany(p => p.Parts), p => p.BaseDrawing.Name == "insert");
}
}