From 95b9613e2de04c13fe9893a546f42074fb2b23bf Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 2 Apr 2026 09:56:28 -0400 Subject: [PATCH] feat: add VariableRefs tracking on Motion and Feedrate Adds Dictionary VariableRefs to Motion (cleared on Rotate/Offset) and string VariableRef to Feedrate, with deep-copy Clone() support, so post processors can emit variable references instead of literal coordinate values. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/CNC/ArcMove.cs | 6 +- OpenNest.Core/CNC/Feedrate.cs | 4 +- OpenNest.Core/CNC/LinearMove.cs | 6 +- OpenNest.Core/CNC/Motion.cs | 9 +- OpenNest.Core/CNC/RapidMove.cs | 6 +- OpenNest.Tests/CNC/ProgramVariableTests.cs | 104 +++++++++++++++++++++ 6 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 OpenNest.Tests/CNC/ProgramVariableTests.cs diff --git a/OpenNest.Core/CNC/ArcMove.cs b/OpenNest.Core/CNC/ArcMove.cs index 2b6c9eb..dd369c3 100644 --- a/OpenNest.Core/CNC/ArcMove.cs +++ b/OpenNest.Core/CNC/ArcMove.cs @@ -1,4 +1,5 @@ -using OpenNest.Geometry; +using System.Collections.Generic; +using OpenNest.Geometry; namespace OpenNest.CNC { @@ -66,7 +67,8 @@ namespace OpenNest.CNC return new ArcMove(EndPoint, CenterPoint, Rotation) { Layer = Layer, - Suppressed = Suppressed + Suppressed = Suppressed, + VariableRefs = VariableRefs != null ? new Dictionary(VariableRefs) : null }; } diff --git a/OpenNest.Core/CNC/Feedrate.cs b/OpenNest.Core/CNC/Feedrate.cs index 03e177c..aee5956 100644 --- a/OpenNest.Core/CNC/Feedrate.cs +++ b/OpenNest.Core/CNC/Feedrate.cs @@ -17,6 +17,8 @@ public double Value { get; set; } + public string VariableRef { get; set; } + public CodeType Type { get { return CodeType.SetFeedrate; } @@ -24,7 +26,7 @@ public ICode Clone() { - return new Feedrate(Value); + return new Feedrate(Value) { VariableRef = VariableRef }; } public override string ToString() diff --git a/OpenNest.Core/CNC/LinearMove.cs b/OpenNest.Core/CNC/LinearMove.cs index a50e179..7b47721 100644 --- a/OpenNest.Core/CNC/LinearMove.cs +++ b/OpenNest.Core/CNC/LinearMove.cs @@ -1,4 +1,5 @@ -using OpenNest.Geometry; +using System.Collections.Generic; +using OpenNest.Geometry; namespace OpenNest.CNC { @@ -32,7 +33,8 @@ namespace OpenNest.CNC return new LinearMove(EndPoint) { Layer = Layer, - Suppressed = Suppressed + Suppressed = Suppressed, + VariableRefs = VariableRefs != null ? new Dictionary(VariableRefs) : null }; } diff --git a/OpenNest.Core/CNC/Motion.cs b/OpenNest.Core/CNC/Motion.cs index b9b9618..f2c801a 100644 --- a/OpenNest.Core/CNC/Motion.cs +++ b/OpenNest.Core/CNC/Motion.cs @@ -1,4 +1,5 @@ -using OpenNest.Geometry; +using System.Collections.Generic; +using OpenNest.Geometry; namespace OpenNest.CNC { @@ -14,6 +15,8 @@ namespace OpenNest.CNC public bool Suppressed { get; set; } + public Dictionary VariableRefs { get; set; } + protected Motion() { Feedrate = CNC.Feedrate.UseDefault; @@ -22,21 +25,25 @@ namespace OpenNest.CNC public virtual void Rotate(double angle) { EndPoint = EndPoint.Rotate(angle); + VariableRefs = null; } public virtual void Rotate(double angle, Vector origin) { EndPoint = EndPoint.Rotate(angle, origin); + VariableRefs = null; } public virtual void Offset(double x, double y) { EndPoint = new Vector(EndPoint.X + x, EndPoint.Y + y); + VariableRefs = null; } public virtual void Offset(Vector voffset) { EndPoint += voffset; + VariableRefs = null; } public abstract CodeType Type { get; } diff --git a/OpenNest.Core/CNC/RapidMove.cs b/OpenNest.Core/CNC/RapidMove.cs index 3f8e718..0585630 100644 --- a/OpenNest.Core/CNC/RapidMove.cs +++ b/OpenNest.Core/CNC/RapidMove.cs @@ -1,4 +1,5 @@ -using OpenNest.Geometry; +using System.Collections.Generic; +using OpenNest.Geometry; namespace OpenNest.CNC { @@ -28,7 +29,8 @@ namespace OpenNest.CNC { return new RapidMove(EndPoint) { - Suppressed = Suppressed + Suppressed = Suppressed, + VariableRefs = VariableRefs != null ? new Dictionary(VariableRefs) : null }; } diff --git a/OpenNest.Tests/CNC/ProgramVariableTests.cs b/OpenNest.Tests/CNC/ProgramVariableTests.cs new file mode 100644 index 0000000..e5146dd --- /dev/null +++ b/OpenNest.Tests/CNC/ProgramVariableTests.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using OpenNest.CNC; +using OpenNest.Geometry; + +namespace OpenNest.Tests.CNC; + +public class ProgramVariableTests +{ + [Fact] + public void LinearMove_VariableRefs_NullByDefault() + { + var move = new LinearMove(1.0, 2.0); + Assert.Null(move.VariableRefs); + } + + [Fact] + public void LinearMove_Clone_CopiesVariableRefs() + { + var move = new LinearMove(1.0, 2.0); + move.VariableRefs = new Dictionary { { "X", "width" } }; + var clone = (LinearMove)move.Clone(); + Assert.NotSame(move.VariableRefs, clone.VariableRefs); + Assert.Equal("width", clone.VariableRefs["X"]); + } + + [Fact] + public void LinearMove_Clone_NullRefs_StaysNull() + { + var move = new LinearMove(1.0, 2.0); + var clone = (LinearMove)move.Clone(); + Assert.Null(clone.VariableRefs); + } + + [Fact] + public void ArcMove_Clone_CopiesVariableRefs() + { + var move = new ArcMove(1, 0, 0.5, 0); + move.VariableRefs = new Dictionary { { "I", "radius" } }; + var clone = (ArcMove)move.Clone(); + Assert.NotSame(move.VariableRefs, clone.VariableRefs); + Assert.Equal("radius", clone.VariableRefs["I"]); + } + + [Fact] + public void RapidMove_Clone_CopiesVariableRefs() + { + var move = new RapidMove(5.0, 0); + move.VariableRefs = new Dictionary { { "X", "start_x" } }; + var clone = (RapidMove)move.Clone(); + Assert.NotSame(move.VariableRefs, clone.VariableRefs); + Assert.Equal("start_x", clone.VariableRefs["X"]); + } + + [Fact] + public void Feedrate_VariableRef_NullByDefault() + { + var f = new Feedrate(100.0); + Assert.Null(f.VariableRef); + } + + [Fact] + public void Feedrate_Clone_CopiesVariableRef() + { + var f = new Feedrate(100.0) { VariableRef = "cut_speed" }; + var clone = (Feedrate)f.Clone(); + Assert.Equal("cut_speed", clone.VariableRef); + } + + [Fact] + public void Motion_Rotate_ClearsVariableRefs() + { + var move = new LinearMove(1.0, 0); + move.VariableRefs = new Dictionary { { "X", "width" } }; + move.Rotate(System.Math.PI / 2); + Assert.Null(move.VariableRefs); + } + + [Fact] + public void Motion_Offset_ClearsVariableRefs() + { + var move = new LinearMove(1.0, 0); + move.VariableRefs = new Dictionary { { "X", "width" } }; + move.Offset(5.0, 0); + Assert.Null(move.VariableRefs); + } + + [Fact] + public void ArcMove_Rotate_ClearsVariableRefs() + { + var move = new ArcMove(1, 0, 0.5, 0); + move.VariableRefs = new Dictionary { { "I", "radius" } }; + move.Rotate(System.Math.PI / 2); + Assert.Null(move.VariableRefs); + } + + [Fact] + public void ArcMove_Offset_ClearsVariableRefs() + { + var move = new ArcMove(1, 0, 0.5, 0); + move.VariableRefs = new Dictionary { { "I", "radius" } }; + move.Offset(5.0, 0); + Assert.Null(move.VariableRefs); + } +}