mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 10:51:06 -04:00
feat(posts): add Cincinnati CI Fiber (TF5200) post-processor
New post-processor plugin OpenNest.Posts.CincinnatiCIFiber for the CI Fiber laser family (nLight CLX / Beckhoff TF5200 / Precitec ProCutter, e.g. the CI Fiber 4020 8kW). Named by machine family, not table size. Emits the machine program contract of the Cincinnati-supplied sample NC (12992-4SS_NEST.nc): V.E.* header, restart jump, per-part V.E.R4 blocks, per-contour N labels with V.E.R3, skippable /L macro lines (L0/L2+G41 interior, L4+G42 exterior, L6 cut-on, ZHSOFF cut-end), G162 incremental arc I/J, trimmed 3-decimal spaceless coordinates, CRLF, M50/M30/%. Contour classification (interior vs exterior) derives from the material side of the closed cut path, not hardcoded winding. SubProgramCall holes are flattened to sheet coordinates (rotation-safe). Arc lead-ins are rejected per TF5200 13.2.4.1 (first motion block after G41/G42 selection must be linear). Table envelope (default 160.25 x 81.25 in) validated. Tests: structure golden on a square-with-hole nest, rotated-hole flatten, coordinate format, arc-lead-in rejection, table validation, suppressed/ scribe skipping, plus a SkippableFact regression against the real sample NC (109 parts, 2071 contours, L2=1962, L4=109, perimeter vertices match within 0.001). Fixtures configure through OpenNest.Tests/test-config.json and the regression skips when absent.
This commit is contained in:
1 parent
c825f40213
commit
82feb78b0f
14 files changed
+1540
-1
No files matched your search
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Posts.CincinnatiCIFiber
|
||||
{
|
||||
/// <summary>
|
||||
/// One contour (pierce + lead-in + cut path) reduced to sheet-absolute
|
||||
/// moves, ready to emit as a TF5200 N-label block.
|
||||
/// </summary>
|
||||
public sealed class CIFiberContour
|
||||
{
|
||||
/// <summary>Pierce point (sheet-absolute) — rapid target.</summary>
|
||||
public Vector Pierce { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lead-in move to the contour start (sheet-absolute endpoint), or
|
||||
/// null when the contour begins cutting immediately at the pierce.
|
||||
/// Per TF5200 §13.2.4.1 the first motion block after G41/G42 selection
|
||||
/// must be LINEAR, so callers must reject contours whose lead-in is an
|
||||
/// arc unless the config tolerates it.
|
||||
/// </summary>
|
||||
public Motion LeadIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional lead-in layer moves after the first (e.g. the arc that
|
||||
/// follows CleanHoleLeadIn's line). Emitted before the cut layer turns
|
||||
/// on, like the sample's lead block.
|
||||
/// </summary>
|
||||
public List<Motion> LeadInExtra { get; } = new();
|
||||
|
||||
/// <summary>Cutting moves (sheet-absolute) executed after the cut layer turns on.</summary>
|
||||
public List<Motion> Cuts { get; } = new();
|
||||
|
||||
/// <summary>True when the contour is an external perimeter (exterior).</summary>
|
||||
public bool IsExterior { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts cut contours for one part in sheet coordinates.
|
||||
/// Circular holes arrive as <see cref="SubProgramCall"/>s in an incremental
|
||||
/// hole-local program; they are flattened here to sheet-absolute moves the
|
||||
/// same way <see cref="NestPolylineExtractor"/> walks them for HPGL output.
|
||||
/// Moves with <see cref="Motion.Suppressed"/> are skipped.
|
||||
/// </summary>
|
||||
public static class CIFiberContourExtractor
|
||||
{
|
||||
public static List<CIFiberContour> Extract(Part part)
|
||||
{
|
||||
if (part == null)
|
||||
throw new ArgumentNullException(nameof(part));
|
||||
|
||||
var contours = new List<CIFiberContour>();
|
||||
Flatten(part.Program, part.Location, Vector.Zero, contours);
|
||||
return contours;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walk one program whose coordinates are relative to (partLocation +
|
||||
/// callOffset). Sub-program calls recurse with the extra offset.
|
||||
/// A contour starts at each rapid move; moves before the first rapid
|
||||
/// form a contour whose pierce is the last move's endpoint (closed-loop
|
||||
/// continuation), matching FeatureUtils' synthetic-rapid convention.
|
||||
/// </summary>
|
||||
private static void Flatten(
|
||||
Program program,
|
||||
Vector partLocation,
|
||||
Vector subOffset,
|
||||
List<CIFiberContour> contours
|
||||
)
|
||||
{
|
||||
if (program == null)
|
||||
return;
|
||||
|
||||
var origin = partLocation + subOffset;
|
||||
|
||||
// Work on a clone when conversion is needed so caller programs are
|
||||
// never mutated (the CI sample inlines absolute moves).
|
||||
var codes = program.Codes;
|
||||
if (program.Mode == Mode.Incremental)
|
||||
{
|
||||
var clone = (Program)program.Clone();
|
||||
clone.Mode = Mode.Absolute;
|
||||
codes = clone.Codes;
|
||||
}
|
||||
|
||||
CIFiberContour current = null;
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (current != null && (current.LeadIn != null || current.Cuts.Count > 0))
|
||||
contours.Add(current);
|
||||
current = null;
|
||||
}
|
||||
|
||||
foreach (var code in codes)
|
||||
{
|
||||
if (code is Motion suppressed && suppressed.Suppressed)
|
||||
continue;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case RapidMove rapid:
|
||||
Flush();
|
||||
current = new CIFiberContour { Pierce = rapid.EndPoint + origin };
|
||||
break;
|
||||
|
||||
case SubProgramCall call:
|
||||
// A hole call is its own contour: flatten into a temp
|
||||
// list, then merge as one contour anchored at the call
|
||||
// offset.
|
||||
Flush();
|
||||
var sub = new List<CIFiberContour>();
|
||||
Flatten(call.Program, partLocation, subOffset + call.Offset, sub);
|
||||
contours.AddRange(sub);
|
||||
break;
|
||||
|
||||
case Motion motion:
|
||||
if (current == null)
|
||||
{
|
||||
// Contour with no explicit rapid (program starts
|
||||
// mid-contour): in absolute mode the implicit
|
||||
// start position is the frame origin, which is the
|
||||
// closure point of a closed contour = the pierce.
|
||||
current = new CIFiberContour { Pierce = origin };
|
||||
}
|
||||
|
||||
var absolute = (Motion)motion.Clone();
|
||||
absolute.Offset(origin);
|
||||
|
||||
var layer = LayerOf(motion);
|
||||
if (layer == LayerType.Leadin)
|
||||
{
|
||||
// First lead-in move opens the lead block; further
|
||||
// lead-in moves (CleanHole line+arc) extend it.
|
||||
if (current.LeadIn == null)
|
||||
current.LeadIn = absolute;
|
||||
else
|
||||
current.LeadInExtra.Add(absolute);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cut, Leadout and Display all cut: OpenNest tags
|
||||
// contour moves Display by default (see
|
||||
// ContourCuttingStrategy.ConvertShapeToMoves),
|
||||
// only Scribe is reliably a mark. Scribe contours
|
||||
// are dropped by the writer when SkipScribe is on.
|
||||
current.Cuts.Add(absolute);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
private static LayerType LayerOf(Motion motion)
|
||||
{
|
||||
return motion switch
|
||||
{
|
||||
LinearMove l => l.Layer,
|
||||
ArcMove a => a.Layer,
|
||||
_ => LayerType.Cut,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user