mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-07 04:15:43 -05:00
Copy and Cut paste through ActionClone, which added each clone to the plate but never recorded the groups the sources were in, so a pasted common-line pair came out as separate parts held to the part spacing. ActionClone now reads the copied parts' groups when it starts (before Cut removes the sources and their groups) and joins each paste's clones the same way, at the source group's kerf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
189 lines
7.0 KiB
C#
189 lines
7.0 KiB
C#
using OpenNest.Actions;
|
|
using OpenNest.Controls;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.WinForms.Tests.Controls;
|
|
|
|
public class CommonLineSelectionTests
|
|
{
|
|
private const double Kerf = 0.06;
|
|
|
|
[Fact]
|
|
public void SelectingOneMemberSelectsItsWholeGroup() => RunSta(() =>
|
|
{
|
|
var (plate, left, right, loose) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
|
|
view.Selection.Select(Layout(view, left));
|
|
|
|
Assert.Equal(new[] { left, right }, view.SelectedParts.Select(p => p.BasePart));
|
|
Assert.All(view.SelectedParts, p => Assert.True(p.IsSelected));
|
|
Assert.False(Layout(view, loose).IsSelected);
|
|
});
|
|
|
|
[Fact]
|
|
public void SelectingALoosePartSelectsOnlyThatPart() => RunSta(() =>
|
|
{
|
|
var (plate, _, _, loose) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
|
|
view.Selection.Select(Layout(view, loose));
|
|
|
|
Assert.Equal(new[] { loose }, view.SelectedParts.Select(p => p.BasePart));
|
|
});
|
|
|
|
[Fact]
|
|
public void NudgingOneMemberMovesTheJoinedPairAndKeepsItsSharedLine() => RunSta(() =>
|
|
{
|
|
var (plate, left, right, _) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
var leftStart = left.BoundingBox.Location;
|
|
var rightStart = right.BoundingBox.Location;
|
|
|
|
view.Selection.Select(Layout(view, right));
|
|
view.Selection.NudgeSelected(1, 2);
|
|
|
|
Assert.Equal(leftStart + new Vector(1, 2), left.BoundingBox.Location);
|
|
Assert.Equal(rightStart + new Vector(1, 2), right.BoundingBox.Location);
|
|
var group = Assert.Single(plate.CommonLines);
|
|
Assert.True(CommonLineGeometry.Check(group).IsIntact);
|
|
});
|
|
|
|
[Fact]
|
|
public void PartMenuOffersRemoveForAGroupAndJoinWhenALoosePartIsAdded() => RunSta(() =>
|
|
{
|
|
var (plate, left, _, loose) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
var joins = 0;
|
|
var removes = 0;
|
|
view.JoinCommonLineCommand = () => joins++;
|
|
view.RemoveCommonLineCommand = () => removes++;
|
|
var onLeft = view.PointWorldToControl(left.BoundingBox.Center);
|
|
|
|
// Off the selection there is no menu.
|
|
Assert.Null(view.PartMenu.BuildMenu(onLeft));
|
|
|
|
view.Selection.Select(Layout(view, left));
|
|
var items = MenuItems(view.PartMenu.BuildMenu(onLeft));
|
|
Assert.Equal(new[] { "Join Common Line", "Remove Common Line" }, items.Select(i => i.Text));
|
|
Assert.False(items[0].Enabled);
|
|
Assert.True(items[1].Enabled);
|
|
|
|
view.Selection.Select(Layout(view, loose));
|
|
items = MenuItems(view.PartMenu.BuildMenu(onLeft));
|
|
Assert.True(items[0].Enabled);
|
|
items[0].PerformClick();
|
|
items[1].PerformClick();
|
|
Assert.Equal(1, joins);
|
|
Assert.Equal(1, removes);
|
|
});
|
|
|
|
[Fact]
|
|
public void PartMenuOffersJoinButNotRemoveForLooseParts() => RunSta(() =>
|
|
{
|
|
var (plate, _, _, loose) = JoinedPairWithLoosePart();
|
|
var other = new Part(loose.BaseDrawing, new Vector(30, 10));
|
|
plate.Parts.Add(other);
|
|
using var view = new PlateView { Plate = plate };
|
|
view.JoinCommonLineCommand = () => { };
|
|
view.RemoveCommonLineCommand = () => { };
|
|
|
|
view.Selection.Select(Layout(view, loose));
|
|
var onLoose = view.PointWorldToControl(loose.BoundingBox.Center);
|
|
var items = MenuItems(view.PartMenu.BuildMenu(onLoose));
|
|
Assert.False(items[0].Enabled);
|
|
Assert.False(items[1].Enabled);
|
|
|
|
view.Selection.Select(Layout(view, other));
|
|
items = MenuItems(view.PartMenu.BuildMenu(onLoose));
|
|
Assert.True(items[0].Enabled);
|
|
Assert.False(items[1].Enabled);
|
|
|
|
view.InspectionOnly = true;
|
|
Assert.Null(view.PartMenu.BuildMenu(onLoose));
|
|
});
|
|
|
|
[Fact]
|
|
public void PartMenuIsAbsentWithoutHostCommands() => RunSta(() =>
|
|
{
|
|
var (plate, left, _, _) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
view.Selection.Select(Layout(view, left));
|
|
|
|
Assert.Null(view.PartMenu.BuildMenu(view.PointWorldToControl(left.BoundingBox.Center)));
|
|
});
|
|
|
|
[Fact]
|
|
public void PastingACopiedJoinedPairKeepsTheCopiesJoined() => RunSta(() =>
|
|
{
|
|
var (plate, left, right, loose) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
|
|
view.SetAction(typeof(ActionClone), new List<Part> { left, right, loose });
|
|
Assert.IsType<ActionClone>(view.Actions.CurrentAction).Apply();
|
|
|
|
Assert.Equal(6, plate.Parts.Count);
|
|
Assert.Equal(2, plate.CommonLines.Count);
|
|
var pasted = plate.CommonLines[1];
|
|
Assert.DoesNotContain(pasted.Members, p => p == left || p == right || p == loose);
|
|
Assert.Equal(Kerf, pasted.Kerf);
|
|
Assert.True(CommonLineGeometry.Check(pasted).IsIntact);
|
|
});
|
|
|
|
[Fact]
|
|
public void PastingACutJoinedPairKeepsItJoined() => RunSta(() =>
|
|
{
|
|
var (plate, left, right, _) = JoinedPairWithLoosePart();
|
|
using var view = new PlateView { Plate = plate };
|
|
|
|
// Edit > Cut starts the clone, then removes the sources and their group.
|
|
view.SetAction(typeof(ActionClone), new List<Part> { left, right });
|
|
plate.Parts.Remove(left);
|
|
plate.Parts.Remove(right);
|
|
Assert.Empty(plate.CommonLines);
|
|
Assert.IsType<ActionClone>(view.Actions.CurrentAction).Apply();
|
|
|
|
var group = Assert.Single(plate.CommonLines);
|
|
Assert.Equal(2, group.Members.Count);
|
|
Assert.True(CommonLineGeometry.Check(group).IsIntact);
|
|
});
|
|
|
|
private static List<System.Windows.Forms.ToolStripItem> MenuItems(System.Windows.Forms.ContextMenuStrip? menu)
|
|
{
|
|
Assert.NotNull(menu);
|
|
return menu.Items.Cast<System.Windows.Forms.ToolStripItem>().ToList();
|
|
}
|
|
|
|
private static (Plate Plate, Part Left, Part Right, Part Loose) JoinedPairWithLoosePart()
|
|
{
|
|
var plate = new Plate(40, 20);
|
|
var drawing = new Drawing("square", Square(4));
|
|
var left = new Part(drawing, new Vector(2, 2));
|
|
var right = new Part(drawing, new Vector(6 + Kerf, 2));
|
|
var loose = new Part(drawing, new Vector(20, 10));
|
|
plate.Parts.Add(left);
|
|
plate.Parts.Add(right);
|
|
plate.Parts.Add(loose);
|
|
plate.JoinCommonLine(new[] { left, right }, Kerf);
|
|
Assert.True(CommonLineGeometry.Check(plate.CommonLines[0]).IsIntact);
|
|
return (plate, left, right, loose);
|
|
}
|
|
|
|
private static CNC.Program Square(double size)
|
|
{
|
|
var program = new CNC.Program();
|
|
program.MoveTo(0, 0);
|
|
program.LineTo(size, 0);
|
|
program.LineTo(size, size);
|
|
program.LineTo(0, size);
|
|
program.LineTo(0, 0);
|
|
return program;
|
|
}
|
|
|
|
private static LayoutPart Layout(PlateView view, Part part) =>
|
|
view.LayoutParts.Single(p => ReferenceEquals(p.BasePart, part));
|
|
|
|
private static void RunSta(System.Action action) =>
|
|
StaTestThread.Run(action, TimeSpan.FromSeconds(30), "Common-line selection test timed out.");
|
|
}
|