Files
OpenNest/OpenNest.Tests/CutOffs/CutOffFirstCrossingTests.cs
T
ajandClaude Opus 5.5 889e33d179 feat(ui): stop new sheet cut-offs at the first crossing
A manual cut-off ran the full sheet even when another cut-off crossed it,
leaving a Trim/Extend pass to clean up what Automatic Scrap Cutoffs does
on its own. New lines now run from their start edge, set by the cut
direction, and stop at the first crossing line. Tab switches to full
length for the lines that should run through, and the choice lasts until
the nest is closed.

Suggested-by: alexvyperus
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 07:20:56 -04:00

142 lines
4.4 KiB
C#

using OpenNest.CNC;
using OpenNest.Geometry;
namespace OpenNest.Tests.CutOffs;
/// <summary>
/// A new Sheet Cut-Off line runs from its start edge, set by the cut direction, and stops at the
/// first crossing line it meets, unless that would leave it too short.
/// </summary>
public class CutOffFirstCrossingTests
{
private static Plate PlateWithHorizontals(int quadrant, params double[] ys)
{
var plate = new Plate(48, 96) { Quadrant = quadrant };
foreach (var y in ys)
plate.CutOffs.Add(new CutOff(new Vector(0, y), CutOffAxis.Horizontal));
return plate;
}
private static CutOffEdit Stop(Plate plate, CutOff line, CutDirection direction = CutDirection.AwayFromOrigin)
{
var edit = CutOffTrim.StopAtFirstCrossing(plate, line, new CutOffSettings { CutDirection = direction });
Assert.True(edit.Succeeded, edit.Refusal);
edit.Apply();
return edit;
}
[Fact]
public void AwayFromOrigin_StopsAtTheCrossingNearestTheOrigin()
{
var plate = PlateWithHorizontals(1, 30, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Stop(plate, line);
Assert.Null(line.StartLimit);
Assert.Equal(10, line.EndLimit);
}
[Fact]
public void TowardOrigin_StopsAtTheCrossingNearestTheFarEdge()
{
var plate = PlateWithHorizontals(1, 30, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Stop(plate, line, CutDirection.TowardOrigin);
Assert.Equal(30, line.StartLimit);
Assert.Null(line.EndLimit);
}
[Fact]
public void AwayFromOrigin_StartsAtTheOriginEdgeInAnyQuadrant()
{
var plate = PlateWithHorizontals(3, -30, -10);
var line = new CutOff(new Vector(-40, -25), CutOffAxis.Vertical);
Stop(plate, line);
Assert.Equal(-10, line.StartLimit);
Assert.Null(line.EndLimit);
}
[Fact]
public void HorizontalLineStopsAtTheFirstVertical()
{
var plate = new Plate(48, 96);
plate.CutOffs.Add(new CutOff(new Vector(60, 0), CutOffAxis.Vertical));
plate.CutOffs.Add(new CutOff(new Vector(20, 0), CutOffAxis.Vertical));
var line = new CutOff(new Vector(70, 12), CutOffAxis.Horizontal);
Stop(plate, line);
Assert.Null(line.StartLimit);
Assert.Equal(20, line.EndLimit);
}
[Fact]
public void StaysFullLengthWhenNothingCrossesIt()
{
var plate = new Plate(48, 96);
plate.CutOffs.Add(new CutOff(new Vector(10, 0), CutOffAxis.Vertical));
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Assert.Empty(Stop(plate, line).Changes);
Assert.Null(line.StartLimit);
Assert.Null(line.EndLimit);
}
[Fact]
public void ALineStoppedShortOfItDoesNotCross()
{
var plate = new Plate(48, 96);
plate.CutOffs.Add(new CutOff(new Vector(0, 10), CutOffAxis.Horizontal) { EndLimit = 30 });
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Assert.Empty(Stop(plate, line).Changes);
}
[Fact]
public void StaysFullLengthWhenTheLineLeftWouldBeTooShort()
{
var plate = PlateWithHorizontals(1, 0.03, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Assert.Empty(Stop(plate, line).Changes);
Assert.Null(line.EndLimit);
}
[Fact]
public void StoppingShortOfTheFarEdgeSaysTheOvercutIsOff()
{
var plate = PlateWithHorizontals(1, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical) { HeadLockOvercut = 1.5 };
Assert.Equal(CutOffTrim.OvercutOff, Stop(plate, line).Notice);
}
[Fact]
public void ReachingTheFarEdgeKeepsTheOvercut()
{
var plate = PlateWithHorizontals(1, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical) { HeadLockOvercut = 1.5 };
Assert.Null(Stop(plate, line, CutDirection.TowardOrigin).Notice);
}
[Fact]
public void TheRegeneratedLineEndsOnTheCrossing()
{
var plate = PlateWithHorizontals(1, 10);
var line = new CutOff(new Vector(40, 25), CutOffAxis.Vertical);
Stop(plate, line);
plate.CutOffs.Add(line);
plate.RegenerateCutOffs(new CutOffSettings());
var moves = line.Drawing.Program.Codes.OfType<LinearMove>().ToList();
Assert.Equal(10, moves.Max(move => move.EndPoint.Y), 6);
}
}