fix(desktop): keep parts muted when Sequence by Path is restarted

Starting the tool while it was open built the new action before the old
one was disconnected, and the old one's teardown cleared the new muting.
The outgoing action now leaves the view alone once a successor owns it,
and opening the tool repaints the plate right away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-24 15:33:12 -04:00
1 parent 414479fc4e
commit cd1961e26b
2 files changed
+77 -5

No files matched your search

@@ -0,0 +1,60 @@
using OpenNest.Actions;
using OpenNest.CNC;
using OpenNest.Controls;
using OpenNest.Geometry;
namespace OpenNest.WinForms.Tests.Controls;
/// <summary>
/// Sequence by Path mutes every part until a line reaches it, however the tool is started.
/// </summary>
public class ActionSequenceByPathTests
{
private static PlateView View()
{
var drawing = new Drawing("square", Square());
var plate = new Plate(48, 48);
plate.Parts.Add(new Part(drawing, new Vector(2, 2)));
plate.Parts.Add(new Part(drawing, new Vector(20, 2)));
var view = new PlateView { Plate = plate, Size = new System.Drawing.Size(600, 600) };
view.ZoomToFit();
return view;
}
private static Program Square()
{
var program = new Program();
program.Codes.Add(new RapidMove(0, 0));
program.Codes.Add(new LinearMove(4, 0));
program.Codes.Add(new LinearMove(4, 4));
program.Codes.Add(new LinearMove(0, 4));
program.Codes.Add(new LinearMove(0, 0));
return program;
}
[Fact]
public void StartingTheToolMutesEveryPart() => StaTestThread.Run(() =>
{
using var view = View();
view.SetAction(typeof(ActionSequenceByPath));
Assert.All(view.Plate.Parts, part => Assert.Contains(part, view.MutedParts!));
view.SetAction(typeof(ActionSelect));
Assert.Null(view.MutedParts);
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
[Fact]
public void RestartingTheToolKeepsEveryPartMuted() => StaTestThread.Run(() =>
{
using var view = View();
view.SetAction(typeof(ActionSequenceByPath));
view.SetAction(typeof(ActionSequenceByPath));
Assert.NotNull(view.MutedParts);
Assert.All(view.Plate.Parts, part => Assert.Contains(part, view.MutedParts!));
view.SetAction(typeof(ActionSelect));
Assert.Null(view.MutedParts);
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
}
+17 -5
View File
@@ -38,6 +38,11 @@ namespace OpenNest.Actions
};
private List<Vector> drawing;
private bool disconnecting;
private IReadOnlySet<Part> muted;
// Starting the tool again builds the new one before this one is cancelled and
// disconnected, so this one must leave the view's muting to its successor.
private bool OwnsView => ReferenceEquals(plateView.MutedParts, muted);
public override bool ResumeOnEscape => false;
@@ -69,6 +74,7 @@ namespace OpenNest.Actions
plateView.KeyDown += plateView_KeyDown;
plateView.Paint += plateView_Paint;
UpdateMuted(lines);
plateView.Invalidate();
}
private void plateView_MouseDown(object sender, MouseEventArgs e)
@@ -182,13 +188,14 @@ namespace OpenNest.Actions
/// <summary>Mutes the parts the lines do not reach, so what is left to sequence stands out.</summary>
private void UpdateMuted(IReadOnlyList<IReadOnlyList<Vector>> paths)
{
var muted = new HashSet<Part>(ReferenceEqualityComparer.Instance);
var unreached = new HashSet<Part>(ReferenceEqualityComparer.Instance);
foreach (var part in plateView.Plate.Parts)
{
if (!DrawnPathSequencing.Reaches(paths, part))
muted.Add(part);
unreached.Add(part);
}
plateView.MutedParts = muted;
muted = unreached;
plateView.MutedParts = unreached;
}
private void plateView_Paint(object sender, PaintEventArgs e)
@@ -230,8 +237,11 @@ namespace OpenNest.Actions
plateView.MouseUp -= plateView_MouseUp;
plateView.KeyDown -= plateView_KeyDown;
plateView.Paint -= plateView_Paint;
plateView.PreviewSequence = null;
plateView.MutedParts = null;
if (OwnsView)
{
plateView.PreviewSequence = null;
plateView.MutedParts = null;
}
form.Close();
form.Dispose();
linePen.Dispose();
@@ -246,6 +256,8 @@ namespace OpenNest.Actions
public override void CancelAction()
{
drawing = null;
if (!OwnsView)
return;
plateView.PreviewSequence = null;
UpdateMuted(lines);
plateView.Invalidate();