fix: offset inline feature coordinates by part location for G90 absolute mode
Part.Program stores coordinates relative to the part's own origin, but the Cincinnati post processor emits G90 (absolute positioning). Inline features were writing part-relative coordinates directly without adding Part.Location, producing incorrect output. Sub-program mode was unaffected because it uses G92 to set up local coordinate systems. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,12 @@ public sealed class FeatureContext
|
||||
public string LibraryFile { get; set; } = "";
|
||||
public double CutDistance { get; set; }
|
||||
public double SheetDiagonal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Part location on the plate. Added to all output X/Y coordinates
|
||||
/// so part-relative programs become plate-absolute under G90.
|
||||
/// </summary>
|
||||
public Vector PartLocation { get; set; } = Vector.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user