fix(plate-view): recompute part labels after a drawing edit

A part's sequence-number label point was computed once from the drawing's
program and cached forever. Editing a drawing in the converter replaces
that program, sometimes in a different frame, so the labels of every
placed copy stayed at the old frame's point and drifted off the parts.
Keep the cached point only while the drawing program (and its rotation)
is the one it came from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-11-10 07:48:35 -05:00
1 parent b329dd6c6b
commit bffcd52db1
2 files changed
+50 -2

No files matched your search

@@ -57,6 +57,42 @@ public class LayoutPartViewCacheTests
AssertMatchesFreshBuild(layout, view);
}, TimeSpan.FromMinutes(1), "Layout part rebuild test timed out.");
[Fact]
public void LabelFollowsAReplacedDrawingProgram() => StaTestThread.Run(() =>
{
// Editing a drawing replaces its program, possibly in another frame; the part's sequence
// label must be found again in the new frame, not kept from the old program.
var drawing = new Drawing("Door stop", LShape(Vector.Zero));
var part = new Part(drawing, new Vector(5, 7));
using var view = new PlateView(ColorSchemeRegistry.Get("Workshop"));
view.Matrix = new Matrix(2, 0, 0, -2, 0, 0);
var layout = LayoutPart.Create(part, view);
var before = layout.LabelScreenPoint;
drawing.Program = LShape(new Vector(-10, -5));
layout.IsDirty = true;
layout.UpdateView(view);
var fresh = LayoutPart.Create(part, view);
Assert.NotEqual(before, fresh.LabelScreenPoint);
Assert.Equal(fresh.LabelScreenPoint.X, layout.LabelScreenPoint.X, 2);
Assert.Equal(fresh.LabelScreenPoint.Y, layout.LabelScreenPoint.Y, 2);
fresh.ReleaseDisplayResources();
}, TimeSpan.FromMinutes(1), "Layout part label test timed out.");
private static Program LShape(Vector origin)
{
var program = new Program();
program.MoveTo(origin.X, origin.Y);
program.LineTo(origin.X + 10, origin.Y);
program.LineTo(origin.X + 10, origin.Y + 30);
program.LineTo(origin.X + 40, origin.Y + 30);
program.LineTo(origin.X + 40, origin.Y + 40);
program.LineTo(origin.X, origin.Y + 40);
program.LineTo(origin.X, origin.Y);
return program;
}
private static void AssertMatchesFreshBuild(LayoutPart layout, PlateView view)
{
var fresh = LayoutPart.Create(layout.BasePart, view);
+14 -2
View File
@@ -34,7 +34,11 @@ namespace OpenNest
private double _cachedOffsetTolerance;
private double _cachedOffsetRotation = double.NaN;
// The label point is in the frame of the drawing program it was computed from; editing a
// drawing replaces that program, so the cache is kept only while the program is the same.
private Vector? _labelPoint;
private CNC.Program _labelProgram;
private double _labelProgramRotation;
private PointF _labelScreenPoint;
internal PointF LabelScreenPoint => _labelScreenPoint;
@@ -456,8 +460,16 @@ namespace OpenNest
private Vector PlacedLabelPoint()
{
_labelPoint ??= ComputeLabelPoint();
var baseRotation = BasePart.BaseDrawing.Program.Rotation;
var drawingProgram = BasePart.BaseDrawing.Program;
var baseRotation = drawingProgram.Rotation;
if (_labelPoint == null
|| !ReferenceEquals(_labelProgram, drawingProgram)
|| _labelProgramRotation != baseRotation)
{
_labelPoint = ComputeLabelPoint();
_labelProgram = drawingProgram;
_labelProgramRotation = baseRotation;
}
return _labelPoint.Value.Rotate(BasePart.Rotation - baseRotation) + BasePart.Location;
}