feat: add PlateManager with navigation state and disposal
Introduces PlateChangedEventArgs and PlateManager in OpenNest.Core to centralize plate navigation logic (CurrentIndex, LoadFirst/Last/Next/Previous/At, IsFirst/IsLast). Includes full xUnit test coverage (17 tests) verifying navigation, event firing, and disposal unsubscription. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
|||||||
|
using OpenNest.Collections;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlateAdded(object sender, ItemAddedEventArgs<Plate> e)
|
||||||
|
{
|
||||||
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlateRemoved(object sender, ItemRemovedEventArgs<Plate> e)
|
||||||
|
{
|
||||||
|
if (CurrentIndex >= Count && Count > 0)
|
||||||
|
CurrentIndex = Count - 1;
|
||||||
|
|
||||||
|
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FireCurrentPlateChanged()
|
||||||
|
{
|
||||||
|
CurrentPlateChanged?.Invoke(this, new PlateChangedEventArgs(CurrentPlate, CurrentIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
disposed = true;
|
||||||
|
nest.Plates.ItemAdded -= OnPlateAdded;
|
||||||
|
nest.Plates.ItemRemoved -= OnPlateRemoved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
using OpenNest.CNC;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests;
|
||||||
|
|
||||||
|
public class PlateManagerTests
|
||||||
|
{
|
||||||
|
private static Nest CreateNest()
|
||||||
|
{
|
||||||
|
var nest = new Nest("test");
|
||||||
|
return nest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Part MakePart()
|
||||||
|
{
|
||||||
|
var pgm = new Program();
|
||||||
|
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(10, 0)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||||
|
var drawing = new Drawing("test", pgm);
|
||||||
|
return new Part(drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_EmptyNest_CurrentIndexZero()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_NestWithPlates_CurrentIndexZero()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CurrentPlate_ReturnsPlateAtCurrentIndex()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
var plate = nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.Same(plate, mgr.CurrentPlate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CurrentPlate_EmptyNest_ReturnsNull()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.Null(mgr.CurrentPlate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Count_DelegatesToNestPlates()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.Equal(2, mgr.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadFirst_SetsCurrentIndexToZero()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
mgr.LoadLast();
|
||||||
|
mgr.LoadFirst();
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadLast_SetsCurrentIndexToLastPlate()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
mgr.LoadLast();
|
||||||
|
Assert.Equal(2, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadNext_AdvancesIndex()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
var result = mgr.LoadNext();
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(1, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadNext_AtEnd_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
var result = mgr.LoadNext();
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadPrevious_DecrementsIndex()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
mgr.LoadLast();
|
||||||
|
var result = mgr.LoadPrevious();
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadPrevious_AtStart_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
var result = mgr.LoadPrevious();
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(0, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadAt_SetsExactIndex()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
mgr.LoadAt(2);
|
||||||
|
Assert.Equal(2, mgr.CurrentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsFirst_WhenAtStart_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.True(mgr.IsFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsLast_WhenAtEnd_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
Assert.True(mgr.IsLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsFirst_WhenNotAtStart_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
mgr.LoadLast();
|
||||||
|
Assert.False(mgr.IsFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Navigation_FiresCurrentPlateChanged()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
nest.CreatePlate();
|
||||||
|
nest.CreatePlate();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
PlateChangedEventArgs received = null;
|
||||||
|
mgr.CurrentPlateChanged += (s, e) => received = e;
|
||||||
|
mgr.LoadNext();
|
||||||
|
Assert.NotNull(received);
|
||||||
|
Assert.Equal(1, received.Index);
|
||||||
|
Assert.Same(nest.Plates[1], received.Plate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Dispose_UnsubscribesFromPlateEvents()
|
||||||
|
{
|
||||||
|
var nest = CreateNest();
|
||||||
|
using var mgr = new PlateManager(nest);
|
||||||
|
var eventFired = false;
|
||||||
|
mgr.PlateListChanged += (s, e) => eventFired = true;
|
||||||
|
mgr.Dispose();
|
||||||
|
nest.CreatePlate();
|
||||||
|
Assert.False(eventFired);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user