mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-14 18:21:55 -04:00
test(cutoffs): verify shifted lines through apply save and rerun
This commit is contained in:
1 parent
6d5d963654
commit
b245290664
1 file changed
+140
@@ -0,0 +1,140 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.IO;
|
||||
|
||||
namespace OpenNest.Tests.CutOffs;
|
||||
|
||||
public class AutomaticCutOffShiftLifecycleTests
|
||||
{
|
||||
private static readonly AutomaticCutOffOptions Shifted = new()
|
||||
{
|
||||
Spacing = 35,
|
||||
MinimumStripLength = 17.5,
|
||||
MinimumTailLength = 12,
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
public void BatchApply_SaveReload_RerunAndMove_PreserveShiftedDefinitionsAndClearance(int quadrant)
|
||||
{
|
||||
var nest = new Nest("shifted lifecycle");
|
||||
var plate = AutomaticCutOffShiftTests.UserCase(quadrant);
|
||||
for (var index = 0; index < plate.Parts.Count; index++)
|
||||
{
|
||||
// The nest drawing collection keys by name; distinct synthetic shapes
|
||||
// must have distinct names for the writer to retain every part.
|
||||
var drawing = plate.Parts[index].BaseDrawing;
|
||||
drawing.Name = $"shift rectangle {index}";
|
||||
nest.Drawings.Add(drawing);
|
||||
}
|
||||
nest.Plates.Add(plate);
|
||||
var settings = new CutOffSettings();
|
||||
var realParts = plate.Parts.ToArray();
|
||||
var realPrograms = realParts.Select(p => p.Program).ToArray();
|
||||
var proposal = Assert.Single(AutomaticCutOffBatch.Create(new[] { plate }, Shifted, settings));
|
||||
Assert.Equal(3, proposal.Definitions.Count);
|
||||
Assert.Empty(plate.CutOffs);
|
||||
Assert.Equal(realParts, plate.Parts.ToArray());
|
||||
|
||||
var applied = Assert.Single(AutomaticCutOffBatch.Apply(new[] { plate }, Shifted, settings));
|
||||
Assert.Equal(proposal.Definitions.Select(c => c.Position.X), applied.Definitions.Select(c => c.Position.X));
|
||||
var locations = plate.CutOffs.Select(c => c.Position.X).ToArray();
|
||||
AssertCutsClearRealRectangles(plate, settings.PartClearance);
|
||||
Assert.Equal(realPrograms, realParts.Select(p => p.Program));
|
||||
Assert.Empty(Assert.Single(AutomaticCutOffBatch.Apply(new[] { plate }, Shifted, settings)).Definitions);
|
||||
Assert.Equal(locations, plate.CutOffs.Select(c => c.Position.X));
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
Assert.True(new NestWriter(nest).Write(stream));
|
||||
stream.Position = 0;
|
||||
var loaded = new NestReader(stream).Read();
|
||||
var reloaded = Assert.Single(loaded.Plates);
|
||||
Assert.Equal(locations, reloaded.CutOffs.Select(c => c.Position.X));
|
||||
Assert.Equal(realParts.Length, reloaded.Parts.Count(p => !p.BaseDrawing.IsCutOff));
|
||||
var reloadedPlan = AutomaticCutOffPlanner.Create(reloaded, Shifted, settings);
|
||||
Assert.Equal(proposal.OccupiedSpan, reloadedPlan.OccupiedSpan);
|
||||
Assert.Empty(reloadedPlan.Definitions);
|
||||
AssertCutsClearRealRectangles(reloaded, settings.PartClearance);
|
||||
|
||||
var moved = reloaded.Parts.First(p => !p.BaseDrawing.IsCutOff &&
|
||||
System.Math.Abs(p.BoundingBox.Right - p.BoundingBox.Left - 10) < 1e-6);
|
||||
moved.Offset(quadrant is 2 or 3 ? new Vector(-1, 0) : new Vector(1, 0));
|
||||
reloaded.RegenerateCutOffs(settings);
|
||||
Assert.Equal(locations, reloaded.CutOffs.Select(c => c.Position.X));
|
||||
AssertCutsClearRealRectangles(reloaded, settings.PartClearance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShiftedBatch_LimitedSeparatorOnLaterPlateRefusesAllWithoutMutation()
|
||||
{
|
||||
var plates = new[] { AutomaticCutOffShiftTests.UserCase(), AutomaticCutOffShiftTests.UserCase() };
|
||||
var settings = new CutOffSettings();
|
||||
var separator = AutomaticCutOffPlanner.Create(plates[1], Shifted, settings).TailSeparatorX!.Value;
|
||||
var manual = new CutOff(new Vector(separator, 0), CutOffAxis.Vertical) { EndLimit = 2 };
|
||||
plates[1].CutOffs.Add(manual);
|
||||
var before = plates.Select(p => p.Parts.ToArray()).ToArray();
|
||||
|
||||
var result = AutomaticCutOffBatch.Apply(plates, Shifted, settings);
|
||||
|
||||
Assert.True(result[1].HasBlockingDiagnostics);
|
||||
Assert.Empty(plates[0].CutOffs);
|
||||
Assert.Same(manual, Assert.Single(plates[1].CutOffs));
|
||||
for (var i = 0; i < plates.Length; i++)
|
||||
Assert.Equal(before[i], plates[i].Parts.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShiftedBatch_LateRegenerationFailureRollsBackEveryTouchedPlate()
|
||||
{
|
||||
var plates = new[] { AutomaticCutOffShiftTests.UserCase(), AutomaticCutOffShiftTests.UserCase() };
|
||||
var before = plates.Select(p => p.Parts.ToArray()).ToArray();
|
||||
var programs = plates.Select(p => p.Parts.Select(part => part.Program).ToArray()).ToArray();
|
||||
var failOnce = true;
|
||||
plates[1].PartAdded += (_, e) =>
|
||||
{
|
||||
if (failOnce && e.Item.BaseDrawing.IsCutOff)
|
||||
{
|
||||
failOnce = false;
|
||||
throw new InvalidOperationException("Injected regeneration failure");
|
||||
}
|
||||
};
|
||||
|
||||
var error = Assert.Throws<InvalidOperationException>(() =>
|
||||
AutomaticCutOffBatch.Apply(plates, Shifted, new CutOffSettings()));
|
||||
|
||||
Assert.False(failOnce);
|
||||
Assert.Contains("original cut-offs were restored", error.Message);
|
||||
for (var i = 0; i < plates.Length; i++)
|
||||
{
|
||||
Assert.Empty(plates[i].CutOffs);
|
||||
Assert.Equal(before[i], plates[i].Parts.ToArray());
|
||||
Assert.Equal(programs[i], plates[i].Parts.Select(p => p.Program).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertCutsClearRealRectangles(Plate plate, double clearance)
|
||||
{
|
||||
var material = plate.Parts.Where(p => !p.BaseDrawing.IsCutOff).Select(p => p.BoundingBox).ToArray();
|
||||
foreach (var cut in plate.Parts.Where(p => p.BaseDrawing.IsCutOff))
|
||||
{
|
||||
var codes = cut.Program.Codes;
|
||||
Assert.Equal(0, codes.Count % 2);
|
||||
for (var index = 0; index < codes.Count; index += 2)
|
||||
{
|
||||
var from = Assert.IsType<RapidMove>(codes[index]).EndPoint + cut.Location;
|
||||
var to = Assert.IsType<LinearMove>(codes[index + 1]).EndPoint + cut.Location;
|
||||
var left = System.Math.Min(from.X, to.X);
|
||||
var right = System.Math.Max(from.X, to.X);
|
||||
var bottom = System.Math.Min(from.Y, to.Y);
|
||||
var top = System.Math.Max(from.Y, to.Y);
|
||||
Assert.All(material, box => Assert.True(
|
||||
right <= box.Left - clearance || left >= box.Right + clearance ||
|
||||
top <= box.Bottom - clearance || bottom >= box.Top + clearance,
|
||||
$"Cut {from} -> {to} crosses a rectangle or its clearance."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user