Add LoadPlateOptions() method to AutoNestForm that restores saved plate
options and salvage rate from the Nest. Call this method in
RunAutoNest_Click when opening the dialog if saved options exist, and save
settings back to Nest after dialog completion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PlateOptions and SalvageRate properties to the Nest class and
round-trip them through NestWriter/NestReader via a new PlateOptionDto.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When "Optimize plate size" is enabled in AutoNestForm, NestSinglePlateAsync
calls PlateOptimizer.Optimize instead of engine.Nest, trying multiple plate
sizes and resizing the plate to the winning option.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tries each candidate plate size via the nesting engine, compares results
by part count then net cost (accounting for salvage credit on remnant
material), and returns the best option.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add colored left-edge bars (green=met, orange=short) to indicate nesting
quantity status. Replace blue selection highlight with a border outline.
Add toolbar toggle to hide fully nested drawings, auto-updating as parts
are placed or removed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enables a "move" workflow: clone parts to a new position, then
press Delete to remove the originals. Previously Delete just
cancelled the clone action.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strategies and fillers previously called NestEngineBase.ReportProgress
directly, each constructing ProgressReport structs with phase, plate
number, and work area manually. Some strategies (RectBestFit) reported
nothing at all. This made progress updates inconsistent and flakey.
Add FillContext.ReportProgress(parts, description) as the single
standard method for intermediate progress. RunPipeline sets ActivePhase
before each strategy, and the context handles common fields. Lower-level
fillers (PairFiller, FillExtents, StripeFiller) now accept an
Action<List<Part>, string> callback instead of raw IProgress, removing
their coupling to NestEngineBase and ProgressReport.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ActionLeadIn.DisconnectEvents() nulled selectedLayoutPart without first
setting IsSelected = false, leaving the part permanently rendered in the
selection color (transparent blue) after switching actions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BuildOffsets had Width and Length swapped after the Box axis correction
in c5943e2. Horizontal pushes used Length (X) for perpendicular sweep
and Width (Y) for push start — backwards. This caused part2 to start
inside part1's footprint, producing overlapping best-fit pairs.
Added regression test that verifies no kept best-fit pairs overlap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ObservableList.Remove fired ItemRemoved even when the item wasn't in
the list, causing Plate to decrement Quantity.Nested for clone preview
parts that were never added — producing -1 counts. Delete in PlateView
now cancels ActionClone instead of trying to remove its preview parts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Box constructor and derived properties (Right, Top, Center, Translate, Offset)
had Width and Length swapped — Length is X axis, Width is Y axis. Corrected
across Core geometry, plate bounding box, rectangle packing, fill algorithms,
tests, and UI renderers.
Added FillSpiral with center remnant detection and recursive FillBest on
the gap between the 4 spiral quadrants. RectFill.FillBest now compares
spiral+center vs full best-fit fairly. BestCombination returns a
CombinationResult record instead of out params.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add general-purpose ShowSidePanel/HideSidePanel to EditNestForm
- CuttingPanel uses Dock.Top layout so collapsible panels reflow
- Add loop selection step: click contour to lock before placing lead-in
- Stay on selected part after placing a lead-in
- Delete unused LeadInToolWindow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove CuttingParametersForm modal dialog. PlaceLeadIn_Click,
AssignLeadIns_Click, and AssignLeadInsAllPlates now initialize
cutting parameters from saved settings or defaults instead of
showing a dialog. The CuttingPanel tool window (in LeadInToolWindow)
replaces the form for interactive parameter editing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
When TabsEnabled is set, trims the end of each contour using a circle
centered at the lead-in point with radius equal to the tab size. The
uncut gap between the trim point and the contour start keeps the part
connected to the sheet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The offset direction (start→pierce) is reversed from the approach
direction (pierce→start), so the old formula produced 180°−angle
instead of the requested angle. Invisible at the 90° default but
caused 45° to render as 135°.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a "Draw Cut Direction" toggle to the View menu that draws small
arrowheads along cutting paths to indicate the direction of travel.
Arrows are placed on both linear and arc moves, spaced ~60px apart,
and correctly follow CW/CCW arc tangents.
Extract all rendering methods (~660 lines) from PlateView into a new
PlateRenderer class, reducing PlateView from 1640 to 979 lines.
PlateView retains input handling, selection, zoom, and part management.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scribe/etch lines were being treated as cut contours by
ContourCuttingStrategy, receiving lead-ins and kerf compensation.
Now they are separated before ShapeProfile construction and emitted
as plain moves with LayerType.Scribe preserved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The approach rapid from sheet origin was drawing to part.Location (the
program coordinate origin) then a second rapid to the actual first
pierce point. This created a dog-leg through the part origin instead
of a single straight rapid to the lead-in. Also fixed PlateProcessor
using the original program's start point instead of the processed one
when the cutting strategy is applied on-the-fly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clears all manual lead-ins from every part on the active plate and
rebuilds the layout graphics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Save/restore cutting parameters as JSON in user settings so values
survive between sessions. Add pierce clearance numeric input to the
CuttingParametersForm.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scales down lead-ins that would place the pierce point too close to the
opposite wall of small holes. Uses quadratic solve to find the maximum
safe distance inside a clearance-reduced radius. Adds Scale() method to
all LeadIn types and applies clamping in both the strategy and the
interactive preview.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three issues caused incorrect rendering after lead-in application:
- Rapid move entities from ToGeometry() were included in ShapeProfile
contour detection, turning traversal paths into cutting moves
- Program created with Mode.Incremental default made the absolute-to-
incremental conversion a no-op, leaving coordinates unconverted
- AddProgramSplit didn't call StartFigure() at rapid moves, causing
GraphicsPath to draw implicit connecting lines between contours
- Part.Rotation returned 0 from the new program instead of the actual
rotation, displacing the sequence label on rotated parts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure the cutting parameters dialog with separate Lead-In and
Lead-Out GroupBoxes per tab, exposing editable length/angle/radius
fields for lead-outs (previously hardcoded). Add Tabs section with
enable checkbox and width control. Also fix lead-in/lead-out angle
calculations and convert cutting strategy output to incremental mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a text-only toolbar button to the Plates tab that opens the
CuttingParametersForm, saves the chosen parameters on the plate, and
runs LeadInAssigner with LeftSideSequencer to auto-assign lead-ins.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add GetGraphicsPaths/AddProgramSplit to GraphicsHelper that builds separate
GraphicsPath objects for cut vs lead-in/lead-out codes, skipping suppressed
codes. Update LayoutPart to use split paths when HasManualLeadIns is set,
drawing lead-in geometry in yellow regardless of selection state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds LayerType.Leadout to all LinearMove and ArcMove instances produced
by LineLeadOut and ArcLeadOut Generate() methods.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds LayerType.Leadin to all LinearMove and ArcMove instances produced
by LineLeadIn, ArcLeadIn, LineArcLeadIn, CleanHoleLeadIn, and
LineLineLeadIn Generate() methods, plus tests covering all subclasses.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Suppressed bool to the Motion base class so LinearMove, ArcMove,
and RapidMove can be flagged for skip during rendering and post-processing
when they fall within a tab gap. Clone() updated on all three subclasses
to preserve the flag. Covered by new MotionSuppressedTests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>