mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-15 17:01:36 -04:00
211 lines
8.9 KiB
C#
211 lines
8.9 KiB
C#
using System.Drawing;
|
|
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
using OpenNest.Actions;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Controls;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.WinForms.Tests;
|
|
|
|
public class CutOffWindowSelectionTests
|
|
{
|
|
[Theory]
|
|
[InlineData(CutOffAxis.Vertical, 1)]
|
|
[InlineData(CutOffAxis.Horizontal, 2)]
|
|
[InlineData(CutOffAxis.Vertical, 3)]
|
|
[InlineData(CutOffAxis.Horizontal, 4)]
|
|
public void WindowSelectsVisibleCutOffSegmentsInBothDirections(CutOffAxis axis, int quadrant) => RunSta(() =>
|
|
{
|
|
var plate = new Plate(20, 20) { Quadrant = quadrant };
|
|
var bounds = plate.BoundingBox(includeParts: false);
|
|
var cutoff = new CutOff(bounds.Center, axis);
|
|
plate.CutOffs.Add(cutoff);
|
|
cutoff.Regenerate(plate, new CutOffSettings());
|
|
using var view = new PlateView { Plate = plate };
|
|
var full = GraphRect(view, new Vector(bounds.Left - 1, bounds.Bottom - 1),
|
|
new Vector(bounds.Right + 1, bounds.Top + 1));
|
|
var crossing = GraphRect(view, new Vector(bounds.Center.X - 1, bounds.Center.Y - 1),
|
|
new Vector(bounds.Center.X + 1, bounds.Center.Y + 1));
|
|
var missed = GraphRect(view, new Vector(bounds.Left + 1, bounds.Bottom + 1),
|
|
new Vector(bounds.Left + 2, bounds.Bottom + 2));
|
|
|
|
Assert.Contains(cutoff, view.Selection.GetCutOffsFromWindow(full, SelectionType.Contains));
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(crossing, SelectionType.Contains));
|
|
Assert.Contains(cutoff, view.Selection.GetCutOffsFromWindow(crossing, SelectionType.Intersect));
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(missed, SelectionType.Intersect));
|
|
});
|
|
|
|
[Fact]
|
|
public void WindowUsesActualSegmentsNotTheNominalLineOrGap() => RunSta(() =>
|
|
{
|
|
using var view = new PlateView();
|
|
var cutoff = new CutOff(new Vector(10, 10), CutOffAxis.Vertical);
|
|
view.Plate.CutOffs.Add(cutoff);
|
|
cutoff.Drawing.Program = new CNC.Program();
|
|
cutoff.Drawing.Program.Codes.Add(new RapidMove(new Vector(10, 0)));
|
|
cutoff.Drawing.Program.Codes.Add(new LinearMove(new Vector(10, 4)));
|
|
cutoff.Drawing.Program.Codes.Add(new RapidMove(new Vector(10, 16)));
|
|
cutoff.Drawing.Program.Codes.Add(new LinearMove(new Vector(10, 20)));
|
|
|
|
var gap = GraphRect(view, new Vector(9, 8), new Vector(11, 12));
|
|
var oneSegment = GraphRect(view, new Vector(9, -1), new Vector(11, 5));
|
|
var full = GraphRect(view, new Vector(9, -1), new Vector(11, 21));
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(gap, SelectionType.Intersect));
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(oneSegment, SelectionType.Contains));
|
|
Assert.Contains(cutoff, view.Selection.GetCutOffsFromWindow(oneSegment, SelectionType.Intersect));
|
|
Assert.Contains(cutoff, view.Selection.GetCutOffsFromWindow(full, SelectionType.Contains));
|
|
|
|
cutoff.Drawing.Program = new CNC.Program();
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(full, SelectionType.Intersect));
|
|
Assert.Empty(view.Selection.GetCutOffsFromWindow(full, SelectionType.Contains));
|
|
});
|
|
|
|
[Fact]
|
|
public void ActionSelectsPartsAndCutOffsTogetherAndDeleteRemovesBoth() => RunSta(() =>
|
|
{
|
|
var plate = new Plate(20, 20);
|
|
var program = new CNC.Program();
|
|
program.MoveTo(2, 2);
|
|
program.LineTo(4, 2);
|
|
program.LineTo(4, 4);
|
|
program.LineTo(2, 4);
|
|
program.LineTo(2, 2);
|
|
plate.Parts.Add(new Part(new Drawing("square", program)));
|
|
var cutoff = new CutOff(new Vector(10, 10), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(cutoff);
|
|
cutoff.Regenerate(plate, new CutOffSettings());
|
|
using var view = new PlateView { Plate = plate };
|
|
var action = new ActionSelect(view);
|
|
try
|
|
{
|
|
action.Point1 = new Vector(0, 0);
|
|
action.Point2 = new Vector(20, 20); // left-to-right: contain
|
|
SelectWindow(action);
|
|
SelectWindow(action); // repeated selection must not duplicate a definition
|
|
Assert.Single(view.SelectedParts);
|
|
Assert.Same(cutoff, Assert.Single(view.Selection.SelectedCutOffs));
|
|
Assert.True(action.IsBusy());
|
|
|
|
view.Selection.DeleteSelected();
|
|
Assert.Empty(plate.CutOffs);
|
|
Assert.Empty(plate.Parts);
|
|
Assert.Empty(view.SelectedParts);
|
|
Assert.Empty(view.Selection.SelectedCutOffs);
|
|
|
|
plate.CutOffs.Add(cutoff);
|
|
cutoff.Regenerate(plate, view.CutOffSettings);
|
|
action.Point1 = new Vector(12, 12);
|
|
action.Point2 = new Vector(8, 8); // right-to-left: intersect
|
|
SelectWindow(action);
|
|
Assert.Same(cutoff, Assert.Single(view.Selection.SelectedCutOffs));
|
|
Assert.True(action.IsBusy());
|
|
action.CancelAction();
|
|
Assert.Empty(view.Selection.SelectedCutOffs);
|
|
Assert.False(action.IsBusy());
|
|
}
|
|
finally
|
|
{
|
|
action.DisconnectEvents();
|
|
}
|
|
});
|
|
|
|
[Fact]
|
|
public void MixedDeleteRegeneratesSurvivingCutOffAfterRemovingPart() => RunSta(() =>
|
|
{
|
|
var program = new CNC.Program();
|
|
program.MoveTo(9, 5);
|
|
program.LineTo(11, 5);
|
|
program.LineTo(11, 10);
|
|
program.LineTo(9, 10);
|
|
program.LineTo(9, 5);
|
|
var part = new Part(new Drawing("square", program));
|
|
var plate = new Plate(20, 20);
|
|
plate.Parts.Add(part);
|
|
var removed = new CutOff(new Vector(2, 0), CutOffAxis.Vertical)
|
|
{
|
|
StartLimit = 4,
|
|
EndLimit = 11,
|
|
};
|
|
var kept = new CutOff(new Vector(10, 0), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(removed);
|
|
plate.CutOffs.Add(kept);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
using var view = new PlateView { Plate = plate };
|
|
var action = (ActionSelect)view.Actions.CurrentAction;
|
|
action.Point1 = new Vector(0, 3);
|
|
action.Point2 = new Vector(12, 12);
|
|
SelectWindow(action);
|
|
Assert.Contains(view.SelectedParts, selected => selected.BasePart == part);
|
|
Assert.Same(removed, Assert.Single(view.Selection.SelectedCutOffs));
|
|
Assert.True(kept.Drawing.Program.Codes.Count > 2);
|
|
|
|
view.Selection.DeleteSelected();
|
|
Assert.Same(kept, Assert.Single(plate.CutOffs));
|
|
Assert.Empty(view.SelectedParts);
|
|
Assert.Equal(2, kept.Drawing.Program.Codes.Count);
|
|
Assert.Equal(2, Assert.Single(plate.Parts).Program.Codes.Count);
|
|
});
|
|
|
|
[Fact]
|
|
public void EscapeStopsCutOffDragBeforeAnotherWindowSelection() => RunSta(() =>
|
|
{
|
|
var plate = new Plate(20, 20);
|
|
var cutoff = new CutOff(new Vector(10, 0), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(cutoff);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
using var view = new EventView { Plate = plate, Size = new System.Drawing.Size(500, 500) };
|
|
view.ZoomToArea(-2, -2, 24, 24);
|
|
view.Down(new Vector(10, 2));
|
|
Assert.True(view.CutOffs.IsDragging);
|
|
view.ProcessEscapeKey();
|
|
Assert.Empty(view.Selection.SelectedCutOffs);
|
|
Assert.False(view.CutOffs.IsDragging);
|
|
view.Up(new Vector(10, 2));
|
|
view.Down(new Vector(12, 12));
|
|
view.MoveTo(new Vector(8, -1), MouseButtons.Left);
|
|
view.Up(new Vector(8, -1));
|
|
Assert.Same(cutoff, Assert.Single(view.Selection.SelectedCutOffs));
|
|
var before = cutoff.Position;
|
|
view.MoveTo(new Vector(13, 8), MouseButtons.None);
|
|
Assert.Equal(before.X, cutoff.Position.X);
|
|
});
|
|
|
|
private sealed class EventView : PlateView
|
|
{
|
|
public void Down(Vector world)
|
|
{
|
|
MoveTo(world, MouseButtons.None);
|
|
var point = PointWorldToControl(world);
|
|
OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, point.X, point.Y, 0));
|
|
}
|
|
|
|
public void Up(Vector world)
|
|
{
|
|
var point = PointWorldToControl(world);
|
|
OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, point.X, point.Y, 0));
|
|
}
|
|
|
|
public void MoveTo(Vector world, MouseButtons buttons)
|
|
{
|
|
var point = PointWorldToControl(world);
|
|
OnMouseMove(new MouseEventArgs(buttons, 0, point.X, point.Y, 0));
|
|
}
|
|
}
|
|
|
|
private static void SelectWindow(ActionSelect action) =>
|
|
typeof(ActionSelect).GetMethod("SelectPartsInWindow", BindingFlags.Instance | BindingFlags.NonPublic)!
|
|
.Invoke(action, null);
|
|
|
|
private static RectangleF GraphRect(PlateView view, Vector first, Vector second)
|
|
{
|
|
var a = view.PointWorldToGraph(first);
|
|
var b = view.PointWorldToGraph(second);
|
|
return RectangleF.FromLTRB(System.Math.Min(a.X, b.X), System.Math.Min(a.Y, b.Y),
|
|
System.Math.Max(a.X, b.X), System.Math.Max(a.Y, b.Y));
|
|
}
|
|
|
|
private static void RunSta(System.Action action) =>
|
|
StaTestThread.Run(action, TimeSpan.FromSeconds(30), "Cut-off window selection test timed out.");
|
|
}
|