mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-14 17:11:57 -04:00
fix(engine): return detached clamp zones from plate stock
NestPlateStock is documented as immutable input, but ClampZones handed out its own zones, whose bounds are mutable. Editing one after the stock was built moved the stock's clamp while plates materialized from the stock (layout check, fill, posting) kept the original zone, so a part or cut on the moved clamp validated and posted clear. Return detached copies on every read, so the stock and its plates can never disagree. Copies keep the exact bits; engine results are unchanged. Project Memory: opennest 14f93b26-2e41-41f1-b09f-87dc895bb4da
This commit is contained in:
1 parent
df2f1b796d
commit
92d8964339
2 files changed
+46
-2
No files matched your search
@@ -79,6 +79,26 @@ public class ClampZoneSheetFrameTests
|
||||
Assert.False(report.CanPost(false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StockClampZonesCannotBeEditedThroughTheReturnedZones()
|
||||
{
|
||||
// Stock is immutable input: editing a returned zone must not move the stock's clamp
|
||||
// away from the zones its materialized plates (validation, fill, posting) use.
|
||||
var live = new Plate(24, 48);
|
||||
live.ClampZones.Add(new ClampZone("z", new Box(10, 0, 1.5, 0.625)));
|
||||
var stock = DrawingJobMapper.FromPlate("s", live, 1);
|
||||
|
||||
stock.ClampZones[0].Bounds.X += 10;
|
||||
foreach (var zone in stock.ClampZones)
|
||||
zone.Bounds.Y = 5;
|
||||
|
||||
var current = Assert.Single(stock.ClampZones);
|
||||
Assert.Equal(10, current.Bounds.X);
|
||||
Assert.Equal(0, current.Bounds.Y);
|
||||
var plate = DrawingJobMapper.CreatePlate(stock);
|
||||
Assert.Equal(stock.ClampZones.Select(Bits), ClampZoneFrame.ToPlacement(plate).Select(Bits));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
|
||||
@@ -60,14 +60,16 @@ public sealed class NestPlateStock
|
||||
zones[i] = new ClampZone(clampZones[i].Id, clampZones[i].Bounds.Translate(left, bottom));
|
||||
sheetZones[i] = new ClampZone(clampZones[i].Id, clampZones[i].Bounds);
|
||||
}
|
||||
ClampZones = zones;
|
||||
ClampZones = new DetachedZones(zones);
|
||||
SheetClampZones = sheetZones;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keep-out rectangles in the placement frame (quadrant applied), translated exactly
|
||||
/// like <see cref="WorkArea"/>. Empty when the plate defines none; never null.
|
||||
/// like <see cref="WorkArea"/>. Empty when the plate defines none; never null. Every
|
||||
/// read returns detached copies: zone bounds are mutable, and the stock must stay
|
||||
/// consistent with the zones its materialized plates use.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ClampZone> ClampZones { get; }
|
||||
|
||||
@@ -102,4 +104,26 @@ public sealed class NestPlateStock
|
||||
/// <summary>True when a width (X) by height (Y) envelope fits the work area within epsilon.</summary>
|
||||
public bool Fits(double width, double height, double epsilon = 1e-9) =>
|
||||
width <= workArea.Length + epsilon && height <= workArea.Width + epsilon;
|
||||
|
||||
private sealed class DetachedZones : IReadOnlyList<ClampZone>
|
||||
{
|
||||
private readonly ClampZone[] zones;
|
||||
|
||||
public DetachedZones(ClampZone[] zones) => this.zones = zones;
|
||||
|
||||
public int Count => zones.Length;
|
||||
|
||||
public ClampZone this[int index] => Copy(zones[index]);
|
||||
|
||||
public IEnumerator<ClampZone> GetEnumerator()
|
||||
{
|
||||
foreach (var zone in zones)
|
||||
yield return Copy(zone);
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
// The ClampZone constructor copies the rectangle, keeping its exact bits.
|
||||
private static ClampZone Copy(ClampZone zone) => new(zone.Id, zone.Bounds);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user