Adds ApplySingle to ContourCuttingStrategy that applies lead-in/out to
only the contour containing the clicked entity, emitting other contours
as raw geometry. Also adds ApplySingleLeadIn wrapper to Part.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add AutoTabMinSize and AutoTabMaxSize properties to enable automatic tab
assignment based on part size. Update CuttingParametersSerializer for
round-trip serialization and add tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shape.Area() returns Math.Abs(signedArea), so DetermineWinding always
detected CCW regardless of actual winding. Use ToPolygon().RotationDirection()
which uses the signed area correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ComputeNormal assumed CW winding for all contours. For CCW-wound cutouts,
line normals pointed to the material side instead of scrap, placing lead-ins
on the wrong side. Now accepts a winding parameter: lines flip the normal
for CCW winding, and arcs flip when arc direction differs from contour
winding (concave feature detection).
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>
Emit variable definitions before G-code in program text entries and use
\$varName syntax for coordinate fields that have VariableRefs, so programs
round-trip through NestWriter → NestReader without losing variable information.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProgramReader now supports G-code user variables with a two-pass
approach: first pass collects variable definitions (name = expression
[inline] [global]) and evaluates them via topological sort and
ExpressionEvaluator; second pass parses G-code lines with $name
substitution and VariableRef tracking on motion and feedrate objects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Dictionary<string,string> VariableRefs to Motion (cleared on Rotate/Offset) and string VariableRef to Feedrate, with deep-copy Clone() support, so post processors can emit variable references instead of literal coordinate values.
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>
Adds immutable VariableDefinition record to OpenNest.CNC with name,
expression, resolved value, inline, and global flags. Fixes namespace
collision in PatternTilerTests and PolygonHelperTests caused by the new
OpenNest.Tests.CNC namespace.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch gcodeEditor from TextBox to RichTextBox and colorize G-code
tokens: rapids (amber), linear cuts (green), arcs (blue), comments
(dim gray), and mode codes (purple).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the Apply button and OnApplyClicked handler since the gcode
editor is now read-only. Add contour label comments (e.g. "; Hole 1
(CCW)") to the formatted gcode output so users can see which feature
each group of codes belongs to.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only include cut-layer entities when building the ShapeProfile for lead-in
placement, instead of removing just scribe entities. This prevents display,
lead-in, and lead-out geometry from interfering with contour detection.
Co-Authored-By: Claude Opus 4.6 (1M context) <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>
Move 43 root-level test files into feature-specific subdirectories
mirroring the main codebase structure: Geometry, Fill, BestFit, CutOffs,
CuttingStrategy, Engine, IO. Update namespaces to match folder paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch colorsList from CheckedListBox (which silently ignores owner
draw) to a plain ListBox with manual checkbox, color swatch, and hex
label rendering. Clone entities in ProgramEditorControl preview to
avoid mutating originals. Remove contour color application from
CadConverterForm. Fix struct null comparison warning in SplitDrawingForm.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve lead-in points by walking backward through cutting order (from
perimeter outward) so each lead-in faces the next cutout to be cut
rather than pointing back at the previous lead-out. Extract EmitContour
and EmitScribeContours to eliminate duplicated cutout/perimeter logic.
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>
Priority-based snapping: when the cursor is within 10px of an entity
endpoint or midpoint, snaps to it instead of the nearest contour point.
Diamond marker (endpoint) or triangle marker (midpoint) replaces the
lime dot to indicate active snap. Also refactors OnPaint into focused
helper methods and adds Arc.MidPoint().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move Assign/Place/Remove Lead-ins from EditNestForm toolstrip to the
Plate menu in the main menubar. Add nest-wide Assign/Remove Lead-ins
to the Nest menu for applying to all plates at once.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace Program.ToString() with Cincinnati-style formatter (spaced
coordinates, blank lines between contours, trailing zero suppression)
- Fix empty Program tab when switching files while on the tab by
loading immediately instead of only marking stale
- Set contour-type colors on entities at load time and restore base
colors before selection highlight to prevent color bleed to CAD view
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up the Apply button to parse the G-code text back into a Program,
rebuild contours via ConvertProgram/ShapeBuilder/ContourInfo, and fire
ProgramChanged so callers receive the updated program.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parts were not redrawn after AssignLeadIns because their LayoutPart
graphics paths were stale.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover assign, remove, re-assign, multiple rotations, and external
HasManualLeadIns scenarios to verify rotation is preserved throughout
the lead-in lifecycle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Track preLeadInRotation when parts are rotated so lead-in removal
can restore the correct rotation. Remove stale HasManualLeadIns and
LeadInsLocked deserialization from NestReader since these flags are
transient state, not persisted data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove LeadInsLocked guard so parts can be re-selected for lead-in
re-placement. Change preview color from yellow to magenta for better
visibility against the cyan contour highlight.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CCW arcs (e.g. the top of a U-slot) had the radial normal pointing
into the part material instead of into the scrap. This caused the
lead-in preview to flip sides on concave features.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>