From c552372f81d0d84d23b71e79577b99f099fc651f Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 19 Mar 2026 21:10:20 -0400 Subject: [PATCH] fix(core): copy-on-write for shared Program in tiled parts CloneAtOffset shares the Program instance for tiling performance, but rotating a part on the plate mutated the shared Program, causing all parts from the same tile template to rotate together. Added ownsProgram flag with EnsureOwnedProgram() that clones the Program before first mutation, preserving tiling performance while making user rotations independent. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Part.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/OpenNest.Core/Part.cs b/OpenNest.Core/Part.cs index e0c3b0d..b47793c 100644 --- a/OpenNest.Core/Part.cs +++ b/OpenNest.Core/Part.cs @@ -20,6 +20,7 @@ namespace OpenNest public class Part : IPart, IBoundable { private Vector location; + private bool ownsProgram; public readonly Drawing BaseDrawing; @@ -32,6 +33,7 @@ namespace OpenNest { BaseDrawing = baseDrawing; Program = baseDrawing.Program.Clone() as Program; + ownsProgram = true; this.location = location; UpdateBounds(); } @@ -67,6 +69,7 @@ namespace OpenNest /// Angle of rotation in radians. public void Rotate(double angle) { + EnsureOwnedProgram(); Program.Rotate(angle); location = Location.Rotate(angle); UpdateBounds(); @@ -79,6 +82,7 @@ namespace OpenNest /// The origin to rotate the part around. public void Rotate(double angle, Vector origin) { + EnsureOwnedProgram(); Program.Rotate(angle); location = Location.Rotate(angle, origin); UpdateBounds(); @@ -222,6 +226,15 @@ namespace OpenNest return part; } + private void EnsureOwnedProgram() + { + if (!ownsProgram) + { + Program = Program.Clone() as Program; + ownsProgram = true; + } + } + private Part(Drawing baseDrawing, Program program, Vector location, Box boundingBox) { BaseDrawing = baseDrawing;