From 6803d7519ffbfe918961ee34df979051f10c0efd Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 28 Sep 2026 18:41:06 -0400 Subject: [PATCH] fix(posts): post CI Fiber cut-offs in their sequence, not last b0997f6 moved every cut-off after the parts. Plate.Parts order is the cut sequence, and the user sets each cut-off's place in it (Set Sequence), so the post must follow it rather than reorder. Cut-offs still post as uncompensated open lines with no lead-in. --- .../CIFiberPostProcessorTests.cs | 39 +++++++++++++++---- .../CIFiberProgramWriter.cs | 10 ++--- docs/cincinnati-ci-fiber-post-output.md | 6 ++- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs b/OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs index 9c3eb43..3891e25 100644 --- a/OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs +++ b/OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs @@ -223,7 +223,7 @@ public class CIFiberPostProcessorTests } [Fact] - public void Post_CutOff_PostsLastWithoutCompensationOrLeadIn() + public void Post_CutOff_PostsWithoutCompensationOrLeadIn() { var nest = MakeSquareWithHoleNest(); var plate = nest.Plates[0]; @@ -231,17 +231,12 @@ public class CIFiberPostProcessorTests // 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)); + plate.Parts.Add(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.True(n3 > lines.IndexOf("( PART:CutOff-V-20.00 )")); Assert.Equal("/L \"L0\"", lines[n3 + 1]); Assert.Equal("V.E.R3=3", lines[n3 + 2]); Assert.StartsWith("G0X20Y", lines[n3 + 3]); @@ -251,6 +246,34 @@ public class CIFiberPostProcessorTests Assert.Equal("/L \"ZHSOFF\"", lines[n3 + 7]); } + [Fact] + public void Post_CutOff_FollowsPlateSequence() + { + // Plate order is the cut sequence: part, cut-off, part. The cut-off is + // sequence 2, so it posts second, not after every part. + var nest = MakeSquareWithHoleNest(); + var plate = nest.Plates[0]; + plate.Parts.Add(new Part(plate.Parts[0].BaseDrawing, new Vector(40, 2))); + + var cutOff = new CutOff(new Vector(20, 0), CutOffAxis.Vertical); + cutOff.Regenerate(plate, new CutOffSettings()); + plate.Parts.Insert(1, new Part(cutOff.Drawing, Vector.Zero)); + + var lines = Lines(Post(nest)); + + Assert.Equal( + new[] { "( PART:square-hole )", "( PART:CutOff-V-20.00 )", "( PART:square-hole )" }, + lines.Where(l => l.StartsWith("( PART:")).ToArray() + ); + + // Part number and restart label run in the same order: the first + // part's hole and perimeter are N1/N2, so the cut-off is N3. + var cutOffBlock = lines.IndexOf("( PART:CutOff-V-20.00 )"); + Assert.Equal("( Part #2 )", lines[cutOffBlock - 1]); + Assert.Equal("V.E.R4=2", lines[cutOffBlock + 1]); + Assert.Equal("N3:", lines[cutOffBlock + 2]); + } + [Fact] public void Post_ValidatesTableSize() { diff --git a/Posts/OpenNest.Posts.CincinnatiCIFiber/CIFiberProgramWriter.cs b/Posts/OpenNest.Posts.CincinnatiCIFiber/CIFiberProgramWriter.cs index a51120f..f35492c 100644 --- a/Posts/OpenNest.Posts.CincinnatiCIFiber/CIFiberProgramWriter.cs +++ b/Posts/OpenNest.Posts.CincinnatiCIFiber/CIFiberProgramWriter.cs @@ -196,14 +196,10 @@ namespace OpenNest.Posts.CincinnatiCIFiber 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)); - + // Plate order is the cut sequence, cut-offs included: the user sets + // where each cut-off runs, so the post must not reorder them. var partNumber = 0; - foreach (var part in ordered) + foreach (var part in plate.Parts) { partNumber++; contourNumber = WritePart(part, partNumber, w, contourNumber); diff --git a/docs/cincinnati-ci-fiber-post-output.md b/docs/cincinnati-ci-fiber-post-output.md index 57ab252..6e84bc4 100644 --- a/docs/cincinnati-ci-fiber-post-output.md +++ b/docs/cincinnati-ci-fiber-post-output.md @@ -28,7 +28,9 @@ uses the Cincinnati machine-sample convention, not a generic TF5200 laser API: and `%`. - 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. -- Sheet cut-offs post after every part on their sheet. Each segment is an open +- Parts and sheet cut-offs post in the plate's cut sequence (`Plate.Parts` + order, as set by Set Sequence or a sequencer). A cut-off is not moved after + the parts. 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 @@ -85,7 +87,7 @@ validated and saved only when OK is pressed. [Output-contract tests](../OpenNest.Tests/CincinnatiCIFiber/CIFiberPostProcessorTests.cs) cover a square with a hole, coordinate transforms/formatting, lead-in rejection, -cut-off ordering and output, table bounds, suppression/scribe handling, and +cut-off sequencing and output, table bounds, suppression/scribe handling, and multi-sheet output in both modes. Run: ```sh