Compare commits
4 Commits
a6c2235647
...
24babe353e
| Author | SHA1 | Date | |
|---|---|---|---|
| 24babe353e | |||
| e63be93051 | |||
| ba3c3cbea3 | |||
| 572fa06a21 |
@@ -211,8 +211,5 @@ FakesAssemblies/
|
|||||||
.superpowers/
|
.superpowers/
|
||||||
docs/superpowers/
|
docs/superpowers/
|
||||||
|
|
||||||
# Documentation (manuals, templates, etc.)
|
|
||||||
docs/
|
|
||||||
|
|
||||||
# Launch settings
|
# Launch settings
|
||||||
**/Properties/launchSettings.json
|
**/Properties/launchSettings.json
|
||||||
|
|||||||
@@ -288,6 +288,10 @@ namespace OpenNest.CNC
|
|||||||
|
|
||||||
private Box BoundingBox(ref Vector pos)
|
private Box BoundingBox(ref Vector pos)
|
||||||
{
|
{
|
||||||
|
// Capture the frame origin at entry. Sub-program Offsets and
|
||||||
|
// absolute-mode endpoints are relative to this fixed origin.
|
||||||
|
var frameOrigin = pos;
|
||||||
|
|
||||||
double minX = 0.0;
|
double minX = 0.0;
|
||||||
double minY = 0.0;
|
double minY = 0.0;
|
||||||
double maxX = 0.0;
|
double maxX = 0.0;
|
||||||
@@ -303,7 +307,7 @@ namespace OpenNest.CNC
|
|||||||
{
|
{
|
||||||
var line = (LinearMove)code;
|
var line = (LinearMove)code;
|
||||||
var pt = Mode == Mode.Absolute ?
|
var pt = Mode == Mode.Absolute ?
|
||||||
line.EndPoint :
|
frameOrigin + line.EndPoint :
|
||||||
line.EndPoint + pos;
|
line.EndPoint + pos;
|
||||||
|
|
||||||
if (pt.X > maxX)
|
if (pt.X > maxX)
|
||||||
@@ -325,7 +329,7 @@ namespace OpenNest.CNC
|
|||||||
{
|
{
|
||||||
var line = (RapidMove)code;
|
var line = (RapidMove)code;
|
||||||
var pt = Mode == Mode.Absolute
|
var pt = Mode == Mode.Absolute
|
||||||
? line.EndPoint
|
? frameOrigin + line.EndPoint
|
||||||
: line.EndPoint + pos;
|
: line.EndPoint + pos;
|
||||||
|
|
||||||
if (pt.X > maxX)
|
if (pt.X > maxX)
|
||||||
@@ -358,8 +362,8 @@ namespace OpenNest.CNC
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
endpt = arc.EndPoint;
|
endpt = frameOrigin + arc.EndPoint;
|
||||||
centerpt = arc.CenterPoint;
|
centerpt = frameOrigin + arc.CenterPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
double minX1;
|
double minX1;
|
||||||
@@ -433,10 +437,13 @@ namespace OpenNest.CNC
|
|||||||
case CodeType.SubProgramCall:
|
case CodeType.SubProgramCall:
|
||||||
{
|
{
|
||||||
var subpgm = (SubProgramCall)code;
|
var subpgm = (SubProgramCall)code;
|
||||||
var subPos = subpgm.Offset.X != 0 || subpgm.Offset.Y != 0
|
if (subpgm.Program == null)
|
||||||
? new Vector(subpgm.Offset.X, subpgm.Offset.Y)
|
break;
|
||||||
: pos;
|
|
||||||
var box = subpgm.Program.BoundingBox(ref subPos);
|
// Sub-program frame origin in this program's frame
|
||||||
|
// is frameOrigin + Offset, regardless of current pos.
|
||||||
|
pos = frameOrigin + subpgm.Offset;
|
||||||
|
var box = subpgm.Program.BoundingBox(ref pos);
|
||||||
|
|
||||||
if (box.Left < minX)
|
if (box.Left < minX)
|
||||||
minX = box.Left;
|
minX = box.Left;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using OpenNest.Geometry;
|
using System.Text;
|
||||||
|
using OpenNest.Geometry;
|
||||||
using OpenNest.Math;
|
using OpenNest.Math;
|
||||||
|
|
||||||
namespace OpenNest.CNC
|
namespace OpenNest.CNC
|
||||||
@@ -90,9 +91,13 @@ namespace OpenNest.CNC
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append($"G65 P{Id}");
|
||||||
if (Offset.X != 0 || Offset.Y != 0)
|
if (Offset.X != 0 || Offset.Y != 0)
|
||||||
return string.Format("G65 P{0} X{1} Y{2}", Id, Offset.X, Offset.Y);
|
sb.Append($" X{Offset.X} Y{Offset.Y}");
|
||||||
return string.Format("G65 P{0} R{1}", Id, Rotation);
|
if (Rotation != 0)
|
||||||
|
sb.Append($" R{Rotation}");
|
||||||
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using OpenNest.CNC;
|
using OpenNest.CNC;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
namespace OpenNest.Converters
|
namespace OpenNest.Converters
|
||||||
@@ -9,7 +9,6 @@ namespace OpenNest.Converters
|
|||||||
/// Converts the program to absolute coordinates.
|
/// Converts the program to absolute coordinates.
|
||||||
/// Does NOT check program mode before converting.
|
/// Does NOT check program mode before converting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pgm"></param>
|
|
||||||
public static void ToAbsolute(Program pgm)
|
public static void ToAbsolute(Program pgm)
|
||||||
{
|
{
|
||||||
var pos = new Vector(0, 0);
|
var pos = new Vector(0, 0);
|
||||||
@@ -17,21 +16,27 @@ namespace OpenNest.Converters
|
|||||||
for (int i = 0; i < pgm.Codes.Count; ++i)
|
for (int i = 0; i < pgm.Codes.Count; ++i)
|
||||||
{
|
{
|
||||||
var code = pgm.Codes[i];
|
var code = pgm.Codes[i];
|
||||||
var motion = code as Motion;
|
|
||||||
|
|
||||||
if (motion != null)
|
if (code is SubProgramCall subCall && subCall.Program != null)
|
||||||
{
|
{
|
||||||
motion.Offset(pos);
|
// Sub-program is placed at Offset in this program's frame.
|
||||||
|
// After it runs, the tool is at Offset + (sub's end in its own frame).
|
||||||
|
pos = ComputeEndPosition(subCall.Program, subCall.Offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code is Motion motion)
|
||||||
|
{
|
||||||
|
motion.Offset(pos.X, pos.Y);
|
||||||
pos = motion.EndPoint;
|
pos = motion.EndPoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the program to intermental coordinates.
|
/// Converts the program to incremental coordinates.
|
||||||
/// Does NOT check program mode before converting.
|
/// Does NOT check program mode before converting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pgm"></param>
|
|
||||||
public static void ToIncremental(Program pgm)
|
public static void ToIncremental(Program pgm)
|
||||||
{
|
{
|
||||||
var pos = new Vector(0, 0);
|
var pos = new Vector(0, 0);
|
||||||
@@ -39,9 +44,16 @@ namespace OpenNest.Converters
|
|||||||
for (int i = 0; i < pgm.Codes.Count; ++i)
|
for (int i = 0; i < pgm.Codes.Count; ++i)
|
||||||
{
|
{
|
||||||
var code = pgm.Codes[i];
|
var code = pgm.Codes[i];
|
||||||
var motion = code as Motion;
|
|
||||||
|
|
||||||
if (motion != null)
|
if (code is SubProgramCall subCall && subCall.Program != null)
|
||||||
|
{
|
||||||
|
// Sub-program is placed at Offset in this program's frame,
|
||||||
|
// regardless of where the tool was before the call.
|
||||||
|
pos = ComputeEndPosition(subCall.Program, subCall.Offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code is Motion motion)
|
||||||
{
|
{
|
||||||
var pos2 = motion.EndPoint;
|
var pos2 = motion.EndPoint;
|
||||||
motion.Offset(-pos.X, -pos.Y);
|
motion.Offset(-pos.X, -pos.Y);
|
||||||
@@ -49,5 +61,37 @@ namespace OpenNest.Converters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the tool position after executing <paramref name="pgm"/>,
|
||||||
|
/// given that the program's frame origin is at <paramref name="startPos"/>
|
||||||
|
/// in the caller's frame. Walks nested sub-program calls recursively.
|
||||||
|
/// </summary>
|
||||||
|
private static Vector ComputeEndPosition(Program pgm, Vector startPos)
|
||||||
|
{
|
||||||
|
var pos = startPos;
|
||||||
|
|
||||||
|
for (int i = 0; i < pgm.Codes.Count; ++i)
|
||||||
|
{
|
||||||
|
var code = pgm.Codes[i];
|
||||||
|
|
||||||
|
if (code is SubProgramCall subCall && subCall.Program != null)
|
||||||
|
{
|
||||||
|
// Nested sub's frame origin in the caller's frame is startPos + Offset.
|
||||||
|
pos = ComputeEndPosition(subCall.Program, startPos + subCall.Offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code is Motion motion)
|
||||||
|
{
|
||||||
|
if (pgm.Mode == Mode.Incremental)
|
||||||
|
pos = pos + motion.EndPoint;
|
||||||
|
else
|
||||||
|
pos = startPos + motion.EndPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ namespace OpenNest.Converters
|
|||||||
|
|
||||||
private static void AddProgram(Program program, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
private static void AddProgram(Program program, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
||||||
{
|
{
|
||||||
|
// Capture the frame origin at entry. Sub-program Offsets are relative
|
||||||
|
// to this fixed origin, not to the current tool position.
|
||||||
|
var frameOrigin = curpos;
|
||||||
mode = program.Mode;
|
mode = program.Mode;
|
||||||
|
|
||||||
for (int i = 0; i < program.Length; ++i)
|
for (int i = 0; i < program.Length; ++i)
|
||||||
@@ -43,20 +46,13 @@ namespace OpenNest.Converters
|
|||||||
case CodeType.SubProgramCall:
|
case CodeType.SubProgramCall:
|
||||||
var subpgm = (SubProgramCall)code;
|
var subpgm = (SubProgramCall)code;
|
||||||
var savedMode = mode;
|
var savedMode = mode;
|
||||||
var savedPos = curpos;
|
|
||||||
|
|
||||||
// Position the sub-program at savedPos + Offset.
|
// The sub-program's frame origin in this program's frame is
|
||||||
// savedPos is the base position ((0,0) here, Part.Location in rendering).
|
// frameOrigin + Offset — independent of current tool position.
|
||||||
// Offset is the hole center in drawing-local coordinates.
|
curpos = new Vector(frameOrigin.X + subpgm.Offset.X, frameOrigin.Y + subpgm.Offset.Y);
|
||||||
curpos = new Vector(savedPos.X + subpgm.Offset.X, savedPos.Y + subpgm.Offset.Y);
|
|
||||||
|
|
||||||
AddProgram(subpgm.Program, ref mode, ref curpos, ref geometry);
|
AddProgram(subpgm.Program, ref mode, ref curpos, ref geometry);
|
||||||
mode = savedMode;
|
mode = savedMode;
|
||||||
|
|
||||||
// Restore curpos: ConvertMode.ToIncremental skips SubProgramCalls
|
|
||||||
// when computing deltas, so subsequent incremental codes expect
|
|
||||||
// curpos to be where it was before the call.
|
|
||||||
curpos = savedPos;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,12 +226,23 @@ public sealed class CincinnatiSheetWriter
|
|||||||
? _config.FeatureLineNumberStart
|
? _config.FeatureLineNumberStart
|
||||||
: 1000 + featureIndex + 1;
|
: 1000 + featureIndex + 1;
|
||||||
|
|
||||||
|
// Shift the local origin to the hole center via G52 (manual §1.52).
|
||||||
|
// G52 does not move the nozzle, so the sub-program's first rapid
|
||||||
|
// (the lead-in to the pierce point) takes the tool straight from the
|
||||||
|
// previous feature's end to pierce. The hole sub-program is authored
|
||||||
|
// in hole-local coordinates and resolves to `hole + local` under the
|
||||||
|
// shift. See docs/cincinnati-post-output.md for the full bracket.
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
if (_config.UseLineNumbers)
|
if (_config.UseLineNumbers)
|
||||||
sb.Append($"N{featureNumber} ");
|
sb.Append($"N{featureNumber} ");
|
||||||
sb.Append($"M98 P{postSubNum} X{_fmt.FormatCoord(call.Offset.X)} Y{_fmt.FormatCoord(call.Offset.Y)}");
|
sb.Append($"G52 X{_fmt.FormatCoord(call.Offset.X)} Y{_fmt.FormatCoord(call.Offset.Y)}");
|
||||||
w.WriteLine(sb.ToString());
|
w.WriteLine(sb.ToString());
|
||||||
|
|
||||||
|
w.WriteLine($"M98 P{postSubNum}");
|
||||||
|
|
||||||
|
// Cancel the local shift (manual §1.52).
|
||||||
|
w.WriteLine("G52 X0 Y0");
|
||||||
|
|
||||||
if (!isLastFeature)
|
if (!isLastFeature)
|
||||||
w.WriteLine("M47");
|
w.WriteLine("M47");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,25 @@ public class HoleSubProgramTests
|
|||||||
Assert.Contains("Y2.5", str);
|
Assert.Contains("Y2.5", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubProgramCall_ToString_IncludesOffsetAndRotation()
|
||||||
|
{
|
||||||
|
var call = new SubProgramCall { Id = 1000, Offset = new Vector(1.5, 2.5), Rotation = 30 };
|
||||||
|
var str = call.ToString();
|
||||||
|
Assert.Contains("P1000", str);
|
||||||
|
Assert.Contains("X1.5", str);
|
||||||
|
Assert.Contains("Y2.5", str);
|
||||||
|
Assert.Contains("R30", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubProgramCall_ToString_OmitsZeroFields()
|
||||||
|
{
|
||||||
|
var call = new SubProgramCall { Id = 1000 };
|
||||||
|
var str = call.ToString();
|
||||||
|
Assert.Equal("G65 P1000", str);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Program_SubPrograms_EmptyByDefault()
|
public void Program_SubPrograms_EmptyByDefault()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ namespace OpenNest.Controls
|
|||||||
{
|
{
|
||||||
public static void DrawProgram(Graphics g, DrawControl view, Program pgm, ref Vector pos,
|
public static void DrawProgram(Graphics g, DrawControl view, Program pgm, ref Vector pos,
|
||||||
Pen pen, double spacing, float arrowSize)
|
Pen pen, double spacing, float arrowSize)
|
||||||
|
{
|
||||||
|
DrawProgram(g, view, pgm, pos, ref pos, pen, spacing, arrowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawProgram(Graphics g, DrawControl view, Program pgm, Vector basePos, ref Vector pos,
|
||||||
|
Pen pen, double spacing, float arrowSize)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < pgm.Length; ++i)
|
for (var i = 0; i < pgm.Length; ++i)
|
||||||
{
|
{
|
||||||
@@ -19,10 +25,9 @@ namespace OpenNest.Controls
|
|||||||
var subpgm = (SubProgramCall)code;
|
var subpgm = (SubProgramCall)code;
|
||||||
if (subpgm.Program != null)
|
if (subpgm.Program != null)
|
||||||
{
|
{
|
||||||
var savedPos = pos;
|
var holeBase = basePos + subpgm.Offset;
|
||||||
pos = new Vector(savedPos.X + subpgm.Offset.X, savedPos.Y + subpgm.Offset.Y);
|
pos = holeBase;
|
||||||
DrawProgram(g, view, subpgm.Program, ref pos, pen, spacing, arrowSize);
|
DrawProgram(g, view, subpgm.Program, holeBase, ref pos, pen, spacing, arrowSize);
|
||||||
pos = savedPos;
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -31,7 +36,7 @@ namespace OpenNest.Controls
|
|||||||
|
|
||||||
var endpt = pgm.Mode == Mode.Incremental
|
var endpt = pgm.Mode == Mode.Incremental
|
||||||
? motion.EndPoint + pos
|
? motion.EndPoint + pos
|
||||||
: motion.EndPoint;
|
: motion.EndPoint + basePos;
|
||||||
|
|
||||||
if (code.Type == CodeType.LinearMove)
|
if (code.Type == CodeType.LinearMove)
|
||||||
{
|
{
|
||||||
@@ -46,7 +51,7 @@ namespace OpenNest.Controls
|
|||||||
{
|
{
|
||||||
var center = pgm.Mode == Mode.Incremental
|
var center = pgm.Mode == Mode.Incremental
|
||||||
? arc.CenterPoint + pos
|
? arc.CenterPoint + pos
|
||||||
: arc.CenterPoint;
|
: arc.CenterPoint + basePos;
|
||||||
DrawArcArrows(g, view, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
|
DrawArcArrows(g, view, pos, endpt, center, arc.Rotation, pen, spacing, arrowSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,8 +395,8 @@ namespace OpenNest.Controls
|
|||||||
var piercePoint = GetFirstPiercePoint(pgm, part.Location);
|
var piercePoint = GetFirstPiercePoint(pgm, part.Location);
|
||||||
DrawLine(g, pos, piercePoint, view.ColorScheme.RapidPen);
|
DrawLine(g, pos, piercePoint, view.ColorScheme.RapidPen);
|
||||||
|
|
||||||
pos = part.Location;
|
pos = piercePoint;
|
||||||
DrawRapids(g, pgm, ref pos, skipFirstRapid: true);
|
DrawRapids(g, pgm, part.Location, ref pos, skipFirstRapid: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,15 +409,13 @@ namespace OpenNest.Controls
|
|||||||
|
|
||||||
if (pgm[i] is Motion motion)
|
if (pgm[i] is Motion motion)
|
||||||
{
|
{
|
||||||
if (pgm.Mode == Mode.Incremental)
|
|
||||||
return motion.EndPoint + partLocation;
|
return motion.EndPoint + partLocation;
|
||||||
return motion.EndPoint;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return partLocation;
|
return partLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawRapids(Graphics g, Program pgm, ref Vector pos, bool skipFirstRapid = false)
|
private void DrawRapids(Graphics g, Program pgm, Vector basePos, ref Vector pos, bool skipFirstRapid = false)
|
||||||
{
|
{
|
||||||
var firstRapidSkipped = false;
|
var firstRapidSkipped = false;
|
||||||
|
|
||||||
@@ -425,60 +423,47 @@ namespace OpenNest.Controls
|
|||||||
{
|
{
|
||||||
var code = pgm[i];
|
var code = pgm[i];
|
||||||
|
|
||||||
if (code.Type == CodeType.SubProgramCall)
|
if (code is SubProgramCall { Program: { } program } call)
|
||||||
{
|
{
|
||||||
var subpgm = (SubProgramCall)code;
|
// A SubProgramCall is a coordinate-frame shift, not a physical
|
||||||
var program = subpgm.Program;
|
// rapid to the hole center. The Cincinnati post emits it as a
|
||||||
|
// G52 bracket, so the physical rapid is the sub-program's first
|
||||||
|
// motion, which goes straight from here to the lead-in pierce.
|
||||||
|
// Look ahead for that pierce point and draw the direct rapid,
|
||||||
|
// then recurse with skipFirstRapid so the sub doesn't also draw
|
||||||
|
// its first rapid on top. See docs/cincinnati-post-output.md.
|
||||||
|
var holeBase = basePos + call.Offset;
|
||||||
|
var firstPierce = GetFirstPiercePoint(program, holeBase);
|
||||||
|
|
||||||
if (program != null)
|
if (ShouldDrawRapid(skipFirstRapid, ref firstRapidSkipped))
|
||||||
{
|
DrawLine(g, pos, firstPierce, view.ColorScheme.RapidPen);
|
||||||
var holePos = new Vector(pos.X + subpgm.Offset.X, pos.Y + subpgm.Offset.Y);
|
|
||||||
|
|
||||||
// Draw rapid from current position to hole center
|
var subPos = holeBase;
|
||||||
if (!(skipFirstRapid && !firstRapidSkipped))
|
DrawRapids(g, program, holeBase, ref subPos, skipFirstRapid: true);
|
||||||
DrawLine(g, pos, holePos, view.ColorScheme.RapidPen);
|
pos = subPos;
|
||||||
else
|
|
||||||
firstRapidSkipped = true;
|
|
||||||
|
|
||||||
pos = holePos;
|
|
||||||
DrawRapids(g, program, ref pos);
|
|
||||||
// Don't restore pos — let it advance so the next hole's
|
|
||||||
// rapid starts from where this one ended.
|
|
||||||
}
|
}
|
||||||
}
|
else if (code is Motion motion)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var motion = code as Motion;
|
var endpt = pgm.Mode == Mode.Incremental
|
||||||
|
? motion.EndPoint + pos
|
||||||
|
: motion.EndPoint;
|
||||||
|
|
||||||
if (motion != null)
|
if (code.Type == CodeType.RapidMove && ShouldDrawRapid(skipFirstRapid, ref firstRapidSkipped))
|
||||||
{
|
|
||||||
if (pgm.Mode == Mode.Incremental)
|
|
||||||
{
|
|
||||||
var endpt = motion.EndPoint + pos;
|
|
||||||
|
|
||||||
if (code.Type == CodeType.RapidMove)
|
|
||||||
{
|
|
||||||
if (skipFirstRapid && !firstRapidSkipped)
|
|
||||||
firstRapidSkipped = true;
|
|
||||||
else
|
|
||||||
DrawLine(g, pos, endpt, view.ColorScheme.RapidPen);
|
DrawLine(g, pos, endpt, view.ColorScheme.RapidPen);
|
||||||
}
|
|
||||||
pos = endpt;
|
pos = endpt;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
if (code.Type == CodeType.RapidMove)
|
|
||||||
|
private static bool ShouldDrawRapid(bool skipFirstRapid, ref bool firstRapidSkipped)
|
||||||
{
|
{
|
||||||
if (skipFirstRapid && !firstRapidSkipped)
|
if (skipFirstRapid && !firstRapidSkipped)
|
||||||
|
{
|
||||||
firstRapidSkipped = true;
|
firstRapidSkipped = true;
|
||||||
else
|
return false;
|
||||||
DrawLine(g, pos, motion.EndPoint, view.ColorScheme.RapidPen);
|
|
||||||
}
|
|
||||||
pos = motion.EndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAllPiercePoints(Graphics g)
|
private void DrawAllPiercePoints(Graphics g)
|
||||||
@@ -491,11 +476,11 @@ namespace OpenNest.Controls
|
|||||||
var part = view.Plate.Parts[i];
|
var part = view.Plate.Parts[i];
|
||||||
var pgm = part.Program;
|
var pgm = part.Program;
|
||||||
var pos = part.Location;
|
var pos = part.Location;
|
||||||
DrawProgramPiercePoints(g, pgm, ref pos, brush, pen);
|
DrawProgramPiercePoints(g, pgm, part.Location, ref pos, brush, pen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawProgramPiercePoints(Graphics g, Program pgm, ref Vector pos, Brush brush, Pen pen)
|
private void DrawProgramPiercePoints(Graphics g, Program pgm, Vector basePos, ref Vector pos, Brush brush, Pen pen)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < pgm.Length; ++i)
|
for (var i = 0; i < pgm.Length; ++i)
|
||||||
{
|
{
|
||||||
@@ -506,10 +491,9 @@ namespace OpenNest.Controls
|
|||||||
var subpgm = (SubProgramCall)code;
|
var subpgm = (SubProgramCall)code;
|
||||||
if (subpgm.Program != null)
|
if (subpgm.Program != null)
|
||||||
{
|
{
|
||||||
var savedPos = pos;
|
var holeBase = basePos + subpgm.Offset;
|
||||||
pos = new Vector(savedPos.X + subpgm.Offset.X, savedPos.Y + subpgm.Offset.Y);
|
pos = holeBase;
|
||||||
DrawProgramPiercePoints(g, subpgm.Program, ref pos, brush, pen);
|
DrawProgramPiercePoints(g, subpgm.Program, holeBase, ref pos, brush, pen);
|
||||||
pos = savedPos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -519,7 +503,7 @@ namespace OpenNest.Controls
|
|||||||
|
|
||||||
var endpt = pgm.Mode == Mode.Incremental
|
var endpt = pgm.Mode == Mode.Incremental
|
||||||
? motion.EndPoint + pos
|
? motion.EndPoint + pos
|
||||||
: motion.EndPoint;
|
: motion.EndPoint + basePos;
|
||||||
|
|
||||||
if (code.Type == CodeType.RapidMove)
|
if (code.Type == CodeType.RapidMove)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ namespace OpenNest
|
|||||||
private static void AddProgramSplit(GraphicsPath cutPath, GraphicsPath leadPath,
|
private static void AddProgramSplit(GraphicsPath cutPath, GraphicsPath leadPath,
|
||||||
Program pgm, Mode mode, ref Vector curpos)
|
Program pgm, Mode mode, ref Vector curpos)
|
||||||
{
|
{
|
||||||
|
// Capture the frame origin at entry. Sub-program Offsets are relative
|
||||||
|
// to this fixed origin, not to the current tool position.
|
||||||
|
var frameOrigin = curpos;
|
||||||
mode = pgm.Mode;
|
mode = pgm.Mode;
|
||||||
|
|
||||||
for (var i = 0; i < pgm.Length; ++i)
|
for (var i = 0; i < pgm.Length; ++i)
|
||||||
@@ -147,10 +150,8 @@ namespace OpenNest
|
|||||||
{
|
{
|
||||||
cutPath.StartFigure();
|
cutPath.StartFigure();
|
||||||
leadPath.StartFigure();
|
leadPath.StartFigure();
|
||||||
var savedPos = curpos;
|
curpos = new Vector(frameOrigin.X + subpgm.Offset.X, frameOrigin.Y + subpgm.Offset.Y);
|
||||||
curpos = new Vector(savedPos.X + subpgm.Offset.X, savedPos.Y + subpgm.Offset.Y);
|
|
||||||
AddProgramSplit(cutPath, leadPath, subpgm.Program, mode, ref curpos);
|
AddProgramSplit(cutPath, leadPath, subpgm.Program, mode, ref curpos);
|
||||||
curpos = savedPos;
|
|
||||||
}
|
}
|
||||||
mode = tmpmode;
|
mode = tmpmode;
|
||||||
break;
|
break;
|
||||||
@@ -240,6 +241,9 @@ namespace OpenNest
|
|||||||
|
|
||||||
private static void AddProgram(GraphicsPath path, Program pgm, Mode mode, ref Vector curpos)
|
private static void AddProgram(GraphicsPath path, Program pgm, Mode mode, ref Vector curpos)
|
||||||
{
|
{
|
||||||
|
// Capture the frame origin at entry. Sub-program Offsets are relative
|
||||||
|
// to this fixed origin, not to the current tool position.
|
||||||
|
var frameOrigin = curpos;
|
||||||
mode = pgm.Mode;
|
mode = pgm.Mode;
|
||||||
GraphicsPath currentFigure = null;
|
GraphicsPath currentFigure = null;
|
||||||
|
|
||||||
@@ -308,10 +312,8 @@ namespace OpenNest
|
|||||||
|
|
||||||
if (subpgm.Program != null)
|
if (subpgm.Program != null)
|
||||||
{
|
{
|
||||||
var savedPos = curpos;
|
curpos = new Vector(frameOrigin.X + subpgm.Offset.X, frameOrigin.Y + subpgm.Offset.Y);
|
||||||
curpos = new Vector(savedPos.X + subpgm.Offset.X, savedPos.Y + subpgm.Offset.Y);
|
|
||||||
AddProgram(path, subpgm.Program, mode, ref curpos);
|
AddProgram(path, subpgm.Program, mode, ref curpos);
|
||||||
curpos = savedPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mode = tmpmode;
|
mode = tmpmode;
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,212 @@
|
|||||||
|
# Cincinnati Post Output Reference
|
||||||
|
|
||||||
|
Reference for the G-code structure emitted by `OpenNest.Posts.Cincinnati`.
|
||||||
|
Every code listed here maps to a section in the Cincinnati Laser Programming
|
||||||
|
Manual (`docs/CINCINNATI LASER PROGRAMMING MANUAL.pdf`, EM-423 R-02/11).
|
||||||
|
Section numbers in parentheses (e.g. `§1.52`) refer to the manual.
|
||||||
|
|
||||||
|
If you add a new emission in the post, either cite the manual section it maps
|
||||||
|
to, or flag it here as a known custom extension. "Custom code" in this project
|
||||||
|
means something that is not documented in the manual but that the Cincinnati
|
||||||
|
control is known to accept — none exist today and we should not introduce any
|
||||||
|
without confirming the control behavior.
|
||||||
|
|
||||||
|
## Overall file structure
|
||||||
|
|
||||||
|
A generated file contains, in order:
|
||||||
|
|
||||||
|
1. **Main program** (`CincinnatiPreambleWriter.WriteMainProgram`)
|
||||||
|
Preamble, unit/mode setup, initial library, variable-declaration call, one
|
||||||
|
`M98 P<sheetSubNum>` call per plate quantity, and `M30` to end.
|
||||||
|
|
||||||
|
2. **Variable declaration sub-program** (`CincinnatiPreambleWriter.WriteVariableDeclaration`)
|
||||||
|
Machine variables (`#number = value`) used across the nest, terminated
|
||||||
|
with `M99`.
|
||||||
|
|
||||||
|
3. **Sheet sub-programs** (`CincinnatiSheetWriter.Write`), one per unique plate
|
||||||
|
layout. A sheet sub-program contains the cutting sequence for a whole
|
||||||
|
plate, either with features inlined or with `M98` calls into part
|
||||||
|
sub-programs.
|
||||||
|
|
||||||
|
4. **Part sub-programs** (`CincinnatiPartSubprogramWriter.Write`), one per
|
||||||
|
unique `(drawing, rotation)` pair, only emitted when
|
||||||
|
`Config.UsePartSubprograms` is enabled.
|
||||||
|
|
||||||
|
5. **Hole sub-programs** (`CincinnatiPartSubprogramWriter.Write` reused with a
|
||||||
|
`"HOLE"` label), one per unique hole geometry keyed by radius and lead-in
|
||||||
|
normal angle.
|
||||||
|
|
||||||
|
Sub-program bodies start with a `:<subNum>` label and end with `M99`.
|
||||||
|
|
||||||
|
## Feature blocks
|
||||||
|
|
||||||
|
A "feature" is a single contour: lead-in → cut moves → lead-out. Each feature
|
||||||
|
block in a sheet or sub-program output follows this order
|
||||||
|
(`CincinnatiFeatureWriter.Write`):
|
||||||
|
|
||||||
|
1. `G0 X_ Y_` — rapid to the pierce point (§1.00).
|
||||||
|
2. Optional part-name comment, only on the first feature of each part.
|
||||||
|
3. `G89 P<library>` — load process parameters (§2.89). `P` is a library file
|
||||||
|
name; the `(...)` trailing comment carries speed-class info.
|
||||||
|
4. `G84` (cut) or `G85` (etch / no-pierce) — pierce and start cut, or start
|
||||||
|
cut without pierce (§2.84 / §2.85).
|
||||||
|
5. `M130 (ANTI DIVE OFF)` — disable anti-dive, only if configured (§3.130).
|
||||||
|
6. Contour moves:
|
||||||
|
- `G41` (left) or `G42` (right) kerf compensation on the first cut move
|
||||||
|
(§1.41 / §1.42), suppressed for etch features.
|
||||||
|
- `G1 X_ Y_ [F<feedvar>]` — linear cut move (§1.01). Feedrate references a
|
||||||
|
machine variable such as `#148` and is emitted only when it changes.
|
||||||
|
- `G2 X_ Y_ I_ J_ [F<feedvar>]` (CW) or `G3` (CCW) — arc (§1.02 / §1.03).
|
||||||
|
`I`/`J` are incremental offsets from the current position to the center.
|
||||||
|
7. `G40` — cancel kerf compensation (§1.40), only if it was applied.
|
||||||
|
8. `M35` (or `M135` if SpeedGas is enabled) — beam off (§3.35 / §3.135).
|
||||||
|
9. `M131 (ANTI DIVE ON)` — re-enable anti-dive (§3.131).
|
||||||
|
10. `M47` or `M47 P<distance>` — raise Z-axis, unless this is the last feature
|
||||||
|
on the sheet (§3.47). A leading `/` (block delete, §5.6) is prepended when
|
||||||
|
the configured override distance exceeds the default.
|
||||||
|
|
||||||
|
Sheet sub-program and sheet-level feature calls add `G92 X#5021 Y#5022`
|
||||||
|
(§1.92) at the top so the local origin is anchored to the machine's current
|
||||||
|
absolute position (`#5021`/`#5022` are the machine X/Y system variables).
|
||||||
|
|
||||||
|
## Sub-program call patterns
|
||||||
|
|
||||||
|
There are two distinct call-site patterns, depending on whether the call
|
||||||
|
targets a whole-part sub-program or a hole sub-program.
|
||||||
|
|
||||||
|
### Part sub-program call (`WriteSubprogramCall`)
|
||||||
|
|
||||||
|
Used when `Config.UsePartSubprograms` is enabled. The tool physically rapids
|
||||||
|
to the part corner, then G92 sets the current position as the local origin,
|
||||||
|
the sub-program executes in its own local coordinate frame, and G92 restores
|
||||||
|
the original absolute position after return.
|
||||||
|
|
||||||
|
```
|
||||||
|
G0 X<left> Y<bottom> ; rapid to part bounding box corner (§1.00)
|
||||||
|
(PART: <name>)
|
||||||
|
G92 X0 Y0 ; set local origin at current position (§1.92)
|
||||||
|
M98 P<partSubNum> (<name>) ; call the part sub-program (§3.98)
|
||||||
|
G92 X<left> Y<bottom> ; restore the sheet coordinate system (§1.92)
|
||||||
|
M47 ; head raise unless this is the last part (§3.47)
|
||||||
|
```
|
||||||
|
|
||||||
|
This pattern uses G92 because the tool is physically positioned at the part
|
||||||
|
corner first. The sub-program's coordinates are part-local, so they are
|
||||||
|
interpreted against the new origin until G92 restores the sheet frame.
|
||||||
|
|
||||||
|
### Hole sub-program call (`WriteHoleSubprogramCall`)
|
||||||
|
|
||||||
|
Used for the `SubProgramCall` codes that a `ContourCuttingStrategy` emits for
|
||||||
|
each circular hole. Unlike parts, we do **not** want a physical rapid to the
|
||||||
|
hole center before calling — the sub-program's first rapid is the lead-in to
|
||||||
|
the pierce point, and the machine should travel directly from the previous
|
||||||
|
feature's end to that pierce.
|
||||||
|
|
||||||
|
```
|
||||||
|
G52 X<hole.x> Y<hole.y> ; shift local origin to hole center (§1.52)
|
||||||
|
M98 P<holeSubNum> ; call the shared hole sub-program (§3.98)
|
||||||
|
G52 X0 Y0 ; restore the original coordinate system (§1.52)
|
||||||
|
M47 ; head raise unless this is the last feature (§3.47)
|
||||||
|
```
|
||||||
|
|
||||||
|
G52 specifies the new origin in the current work coordinate system and — per
|
||||||
|
§1.52 — "does not move the cutting nozzle". The hole sub-program is written
|
||||||
|
in hole-local coordinates (origin at the hole center, produced by
|
||||||
|
`ContourCuttingStrategy`), so its first `G0 X_ Y_` resolves to `hole + local`
|
||||||
|
in absolute terms. That is the first physical motion, and it takes the tool
|
||||||
|
straight from wherever it was to the lead-in pierce point. G52 X0 Y0 cancels
|
||||||
|
the shift after `M99` returns control.
|
||||||
|
|
||||||
|
## G-code reference
|
||||||
|
|
||||||
|
These are every G/M code the post emits, grouped by category. Anything here is
|
||||||
|
documented in the programming manual. Anything not here should be audited the
|
||||||
|
next time the post is edited.
|
||||||
|
|
||||||
|
### Motion modes and contouring
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G0 X_ Y_` | Rapid traverse | §1.00 |
|
||||||
|
| `G1 X_ Y_ F_` | Linear feedrate move | §1.01 |
|
||||||
|
| `G2 X_ Y_ I_ J_ F_` | Clockwise arc | §1.02 |
|
||||||
|
| `G3 X_ Y_ I_ J_ F_` | Counter-clockwise arc | §1.03 |
|
||||||
|
|
||||||
|
### Units and coordinate mode
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G20` | Inch mode | §1.20 |
|
||||||
|
| `G21` | Metric mode | §1.21 |
|
||||||
|
| `G90` | Absolute mode | §1.90 |
|
||||||
|
|
||||||
|
### Kerf compensation
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G40` | Cancel kerf compensation | §1.40 |
|
||||||
|
| `G41` | Kerf compensation, left side | §1.41 |
|
||||||
|
| `G42` | Kerf compensation, right side | §1.42 |
|
||||||
|
|
||||||
|
### Work coordinate systems
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G52 X_ Y_` | Temporary local work coordinate offset. Does not move the tool. `G52 X0 Y0` cancels. | §1.52 |
|
||||||
|
| `G92 X_ Y_` | Sets the current tool position to `(X, Y)` in the work coordinate system, implicitly redefining the WCS origin. | §1.92 |
|
||||||
|
|
||||||
|
### Exact stop
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G61` | Exact stop mode | §1.61 |
|
||||||
|
|
||||||
|
### Cutting operations (custom Cincinnati G-codes)
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `G84` | Pierce and start cut | §2.84 |
|
||||||
|
| `G85` | Start cut without pierce (used for etch) | §2.85 |
|
||||||
|
| `G89 P<file>` | Load process parameters from a library file | §2.89 |
|
||||||
|
| `G121` | Enable non-stop cutting (Smart Rapids) | §2.121 |
|
||||||
|
|
||||||
|
### Program flow
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `M30` | End of main program with rewind | §3.30 |
|
||||||
|
| `M98 P_` | Sub-program call. **Takes only `P` and `L` — not `X`/`Y`.** | §3.98 |
|
||||||
|
| `M99` | Return from sub-program | §3.99 |
|
||||||
|
|
||||||
|
### Machine state
|
||||||
|
|
||||||
|
| Code | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `M35` | Beam off | §3.35 |
|
||||||
|
| `M42` | Retract Z-axis | §3.42 |
|
||||||
|
| `M47 [P<dist>]` | Raise Z-axis, optionally by a distance | §3.47 |
|
||||||
|
| `M50` | Switch pallets | §3.50 |
|
||||||
|
| `M130` | Anti-dive off | §3.130 |
|
||||||
|
| `M131` | Anti-dive on | §3.131 |
|
||||||
|
| `M135` | Discharge current off (keeps assist gas on) | §3.135 |
|
||||||
|
|
||||||
|
### Comments, labels, and block delete
|
||||||
|
|
||||||
|
| Syntax | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `(text)` | Inline comment | §5.4 |
|
||||||
|
| `:<number>` | Sub-program label | §3.98 |
|
||||||
|
| `/<block>` | Block delete — operator can toggle the line off | §5.6 |
|
||||||
|
| `N<number>` | Line number, used by M99 P / GOTO targets | §5.5 |
|
||||||
|
|
||||||
|
## System variables referenced
|
||||||
|
|
||||||
|
| Variable | Description | Manual |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `#148` | Default cut feedrate variable (used in `F#148`) | §2.89 |
|
||||||
|
| `#5021` | Current machine X position | §6 (table of system variables) |
|
||||||
|
| `#5022` | Current machine Y position | §6 (table of system variables) |
|
||||||
|
|
||||||
|
Project-defined variables start at `Config.SheetWidthVariable` /
|
||||||
|
`Config.SheetLengthVariable` and at `Config.UserVariableStart`. Those ranges
|
||||||
|
are documented in `CincinnatiPostConfig.cs`.
|
||||||
Reference in New Issue
Block a user