fix(cnc): keep hole sub-programs private to each program copy
Program.Clone deep-copied the SubPrograms dictionary but left every SubProgramCall pointing at the source's sub-program, and SubProgramCall.Clone went through the Rotation setter, which re-rotated that shared program to the call's stale angle. Copying a program with hole lead-ins therefore rotated the source's holes, and rotating the copy rotated the source again. Program.Rotate also rotated a shared sub-program once per call, so two identical holes (one deduplicated sub-program) turned twice. Clone now binds calls to one private copy per shared sub-program without re-aligning it, and Rotate turns each distinct sub-program once.
This commit is contained in:
@@ -90,6 +90,9 @@ namespace OpenNest.CNC
|
||||
|
||||
SetModeAbs();
|
||||
|
||||
// Several calls can share one sub-program (identical holes); rotate each once.
|
||||
var rotatedSubPrograms = new HashSet<Program>(ReferenceEqualityComparer.Instance);
|
||||
|
||||
for (int i = 0; i < Codes.Count; ++i)
|
||||
{
|
||||
var code = Codes[i];
|
||||
@@ -110,7 +113,7 @@ namespace OpenNest.CNC
|
||||
);
|
||||
}
|
||||
|
||||
if (subpgm.Program != null)
|
||||
if (subpgm.Program != null && rotatedSubPrograms.Add(subpgm.Program))
|
||||
subpgm.Program.Rotate(angle, origin);
|
||||
}
|
||||
|
||||
@@ -519,8 +522,31 @@ namespace OpenNest.CNC
|
||||
foreach (var kvp in Variables)
|
||||
pgm.Variables[kvp.Key] = kvp.Value;
|
||||
|
||||
// The copy owns its sub-programs: rotating it must never turn the source's holes.
|
||||
// Calls that shared one sub-program keep sharing one copy.
|
||||
Dictionary<Program, Program> subCopies = null;
|
||||
|
||||
Program CopyOf(Program sub)
|
||||
{
|
||||
subCopies ??= new Dictionary<Program, Program>(ReferenceEqualityComparer.Instance);
|
||||
|
||||
if (!subCopies.TryGetValue(sub, out var copy))
|
||||
{
|
||||
copy = (Program)sub.Clone();
|
||||
subCopies[sub] = copy;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
foreach (var kvp in SubPrograms)
|
||||
pgm.SubPrograms[kvp.Key] = (Program)kvp.Value.Clone();
|
||||
pgm.SubPrograms[kvp.Key] = CopyOf(kvp.Value);
|
||||
|
||||
foreach (var code in codes)
|
||||
{
|
||||
if (code is SubProgramCall call && call.Program != null)
|
||||
call.BindProgram(CopyOf(call.Program));
|
||||
}
|
||||
|
||||
return pgm;
|
||||
}
|
||||
|
||||
@@ -79,12 +79,28 @@ namespace OpenNest.CNC
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a shallow copy.
|
||||
/// Gets a shallow copy that references the same program. Copies the fields
|
||||
/// directly: going through the setters would re-align (rotate) the shared program.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ICode Clone()
|
||||
{
|
||||
return new SubProgramCall(program, Rotation) { Id = Id, Offset = Offset };
|
||||
return new SubProgramCall
|
||||
{
|
||||
program = program,
|
||||
rotation = rotation,
|
||||
Id = Id,
|
||||
Offset = Offset,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Points the call at <paramref name="copy"/>, a copy of its current program, without
|
||||
/// re-aligning its rotation: the copy already has the geometry the call executes.
|
||||
/// </summary>
|
||||
internal void BindProgram(Program copy)
|
||||
{
|
||||
program = copy;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
Reference in New Issue
Block a user