fix(post): post sheet cut-offs in CI Fiber without comp or lead-in
Posting a nest with a sheet cut-off threw because the CI Fiber writer treated every contour as a compensated part contour and requires a linear lead-in after G41/G42 (TF5200 13.2.4.1). Cut-offs are open centreline cuts with no lead-in and no inside/outside, so they now post without G41/G42 and run after every part on the sheet so the sheet is not severed before the parts are cut (matching the CL post). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -222,6 +222,35 @@ public class CIFiberPostProcessorTests
|
|||||||
Assert.Contains("13.2.4.1", ex.Message);
|
Assert.Contains("13.2.4.1", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Post_CutOff_PostsLastWithoutCompensationOrLeadIn()
|
||||||
|
{
|
||||||
|
var nest = MakeSquareWithHoleNest();
|
||||||
|
var plate = nest.Plates[0];
|
||||||
|
|
||||||
|
// Vertical cut-off at X=20: a bare rapid + line, no lead-in.
|
||||||
|
var cutOff = new CutOff(new Vector(20, 0), CutOffAxis.Vertical);
|
||||||
|
cutOff.Regenerate(plate, new CutOffSettings());
|
||||||
|
plate.Parts.Insert(0, new Part(cutOff.Drawing, Vector.Zero));
|
||||||
|
|
||||||
|
var lines = Lines(Post(nest));
|
||||||
|
|
||||||
|
// Cut-off follows the real part despite being first in plate order.
|
||||||
|
var partEnd = lines.IndexOf("( PART END )");
|
||||||
|
var cutOffPart = lines.IndexOf("( Part #2 )");
|
||||||
|
Assert.True(partEnd >= 0 && cutOffPart > partEnd);
|
||||||
|
|
||||||
|
var n3 = lines.IndexOf("N3:");
|
||||||
|
Assert.True(n3 > cutOffPart);
|
||||||
|
Assert.Equal("/L \"L0\"", lines[n3 + 1]);
|
||||||
|
Assert.Equal("V.E.R3=3", lines[n3 + 2]);
|
||||||
|
Assert.StartsWith("G0X20Y", lines[n3 + 3]);
|
||||||
|
Assert.Equal("/L \"L4\"", lines[n3 + 4]);
|
||||||
|
Assert.Equal("/L \"L6\"", lines[n3 + 5]);
|
||||||
|
Assert.StartsWith("G1X20Y", lines[n3 + 6]);
|
||||||
|
Assert.Equal("/L \"ZHSOFF\"", lines[n3 + 7]);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Post_ValidatesTableSize()
|
public void Post_ValidatesTableSize()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -135,8 +135,14 @@ namespace OpenNest.Posts.CincinnatiCIFiber
|
|||||||
|
|
||||||
private int WriteSheet(Plate plate, TextWriter w, int contourNumber)
|
private int WriteSheet(Plate plate, TextWriter w, int contourNumber)
|
||||||
{
|
{
|
||||||
|
// Cut-offs run last: severing the sheet first would free the
|
||||||
|
// skeleton before the parts are cut (matches the CL post).
|
||||||
|
var ordered = plate
|
||||||
|
.Parts.Where(p => !p.BaseDrawing.IsCutOff)
|
||||||
|
.Concat(plate.Parts.Where(p => p.BaseDrawing.IsCutOff));
|
||||||
|
|
||||||
var partNumber = 0;
|
var partNumber = 0;
|
||||||
foreach (var part in plate.Parts)
|
foreach (var part in ordered)
|
||||||
{
|
{
|
||||||
partNumber++;
|
partNumber++;
|
||||||
contourNumber = WritePart(part, partNumber, w, contourNumber);
|
contourNumber = WritePart(part, partNumber, w, contourNumber);
|
||||||
@@ -158,10 +164,14 @@ namespace OpenNest.Posts.CincinnatiCIFiber
|
|||||||
if (_config.SkipScribe)
|
if (_config.SkipScribe)
|
||||||
contours = contours.Where(c => !IsScribeContour(c)).ToList();
|
contours = contours.Where(c => !IsScribeContour(c)).ToList();
|
||||||
|
|
||||||
|
var isCutOff = part.BaseDrawing.IsCutOff;
|
||||||
foreach (var contour in contours)
|
foreach (var contour in contours)
|
||||||
{
|
{
|
||||||
contourNumber++;
|
contourNumber++;
|
||||||
WriteContour(contour, contourNumber, w);
|
if (isCutOff)
|
||||||
|
WriteCutOffContour(contour, contourNumber, w);
|
||||||
|
else
|
||||||
|
WriteContour(contour, contourNumber, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
CIFiberFormatter.Line(w, "( PART END )");
|
CIFiberFormatter.Line(w, "( PART END )");
|
||||||
@@ -227,6 +237,38 @@ namespace OpenNest.Posts.CincinnatiCIFiber
|
|||||||
CIFiberFormatter.Line(w, SkippableLine(_config.LayerCutEnd));
|
CIFiberFormatter.Line(w, SkippableLine(_config.LayerCutEnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cut-offs are open straight lines with no lead-in: the line is the
|
||||||
|
/// beam centreline (CutOffSettings clearance already allows for kerf)
|
||||||
|
/// and an open line has no inside/outside, so no G41/G42 is selected
|
||||||
|
/// and the §13.2.4.1 linear-lead-in rule does not apply. The exterior
|
||||||
|
/// lead layer still runs so the pierce sequence matches a perimeter.
|
||||||
|
/// </summary>
|
||||||
|
private void WriteCutOffContour(CIFiberContour contour, int contourNumber, TextWriter w)
|
||||||
|
{
|
||||||
|
CIFiberFormatter.Line(w, $"N{contourNumber}:");
|
||||||
|
CIFiberFormatter.Line(w, SkippableLine(_config.LayerCancel));
|
||||||
|
CIFiberFormatter.Line(w, $"V.E.R3={contourNumber}");
|
||||||
|
CIFiberFormatter.Line(w, $"G0X{Fmt(contour.Pierce.X)}Y{Fmt(contour.Pierce.Y)}");
|
||||||
|
CIFiberFormatter.Line(w, SkippableLine(_config.LayerExteriorLeadin));
|
||||||
|
CIFiberFormatter.Line(w, SkippableLine(_config.LayerCut));
|
||||||
|
|
||||||
|
var prev = contour.Pierce;
|
||||||
|
if (contour.LeadIn != null)
|
||||||
|
{
|
||||||
|
CIFiberFormatter.Line(w, FormatMotion(contour.LeadIn, prev));
|
||||||
|
prev = contour.LeadIn.EndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var motion in contour.LeadInExtra.Concat(contour.Cuts))
|
||||||
|
{
|
||||||
|
CIFiberFormatter.Line(w, FormatMotion(motion, prev));
|
||||||
|
prev = motion.EndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
CIFiberFormatter.Line(w, SkippableLine(_config.LayerCutEnd));
|
||||||
|
}
|
||||||
|
|
||||||
private string ResolvePartComment(Part part)
|
private string ResolvePartComment(Part part)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(_config.PartComment))
|
if (!string.IsNullOrWhiteSpace(_config.PartComment))
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ uses the Cincinnati machine-sample convention, not a generic TF5200 laser API:
|
|||||||
- Tail: `/L "L0"`, `L PROGRAMEND.NC`, `M50`, `M30`, and `%`.
|
- Tail: `/L "L0"`, `L PROGRAMEND.NC`, `M50`, `M30`, and `%`.
|
||||||
- Motion endpoints are sheet-absolute XY. Arc `I`/`J` are offsets from the arc
|
- Motion endpoints are sheet-absolute XY. Arc `I`/`J` are offsets from the arc
|
||||||
start, matching the G162 convention; the post does not explicitly emit G162.
|
start, matching the G162 convention; the post does not explicitly emit G162.
|
||||||
|
- Sheet cut-offs post after every part on their sheet. Each segment is an open
|
||||||
|
line with no lead-in: rapid to its start, `/L "L4"`, `/L "L6"`, the `G1`,
|
||||||
|
and `/L "ZHSOFF"`. No `G41`/`G42` is selected, because the line is the beam
|
||||||
|
centreline and has no inside or outside. Whether the `L4` macro runs
|
||||||
|
correctly without a following lead-in move has not been confirmed on the
|
||||||
|
machine.
|
||||||
- Hole subprogram geometry is inlined. Suppressed moves and, by default,
|
- Hole subprogram geometry is inlined. Suppressed moves and, by default,
|
||||||
wholly scribe contours are omitted.
|
wholly scribe contours are omitted.
|
||||||
- Files use UTF-8 without a BOM and CRLF lines. Default accuracy is three decimal
|
- Files use UTF-8 without a BOM and CRLF lines. Default accuracy is three decimal
|
||||||
@@ -58,6 +64,7 @@ The post is named for the machine family; table size belongs in configuration.
|
|||||||
|
|
||||||
[Output-contract tests](../OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs)
|
[Output-contract tests](../OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs)
|
||||||
cover a square with a hole, coordinate transforms/formatting, lead-in rejection,
|
cover a square with a hole, coordinate transforms/formatting, lead-in rejection,
|
||||||
|
cut-off ordering and output,
|
||||||
table bounds, and suppression/scribe handling. Run:
|
table bounds, and suppression/scribe handling. Run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
Reference in New Issue
Block a user