CincinnatiSheetWriter.WriteHoleSubprogramCall emitted
`M98 P<num> X<x> Y<y>`, but per manual §3.98 ("M98 SUB-PROGRAM CALL
WITH NO ARGUMENTS") M98 takes only P and L — the X/Y had no defined
meaning to the control. The intent was to position the sub-program at
the hole center, which is what G52 is for per §1.52 ("local work
coordinate system") and which explicitly does not move the nozzle.
Emit the documented G52 bracket instead:
G52 X<hole.x> Y<hole.y>
M98 P<holeSubNum>
G52 X0 Y0
The hole sub-program is authored in hole-local coordinates, so its
first rapid (the lead-in to the pierce point) resolves to the absolute
pierce under the G52 shift and moves the tool directly there from the
previous feature's end — no phantom rapid to the hole center.
Also add docs/cincinnati-post-output.md as the reference for the full
post output format, with every emitted G/M code cross-referenced to
the Cincinnati programming manual. Un-ignore docs/ (docs/superpowers/
stays ignored) and track the PDF manual alongside the reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SubProgramCalls are now treated as standalone features in the Cincinnati
post-processor. SplitByRapids emits them as single-element features
instead of splitting on rapids within sub-programs. A nest-level hole
sub-program registry deduplicates by content and assigns post numbers.
Sheet writers emit M98 calls with X/Y offsets for hole features, and
hole sub-program definitions are written after part sub-programs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of emitting separate M98 calls per identical sheet, use the L
(loop count) parameter so the operator can adjust quantity at the control.
M50 pallet exchange moves inside the sheet subprogram so each L iteration
gets its own exchange cycle. GOTO targets now correspond to layout groups.
Also fixes sheet name comment outputting dimensions in wrong order.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rapid traversing back to origin over a sheet of freshly cut parts risks
collisions with tipped or warped pieces. Removed from both the sheet
footer and part subprogram endings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cutoff features now substitute plate-edge coordinates with #SheetWidthVariable
and #SheetLengthVariable references. Vertical cutoffs at Y=plate_width emit
Y#110, horizontal cutoffs at X=plate_length emit X#111. Segmented cutoffs
only substitute the edge coordinate, interior segment endpoints stay literal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When programs have user-defined variables, the Cincinnati post now:
- Assigns numbered machine variables (#200, #201, etc.) to non-inline variables
- Emits declarations like #200=48.0 (SHEET WIDTH) in the variable declaration subprogram
- Emits X#200 instead of X48.0 in coordinates that have VariableRefs
- Handles global variables (shared number across drawings) vs local (per-drawing number)
- Inline variables emit the literal value as before
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also set ContinueOnError=true on Cincinnati's post-build copy to prevent
the running WinForms app from blocking test builds via a file lock.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Material and thickness are properties of the nest (all plates share the
same material/gauge), not individual plates. This moves them to the Nest
class, removes them from Plate and PlateSettings, and updates the UI so
EditNestInfoForm has a material field while EditPlateForm no longer shows
thickness. The nest file format gains top-level thickness/material fields
with backward-compatible reading from PlateDefaults for old files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CAD converter and BOM import were stripping the leading RapidMove
after normalizing program coordinates to origin. This left programs
starting with a LinearMove, causing the post-processor to use that
endpoint as the pierce point — making the first contour edge zero-length
and losing the closing segment (e.g. the bottom line on curved parts).
Root cause: CadConverterForm.GetDrawings(), OnSplitClicked(), and
BomImportForm all called pgm.Codes.RemoveAt(0) after offsetting the
rapid to origin. The rapid at (0,0) is a harmless no-op that marks the
contour start point for downstream processing.
Also adds EnsureLeadingRapid() safety net in the Cincinnati post for
existing nest files that already have the rapid stripped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert programs to absolute mode before extracting features for
Cincinnati post output, fixing incorrect coordinates when programs
are stored in incremental mode. Also ensure G89 library names
always end with .lib extension.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Decorate all properties with [Category], [DisplayName], and [Description]
attributes for use in the WinForms PropertyGrid config dialog. Reorder
properties to match category grouping (1. Output through B. Libraries)
and replace property-level XML doc comments with the attribute descriptions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- Add radius-based arc feedrate calculation (Variables/Percentages modes)
with configurable radius ranges (#123/#124/#125 or inline expressions)
- Fix arc distance in SpeedClassifier using actual arc length instead of
chord length (full circles previously computed as zero)
- Fix G89 P spacing: P now adjacent to filename per CL-707 manual syntax
- Add lead-out feedrate support (#129) and arc lead-in feedrate (#127)
- Fix pallet exchange: StartAndEnd emits M50 in preamble + last sheet only
- Add G121 Smart Rapids emission when UseSmartRapids is enabled
- Add G90 absolute mode to main program preamble alongside G20/G21
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate duplicated static methods (SplitFeatures, ComputeCutDistance,
IsFeatureEtch, feature ordering) from CincinnatiSheetWriter and
CincinnatiPartSubprogramWriter into a shared FeatureUtils class. Move
inline sub-program registry building from Post() into
CincinnatiPartSubprogramWriter.BuildRegistry().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add MaterialLibraryResolver for Cincinnati post processor to resolve
G89 library files from material/thickness/gas configuration
- Add Nest.AssistGas property with serialization support in nest format
- Add etch library support with separate gas configuration
- Fix CutOff tests to match AwayFromOrigin default cut direction
- Fix plate info label not updating after ResizePlateToFitParts
- Add cutoff and remnants toolbar button icons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
G-code output was concatenated without spaces (e.g. N1005G0X1.4375Y-0.6562).
Now emits standard spacing (N1005 G0 X1.4375 Y-0.6562) across all motion
commands, line numbers, kerf comp, feedrates, M-codes, and comments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each unique part geometry (drawing + rotation) is written once as a
reusable sub-program called via M98, reducing output size for nests
with repeated parts. G92 coordinate repositioning handles per-instance
plate placement with restore after each call. Cut-offs remain inline.
Controlled by UsePartSubprograms (default false) and PartSubprogramStart
config properties.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The WinForms app's LoadPosts() scans Posts/ for IPostProcessor DLLs.
A build target copies the Cincinnati DLL there so it appears in the
Nest > Post menu automatically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move ProgramVariable and ProgramVariableManager from
OpenNest.Posts.Cincinnati to OpenNest.Core/CNC (namespace OpenNest.CNC)
so they can be used internally in nest programs
- Add parameterless constructor to CincinnatiPostProcessor that loads
config from a .json file next to the DLL (creates defaults on first run)
- Enums serialize as readable strings (e.g., "Inches", "ControllerSide")
- Config file: OpenNest.Posts.Cincinnati.json in the Posts/ directory
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use CultureInfo.InvariantCulture in CoordinateFormatter, SpeedClassifier,
and CincinnatiPreambleWriter to prevent locale-dependent G-code output
- Make CincinnatiPostConfig sealed per spec
- Fix SpeedClassifier.Classify threshold to >= (matching spec)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Orchestrates CincinnatiPreambleWriter and CincinnatiSheetWriter to produce
a complete Cincinnati CNC output file from a Nest; includes 4 integration tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>