From 07cba5719f32ec6e01851d5e7ecc20bd1409e667 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 13 Oct 2026 17:58:51 -0400 Subject: [PATCH] fix(io): write an empty sheet whose rail differs only in removal time The writer's sentinel rule compared zones only, so an empty sheet following a rail that differs from the default only in clamp removal minutes was dropped on save although ChangePlateDefaults treats it as configured stock. The rule now also requires the sheet to follow the default rail itself. PM 1469b4e0-dc59-416b-8118-560b726aa1e1 --- OpenNest.IO.Tests/ClampRailPersistenceTests.cs | 16 ++++++++++++++++ OpenNest.IO/NestWriter.cs | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/OpenNest.IO.Tests/ClampRailPersistenceTests.cs b/OpenNest.IO.Tests/ClampRailPersistenceTests.cs index 042e93a4..3c2c88bc 100644 --- a/OpenNest.IO.Tests/ClampRailPersistenceTests.cs +++ b/OpenNest.IO.Tests/ClampRailPersistenceTests.cs @@ -470,6 +470,22 @@ public class ClampRailPersistenceTests Assert.Equal(edge, kept.EdgeSpacing.Left); } + // Delta review: a sheet whose rail differs from the defaults only in removal time is + // configured stock too (ChangePlateDefaults leaves it alone) and must be written. + [Fact] + public void EmptySheetOnARailWithOtherRemovalTimeIsKept() + { + var nest = new Nest("railed"); + nest.PlateDefaults.Size = new Size(48, 96); + nest.SetClampRail(Machine()); + var plate = nest.CreatePlate(); + plate.SetClampRail(new ClampRail(1.5, 0.625, MachineCenters, removalMinutes: 7)); + + var kept = Assert.Single(RoundTrip(nest).Plates); + + Assert.Equal(7, kept.ClampRail.RemovalMinutes); + } + /// Removes a named property wherever it appears in the document, simulating a /// key an older writer never emitted. Rebuilds via so the result /// is always valid JSON regardless of whether the property was first, last or only one diff --git a/OpenNest.IO/NestWriter.cs b/OpenNest.IO/NestWriter.cs index 6a07c9a1..fa965421 100644 --- a/OpenNest.IO/NestWriter.cs +++ b/OpenNest.IO/NestWriter.cs @@ -201,7 +201,8 @@ namespace OpenNest.IO /// /// True when a plate created from the saved defaults would be this sheet's clamp /// configuration: same size, quadrant, edge and part spacing, no last-resort - /// removal, and zones the saved rail recreates (C17: anything else is configured + /// removal, not following another rail (removal time included), and zones the + /// saved rail recreates (C17: anything else is configured /// stock and is written). /// private bool DefaultsRecreateClamps(Plate plate) @@ -217,6 +218,7 @@ namespace OpenNest.IO && plate.EdgeSpacing.Bottom == defaults.EdgeSpacing.Bottom && plate.EdgeSpacing.Right == defaults.EdgeSpacing.Right && plate.EdgeSpacing.Top == defaults.EdgeSpacing.Top + && (plate.ClampRail == null || Equals(plate.ClampRail, rail)) && rail.Matches(plate); }