using System.Diagnostics;
using OpenNest.CNC;
using OpenNest.Engine.Fill;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.Jobs.Placement;
using OpenNest.Geometry;
using Xunit.Abstractions;
namespace OpenNest.Engine.Tests.Fill;
///
/// A reported nest: a 7" hexagon cover with a 2.5" center hole and six bolt holes on a
/// 60 x 120 plate. Fill tiled 10 staggered rows of 18 (180) and left 2.7" above the top row;
/// lifting that row straight over the one below opens a notch at its end where one more
/// hexagon nests (181), as the user laid it out by hand.
///
public class EndRowRefillTests
{
private readonly ITestOutputHelper output;
public EndRowRefillTests(ITestOutputHelper output) => this.output = output;
private static Plate HexPlate() =>
new(new Size(60, 120))
{
PartSpacing = 0.25,
EdgeSpacing = new Spacing(0.25, 0.25, 0.25, 0.25),
};
/// The reported drawing's program, in absolute coordinates.
private static Drawing HexCover()
{
var program = new Program();
foreach (var (x, y, r) in new[]
{
(0.0, 0.0, 0.1875), (-1.9918584287, 1.15, 0.1875), (-3.9837168574, 0.0, 0.1875),
(-3.9837168574, -2.3, 0.1875), (-1.9918584287, -3.45, 0.1875), (0.0, -2.3, 0.1875),
(-0.9293584287, -1.15, 1.25),
})
{
program.MoveTo(x, y);
program.ArcTo(new Vector(x, y), new Vector(x - r, y), RotationType.CCW);
}
program.MoveTo(1.3206415713, -1.15);
foreach (var (x, y) in new[]
{
(-0.4293584287, -4.1810889132), (-3.9293584287, -4.1810889132), (-5.6793584287, -1.15),
(-3.9293584287, 1.8810889132), (-0.4293584287, 1.8810889132), (1.3206415713, -1.15),
})
program.LineTo(x, y);
return new Drawing("Hex Cover", program);
}
[Fact]
public void FillNestsOneMoreHexagonAtTheEndOfTheLiftedTopRow()
{
var plate = HexPlate();
var drawing = HexCover();
var parts = PlateFillService.FillItem("Fill", plate,
new NestItem { Drawing = drawing, Quantity = 0 }, plate.WorkArea(), null,
CancellationToken.None);
Assert.Equal(181, parts.Count);
Assert.Empty(NestLayoutCheck.Validate(new() { (plate, parts) },
new Dictionary { [drawing] = ("Hex Cover", 181) }));
}
[Fact]
public void ImproveKeepsAGridThatHasNoRoomAtItsEnds()
{
var plate = new Plate(new Size(40, 40)) { PartSpacing = 0.5 };
var drawing = new Drawing("block", Jobs.TestDrawingFactory.Rectangle(9.5, 9.5));
var parts = new List();
for (var x = 0; x < 4; x++)
for (var y = 0; y < 4; y++)
parts.Add(new Part(drawing, new Vector(x * 10, y * 10)));
var result = EndRowRefiller.Improve(parts, plate.WorkArea(), plate.PartSpacing,
new DefaultFillComparer(), 0, CancellationToken.None);
Assert.Same(parts, result);
}
[Fact]
public void ImproveStopsAtTheRequestedQuantity()
{
var plate = HexPlate();
var drawing = HexCover();
var full = PlateFillService.FillItem("Fill", plate,
new NestItem { Drawing = drawing, Quantity = 0 }, plate.WorkArea(), null,
CancellationToken.None);
var capped = EndRowRefiller.Improve(full, plate.WorkArea(), plate.PartSpacing,
new DefaultFillComparer(), full.Count, CancellationToken.None);
Assert.Same(full, capped);
// Reports what the pass costs on a full plate; not asserted.
var stopwatch = Stopwatch.StartNew();
EndRowRefiller.Improve(full, plate.WorkArea(), plate.PartSpacing,
new DefaultFillComparer(), 0, CancellationToken.None);
output.WriteLine($"Improve on {full.Count} parts: {stopwatch.ElapsedMilliseconds} ms");
}
}