mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-15 12:25:34 -05:00
PlateEdits ports the WinForms plate view's edits of the selection, with its undo labels and coalescing: Delete (parts and cut-offs, cut-offs regenerated once when the plate has any), Nudge by the offset increment or one repeat step, Push to contact, Rotate about the selection's centre keeping its lower-left corner (a quarter turn, or by wheel notches at 10 degrees or a tenth of that), repeated nudges and wheel turns undoing as one step. The Select tool drags the selection as one Move step. Cut-offs regenerate when a drag actually moved parts (WinForms also does it on a plain click, adding an extra undo step); the regeneration runs before the step completes, so a failure rolls the drag back, and a view that loses the pointer mid-drag ends the move as a release would, keeping the selection. A plate tool host now has to supply its undo wiring, the nest's cut-off settings and an edits lock (WinForms EditsLocked): while locked no edit runs and no press selects or starts a move.
145 lines
5.2 KiB
C#
145 lines
5.2 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.CommonLines;
|
|
using OpenNest.CutOffs;
|
|
using OpenNest.Desktop.Interaction;
|
|
using OpenNest.Desktop.Rendering;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Undo;
|
|
|
|
namespace OpenNest.Desktop.Tests.Interaction;
|
|
|
|
/// <summary>A plate view without a toolkit: the tools' host in tests.</summary>
|
|
internal sealed class TestToolHost : IPlateToolHost
|
|
{
|
|
public TestToolHost(Plate plate)
|
|
{
|
|
Plate = plate;
|
|
Selection.Plate = plate;
|
|
// About ten pixels per unit, so a cut-off is picked within half a unit.
|
|
View.Resize(1100, 1100);
|
|
View.ZoomToArea(new Box(0, 0, 100, 100));
|
|
Tools = new PlateToolManager(this);
|
|
}
|
|
|
|
public Plate Plate { get; set; }
|
|
|
|
public PlateSelection Selection { get; } = new();
|
|
|
|
public PlateHitTester HitTester { get; } = new();
|
|
|
|
public ViewTransform View { get; } = new();
|
|
|
|
public PlateToolManager Tools { get; }
|
|
|
|
public int Invalidations { get; private set; }
|
|
|
|
public void Invalidate() => Invalidations++;
|
|
|
|
/// <summary>The history edits record into, or null to record nothing.</summary>
|
|
public NestUndoRecorder Recorder { get; set; }
|
|
|
|
/// <summary>The label of every change begun, in order.</summary>
|
|
public List<string> ChangeLabels { get; } = new();
|
|
|
|
private CutOffSettings cutOffSettings = new();
|
|
|
|
public CutOffSettings CutOffSettings
|
|
{
|
|
get => ThrowOnCutOffSettings
|
|
? throw new InvalidOperationException("cut-off settings unavailable")
|
|
: cutOffSettings;
|
|
set => cutOffSettings = value;
|
|
}
|
|
|
|
/// <summary>Makes reading <see cref="CutOffSettings"/> throw, as a failed rebuild.</summary>
|
|
public bool ThrowOnCutOffSettings { get; set; }
|
|
|
|
public bool EditsLocked { get; set; }
|
|
|
|
public ChangeScope BeginChange(string label, object coalesceKey = null)
|
|
{
|
|
ChangeLabels.Add(label);
|
|
return Recorder?.BeginChange(label, coalesceKey) ?? ChangeScope.Detached();
|
|
}
|
|
|
|
/// <summary>Puts the plate in a nest with a fresh history that the edits record into.</summary>
|
|
public NestUndoRecorder RecordHistory()
|
|
{
|
|
var nest = new Nest { Name = "test" };
|
|
foreach (var drawing in Plate.Parts.Select(p => p.BaseDrawing).Distinct())
|
|
{
|
|
if (!drawing.IsCutOff)
|
|
nest.Drawings.Add(drawing);
|
|
}
|
|
nest.Plates.Add(Plate);
|
|
Recorder = new NestUndoRecorder(new Document { Nest = nest });
|
|
Recorder.Reset();
|
|
return Recorder;
|
|
}
|
|
|
|
public ToolPointer At(double x, double y, ToolButton button = ToolButton.Left,
|
|
ToolModifiers modifiers = ToolModifiers.None) =>
|
|
new(new Vector(x, y), View.WorldToScreen(x, y), button, modifiers);
|
|
|
|
public void Click(double x, double y, ToolModifiers modifiers = ToolModifiers.None)
|
|
{
|
|
Tools.Current.PointerPressed(At(x, y, modifiers: modifiers));
|
|
Tools.Current.PointerReleased(At(x, y, modifiers: modifiers));
|
|
}
|
|
|
|
public void Drag(double x1, double y1, double x2, double y2)
|
|
{
|
|
Tools.Current.PointerPressed(At(x1, y1));
|
|
Tools.Current.PointerMoved(At((x1 + x2) / 2, (y1 + y2) / 2));
|
|
Tools.Current.PointerMoved(At(x2, y2));
|
|
Tools.Current.PointerReleased(At(x2, y2));
|
|
}
|
|
}
|
|
|
|
internal static class InteractionFixtures
|
|
{
|
|
public static Program Rect(double width, double height, double x = 0, double y = 0)
|
|
{
|
|
var program = new Program();
|
|
program.Codes.Add(new RapidMove(new Vector(x, y)));
|
|
program.Codes.Add(new LinearMove(new Vector(x + width, y)));
|
|
program.Codes.Add(new LinearMove(new Vector(x + width, y + height)));
|
|
program.Codes.Add(new LinearMove(new Vector(x, y + height)));
|
|
program.Codes.Add(new LinearMove(new Vector(x, y)));
|
|
return program;
|
|
}
|
|
|
|
/// <summary>A 10x10 square with a 3-unit-radius round hole at its centre.</summary>
|
|
public static Drawing Holed()
|
|
{
|
|
var program = new Program();
|
|
program.Codes.Add(new RapidMove(new Vector(8, 5)));
|
|
program.Codes.Add(new ArcMove(new Vector(8, 5), new Vector(5, 5), RotationType.CW));
|
|
foreach (var code in Rect(10, 10).Codes)
|
|
program.Codes.Add(code);
|
|
return new Drawing("holed", program);
|
|
}
|
|
|
|
/// <summary>
|
|
/// A 100x100 plate: a holed square at (0,0), a square at (20,0), two squares one kerf apart in a
|
|
/// common line at (40,0) and (50.05,0), a square at (20,20) on top of nothing, and a vertical
|
|
/// cut-off at x = 80.
|
|
/// </summary>
|
|
public static Plate Plate()
|
|
{
|
|
var square = new Drawing("square", Rect(10, 10));
|
|
var plate = new Plate(new Size(100, 100));
|
|
plate.Parts.Add(new Part(Holed(), new Vector(0, 0)));
|
|
plate.Parts.Add(new Part(square, new Vector(20, 0)));
|
|
var a = new Part(square, new Vector(40, 0));
|
|
var b = new Part(square, new Vector(50.05, 0));
|
|
plate.Parts.Add(a);
|
|
plate.Parts.Add(b);
|
|
plate.Parts.Add(new Part(square, new Vector(20, 20)));
|
|
plate.CommonLines.Add(new CommonLineGroup(new[] { a, b }, 0.05));
|
|
plate.CutOffs.Add(new CutOff(new Vector(80, 0), CutOffAxis.Vertical));
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
return plate;
|
|
}
|
|
}
|