Files
OpenNest/OpenNest.WinForms.Tests/CutOffWindowSelectionTests.cs
T

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.");
}