mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-15 15:51:38 -04:00
Changing the nest defaults (Nest Info, Set as default, Edit Plate on an empty sheet) left the editor's trailing empty sheet on the old size and quadrant, so the writer's clamp-preserving empty-sheet rule saved it as a stale extra sheet. PlateManager.ChangePlateDefaults wraps a defaults edit: a trailing empty, cutoff-free sheet that exactly matched the old defaults (and followed their rail with no removed clamps or explicit zones) takes the new defaults; every other empty sheet keeps its configuration and is still written. PM cade6901-5f4a-4503-85bf-0ea681d9ca7f
294 lines
8.5 KiB
C#
294 lines
8.5 KiB
C#
using System;
|
|
using OpenNest.Collections;
|
|
|
|
namespace OpenNest
|
|
{
|
|
public class PlateChangedEventArgs : EventArgs
|
|
{
|
|
public Plate Plate { get; }
|
|
public int Index { get; }
|
|
|
|
public PlateChangedEventArgs(Plate plate, int index)
|
|
{
|
|
Plate = plate;
|
|
Index = index;
|
|
}
|
|
}
|
|
|
|
public class PlateManager : IDisposable
|
|
{
|
|
private readonly Nest nest;
|
|
private bool disposed;
|
|
private bool suppressNavigation;
|
|
private bool batching;
|
|
private Plate subscribedLast;
|
|
private Plate subscribedSecondToLast;
|
|
|
|
public event EventHandler<PlateChangedEventArgs> CurrentPlateChanged;
|
|
public event EventHandler PlateListChanged;
|
|
|
|
public PlateManager(Nest nest)
|
|
{
|
|
this.nest = nest;
|
|
nest.Plates.ItemAdded += OnPlateAdded;
|
|
nest.Plates.ItemRemoved += OnPlateRemoved;
|
|
}
|
|
|
|
public int CurrentIndex { get; private set; }
|
|
|
|
public Plate CurrentPlate => nest.Plates.Count > 0 ? nest.Plates[CurrentIndex] : null;
|
|
|
|
public int Count => nest.Plates.Count;
|
|
|
|
public bool IsFirst => Count == 0 || CurrentIndex <= 0;
|
|
|
|
public bool IsLast => CurrentIndex + 1 >= Count;
|
|
|
|
public bool CanRemoveCurrent =>
|
|
Count > 1 && CurrentPlate != null && CurrentPlate.Parts.Count > 0;
|
|
|
|
public void LoadFirst()
|
|
{
|
|
if (Count == 0)
|
|
return;
|
|
|
|
CurrentIndex = 0;
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
public void LoadLast()
|
|
{
|
|
if (Count == 0)
|
|
return;
|
|
|
|
CurrentIndex = Count - 1;
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
public bool LoadNext()
|
|
{
|
|
if (CurrentIndex + 1 >= Count)
|
|
return false;
|
|
|
|
CurrentIndex++;
|
|
FireCurrentPlateChanged();
|
|
return true;
|
|
}
|
|
|
|
public bool LoadPrevious()
|
|
{
|
|
if (Count == 0 || CurrentIndex - 1 < 0)
|
|
return false;
|
|
|
|
CurrentIndex--;
|
|
FireCurrentPlateChanged();
|
|
return true;
|
|
}
|
|
|
|
public void LoadAt(int index)
|
|
{
|
|
if (index < 0 || index >= Count)
|
|
return;
|
|
|
|
CurrentIndex = index;
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
public void EnsureSentinel()
|
|
{
|
|
suppressNavigation = true;
|
|
try
|
|
{
|
|
if (Count == 0 || nest.Plates[^1].Parts.Count > 0)
|
|
nest.CreatePlate();
|
|
|
|
while (
|
|
Count > 1
|
|
&& nest.Plates[^1].Parts.Count == 0
|
|
&& nest.Plates[^2].Parts.Count == 0
|
|
)
|
|
{
|
|
nest.Plates.RemoveAt(Count - 1);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
suppressNavigation = false;
|
|
}
|
|
|
|
SubscribeToTailPlates();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies a nest-defaults edit, moving only an untouched trailing empty sheet
|
|
/// that matched the old defaults to the new defaults.
|
|
/// </summary>
|
|
public void ChangePlateDefaults(Action changeDefaults)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(changeDefaults);
|
|
var defaults = nest.PlateDefaults;
|
|
var trailing = Count > 0 ? nest.Plates[^1] : null;
|
|
// Exact configuration equality, not geometric tolerance: even a small edit
|
|
// makes this configured stock rather than the untouched workspace sentinel.
|
|
var followsDefaults = trailing != null
|
|
&& trailing.Parts.Count == 0
|
|
&& trailing.CutOffs.Count == 0
|
|
&& trailing.Size.Width == defaults.Size.Width
|
|
&& trailing.Size.Length == defaults.Size.Length
|
|
&& trailing.Quadrant == defaults.Quadrant
|
|
&& trailing.EdgeSpacing.Left == defaults.EdgeSpacing.Left
|
|
&& trailing.EdgeSpacing.Bottom == defaults.EdgeSpacing.Bottom
|
|
&& trailing.EdgeSpacing.Right == defaults.EdgeSpacing.Right
|
|
&& trailing.EdgeSpacing.Top == defaults.EdgeSpacing.Top
|
|
&& trailing.PartSpacing == defaults.PartSpacing
|
|
&& trailing.RemovedClamps.Count == 0
|
|
&& Equals(trailing.ClampRail, defaults.ClampRail)
|
|
&& (defaults.ClampRail == null
|
|
? trailing.ClampZones.Count == 0
|
|
: defaults.ClampRail.Matches(trailing));
|
|
|
|
changeDefaults();
|
|
|
|
if (!followsDefaults)
|
|
return;
|
|
|
|
defaults = nest.PlateDefaults;
|
|
trailing.Size = defaults.Size;
|
|
trailing.Quadrant = defaults.Quadrant;
|
|
trailing.EdgeSpacing = defaults.EdgeSpacing;
|
|
trailing.PartSpacing = defaults.PartSpacing;
|
|
trailing.SetClampRail(defaults.ClampRail);
|
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
|
if (ReferenceEquals(CurrentPlate, trailing))
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
public void BeginBatch()
|
|
{
|
|
batching = true;
|
|
}
|
|
|
|
public void EndBatch()
|
|
{
|
|
batching = false;
|
|
EnsureSentinel();
|
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
public Plate GetOrCreateEmpty()
|
|
{
|
|
for (var i = Count - 1; i >= 0; i--)
|
|
{
|
|
if (nest.Plates[i].Parts.Count == 0)
|
|
return nest.Plates[i];
|
|
}
|
|
|
|
return nest.CreatePlate();
|
|
}
|
|
|
|
public void RemoveCurrent()
|
|
{
|
|
if (Count < 2)
|
|
return;
|
|
|
|
nest.Plates.RemoveAt(CurrentIndex);
|
|
}
|
|
|
|
private void SubscribeToTailPlates()
|
|
{
|
|
UnsubscribeFromTailPlates();
|
|
|
|
if (Count > 0)
|
|
{
|
|
subscribedLast = nest.Plates[^1];
|
|
subscribedLast.PartAdded += OnTailPartAdded;
|
|
subscribedLast.PartRemoved += OnTailPartRemoved;
|
|
}
|
|
|
|
if (Count > 1)
|
|
{
|
|
subscribedSecondToLast = nest.Plates[^2];
|
|
subscribedSecondToLast.PartAdded += OnTailPartAdded;
|
|
subscribedSecondToLast.PartRemoved += OnTailPartRemoved;
|
|
}
|
|
}
|
|
|
|
private void UnsubscribeFromTailPlates()
|
|
{
|
|
if (subscribedLast != null)
|
|
{
|
|
subscribedLast.PartAdded -= OnTailPartAdded;
|
|
subscribedLast.PartRemoved -= OnTailPartRemoved;
|
|
subscribedLast = null;
|
|
}
|
|
|
|
if (subscribedSecondToLast != null)
|
|
{
|
|
subscribedSecondToLast.PartAdded -= OnTailPartAdded;
|
|
subscribedSecondToLast.PartRemoved -= OnTailPartRemoved;
|
|
subscribedSecondToLast = null;
|
|
}
|
|
}
|
|
|
|
private void OnTailPartAdded(object sender, ItemAddedEventArgs<Part> e)
|
|
{
|
|
if (!batching)
|
|
EnsureSentinel();
|
|
}
|
|
|
|
private void OnTailPartRemoved(object sender, ItemRemovedEventArgs<Part> e)
|
|
{
|
|
if (!batching)
|
|
EnsureSentinel();
|
|
}
|
|
|
|
private void OnPlateAdded(object sender, ItemAddedEventArgs<Plate> e)
|
|
{
|
|
if (!suppressNavigation && !batching)
|
|
EnsureSentinel();
|
|
|
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
|
|
|
if (!suppressNavigation)
|
|
{
|
|
CurrentIndex = Count - 1;
|
|
FireCurrentPlateChanged();
|
|
}
|
|
}
|
|
|
|
private void OnPlateRemoved(object sender, ItemRemovedEventArgs<Plate> e)
|
|
{
|
|
if (CurrentIndex >= Count && Count > 0)
|
|
CurrentIndex = Count - 1;
|
|
|
|
if (!suppressNavigation && !batching)
|
|
EnsureSentinel();
|
|
|
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
|
|
|
if (!suppressNavigation)
|
|
FireCurrentPlateChanged();
|
|
}
|
|
|
|
private void FireCurrentPlateChanged()
|
|
{
|
|
CurrentPlateChanged?.Invoke(
|
|
this,
|
|
new PlateChangedEventArgs(CurrentPlate, CurrentIndex)
|
|
);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (disposed)
|
|
return;
|
|
|
|
disposed = true;
|
|
UnsubscribeFromTailPlates();
|
|
nest.Plates.ItemAdded -= OnPlateAdded;
|
|
nest.Plates.ItemRemoved -= OnPlateRemoved;
|
|
}
|
|
}
|
|
}
|