diff --git a/OpenNest.Posts.Cincinnati/CincinnatiFeatureWriter.cs b/OpenNest.Posts.Cincinnati/CincinnatiFeatureWriter.cs index ca5bbde..f2885cb 100644 --- a/OpenNest.Posts.Cincinnati/CincinnatiFeatureWriter.cs +++ b/OpenNest.Posts.Cincinnati/CincinnatiFeatureWriter.cs @@ -23,6 +23,12 @@ public sealed class FeatureContext public string LibraryFile { get; set; } = ""; public double CutDistance { get; set; } public double SheetDiagonal { get; set; } + + /// + /// Part location on the plate. Added to all output X/Y coordinates + /// so part-relative programs become plate-absolute under G90. + /// + public Vector PartLocation { get; set; } = Vector.Zero; } /// @@ -51,12 +57,13 @@ public sealed class CincinnatiFeatureWriter var currentPos = Vector.Zero; var lastFeedVar = ""; var kerfEmitted = false; + var offset = ctx.PartLocation; // Find the pierce point from the first rapid move var piercePoint = FindPiercePoint(ctx.Codes); // 1. Rapid to pierce point (with line number if configured) - WriteRapidToPierce(writer, ctx.FeatureNumber, piercePoint); + WriteRapidToPierce(writer, ctx.FeatureNumber, piercePoint, offset); // 2. Part name comment on first feature of each part if (ctx.IsFirstFeatureOfPart && !string.IsNullOrEmpty(ctx.PartName)) @@ -105,7 +112,7 @@ public sealed class CincinnatiFeatureWriter kerfEmitted = true; } - sb.Append($"G1 X{_fmt.FormatCoord(linear.EndPoint.X)} Y{_fmt.FormatCoord(linear.EndPoint.Y)}"); + sb.Append($"G1 X{_fmt.FormatCoord(linear.EndPoint.X + offset.X)} Y{_fmt.FormatCoord(linear.EndPoint.Y + offset.Y)}"); // Feedrate — etch always uses process feedrate var feedVar = ctx.IsEtch ? "#148" : GetLinearFeedVariable(linear.Layer); @@ -131,7 +138,7 @@ public sealed class CincinnatiFeatureWriter // G2 = CW, G3 = CCW var gCode = arc.Rotation == RotationType.CW ? "G2" : "G3"; - sb.Append($"{gCode} X{_fmt.FormatCoord(arc.EndPoint.X)} Y{_fmt.FormatCoord(arc.EndPoint.Y)}"); + sb.Append($"{gCode} X{_fmt.FormatCoord(arc.EndPoint.X + offset.X)} Y{_fmt.FormatCoord(arc.EndPoint.Y + offset.Y)}"); // Convert absolute center to incremental I/J var i = arc.CenterPoint.X - currentPos.X; @@ -188,14 +195,14 @@ public sealed class CincinnatiFeatureWriter return Vector.Zero; } - private void WriteRapidToPierce(TextWriter writer, int featureNumber, Vector piercePoint) + private void WriteRapidToPierce(TextWriter writer, int featureNumber, Vector piercePoint, Vector offset) { var sb = new StringBuilder(); if (_config.UseLineNumbers) sb.Append($"N{featureNumber} "); - sb.Append($"G0 X{_fmt.FormatCoord(piercePoint.X)} Y{_fmt.FormatCoord(piercePoint.Y)}"); + sb.Append($"G0 X{_fmt.FormatCoord(piercePoint.X + offset.X)} Y{_fmt.FormatCoord(piercePoint.Y + offset.Y)}"); writer.WriteLine(sb.ToString()); } diff --git a/OpenNest.Posts.Cincinnati/CincinnatiSheetWriter.cs b/OpenNest.Posts.Cincinnati/CincinnatiSheetWriter.cs index 084f876..6a18ef2 100644 --- a/OpenNest.Posts.Cincinnati/CincinnatiSheetWriter.cs +++ b/OpenNest.Posts.Cincinnati/CincinnatiSheetWriter.cs @@ -153,7 +153,8 @@ public sealed class CincinnatiSheetWriter IsEtch = isEtch, LibraryFile = isEtch ? etchLibrary : cutLibrary, CutDistance = cutDistance, - SheetDiagonal = sheetDiagonal + SheetDiagonal = sheetDiagonal, + PartLocation = part.Location }; _featureWriter.Write(w, ctx); @@ -240,7 +241,8 @@ public sealed class CincinnatiSheetWriter IsEtch = isEtch, LibraryFile = isEtch ? etchLibrary : cutLibrary, CutDistance = cutDistance, - SheetDiagonal = sheetDiagonal + SheetDiagonal = sheetDiagonal, + PartLocation = part.Location }; _featureWriter.Write(w, ctx); diff --git a/OpenNest.Tests/Cincinnati/CincinnatiSheetWriterTests.cs b/OpenNest.Tests/Cincinnati/CincinnatiSheetWriterTests.cs index 3fdc8b0..d13279a 100644 --- a/OpenNest.Tests/Cincinnati/CincinnatiSheetWriterTests.cs +++ b/OpenNest.Tests/Cincinnati/CincinnatiSheetWriterTests.cs @@ -280,6 +280,67 @@ public class CincinnatiSheetWriterTests Assert.False(FeatureUtils.IsEtch(codes)); } + [Fact] + public void WriteSheet_InlineCoordinates_AreAbsoluteOnPlate() + { + var config = new CincinnatiPostConfig { PostedAccuracy = 4 }; + // Part program is at origin: (0,0) to (2,0) to (2,2) to (0,2) to (0,0) + var pgm = new Program(); + pgm.Codes.Add(new RapidMove(0, 0)); + pgm.Codes.Add(new LinearMove(2, 0)); + pgm.Codes.Add(new LinearMove(2, 2)); + pgm.Codes.Add(new LinearMove(0, 2)); + pgm.Codes.Add(new LinearMove(0, 0)); + + var plate = new Plate(48.0, 96.0); + // Place part at (10.5, 5.25) on the plate to produce non-integer coordinates + plate.Parts.Add(new Part(new Drawing("Square", pgm), new Vector(10.5, 5.25))); + + var sb = new StringBuilder(); + using var sw = new StringWriter(sb); + var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager()); + + sheetWriter.Write(sw, plate, "TestNest", 1, 101, "", ""); + + var output = sb.ToString(); + // Under G90, coordinates must be plate-absolute (part coords + part location) + Assert.Contains("G0 X10.5 Y5.25", output); // rapid to pierce + Assert.Contains("G1 X12.5 Y5.25", output); // (2,0) + (10.5,5.25) + Assert.Contains("G1 X12.5 Y7.25", output); // (2,2) + (10.5,5.25) + Assert.Contains("G1 X10.5 Y7.25", output); // (0,2) + (10.5,5.25) + Assert.Contains("G1 X10.5 Y5.25", output); // (0,0) + (10.5,5.25) + } + + [Fact] + public void WriteSheet_TwoPartsAtDifferentLocations_HaveDistinctAbsoluteCoords() + { + var config = new CincinnatiPostConfig { PostedAccuracy = 4 }; + var pgm = new Program(); + pgm.Codes.Add(new RapidMove(0, 0)); + pgm.Codes.Add(new LinearMove(1, 0)); + pgm.Codes.Add(new LinearMove(1, 1)); + pgm.Codes.Add(new LinearMove(0, 0)); + + var drawing = new Drawing("Tri", pgm); + var plate = new Plate(48.0, 96.0); + plate.Parts.Add(new Part(drawing, new Vector(5.5, 3.25))); + plate.Parts.Add(new Part(drawing, new Vector(20.5, 10.25))); + + var sb = new StringBuilder(); + using var sw = new StringWriter(sb); + var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager()); + + sheetWriter.Write(sw, plate, "TestNest", 1, 101, "", ""); + + var output = sb.ToString(); + // First part at (5.5, 3.25) + Assert.Contains("G0 X5.5 Y3.25", output); + Assert.Contains("G1 X6.5 Y3.25", output); + // Second part at (20.5, 10.25) + Assert.Contains("G0 X20.5 Y10.25", output); + Assert.Contains("G1 X21.5 Y10.25", output); + } + private static Program CreateSimpleProgram() { var pgm = new Program();