SplitContainer wasn't sizing children correctly. Switched to the same
Panel(Dock.Left) + Splitter + Fill pattern used by SplitDrawingForm,
with explicit Size on all controls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the old DataGridView+TabControl layout with a sidebar containing
FileListControl and FilterPanel on the left, and EntityView with a detail
bar on the right. Adds drag-and-drop support, thread-safe parallel file
import, bend detection integration, and split-to-DXF workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a pluggable bend detection system in OpenNest.IO.Bending:
- IBendDetector takes CadDocument directly to preserve MText/layer/linetype info
- SolidWorksBendDetector finds lines on BEND layer with CENTER linetype and matches nearby MText notes
- BendDetectorRegistry auto-registers SolidWorks detector and supports AutoDetect
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces OpenNest.Core/Bending/ with Bend and BendDirection types as
the foundation for bend line detection. Includes 6 passing unit tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace polygon boolean clipping with direct entity splitting using
bounding box filtering and exact intersection math. Eliminates Clipper2
precision drift that caused contour gaps (0.0035") breaking area
calculation and ShapeBuilder chaining.
Also fixes SpikeGrooveSplit: spike depth is now grooveDepth + weldGap
(spike protrudes past groove), both V-shapes use same angle formula,
and weldGap no longer double-subtracted from tip depth.
SplitDrawingForm: fix parameter mapping (GrooveDepth direct from nud,
not inflated), remove redundant Spike Depth display, add feature
contour preview and trimmed split lines at feature positions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ToLine() to SplitLine and create SplitLineIntersect static class with
FindIntersection, CrossesSplitLine, and SideOf methods for testing entity
intersections against split lines. These helpers support the upcoming
Clipper2-free DrawingSplitter rewrite.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace raw Panel with EntityView (via SplitPreview subclass) for proper
zoom-to-point, middle-button pan, and double-buffered rendering
- Add draggable handles for tab/spike positions along split lines; positions
flow through to WeldGapTabSplit and SpikeGrooveSplit via SplitLine.FeaturePositions
- Fix OK/Cancel buttons hidden off-screen by putting them in a bottom-docked panel
- Fix DrawControl not invalidating on resize
- Swap plate Width/Length label order, default edge spacing to 0.5
- Rename tab labels: Tab Width→Tab Length, Tab Height→Weld Gap, default count 2
- Spike depth now calculated (read-only), groove depth means positioning depth
beyond spike tip (default 0.125), converted to total depth internally
- Set entity layers visible so EntityView renders them
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- SpikeParameters: added GrooveDepth (how deep groove cuts into
receiving part) and SpikeWeldGap (gap between spike tip and groove)
- SpikeGrooveSplit: groove uses its own depth (wider/deeper than spike),
spike tip stops short by weld gap amount
- UI: added Groove Depth and Weld Gap fields to spike parameters panel
- Changed default pair count to 2 (one near each end)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When user selects Vertical Only or Horizontal Only, use the smaller
plate dimension as the constraint for that axis. Previously it filtered
results from FitToPlate which produced no lines when the part already
fit in the plate width but not height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a "Split Axis" dropdown (Auto / Vertical Only / Horizontal Only)
to the Fit to Plate options so users can control which direction the
part is split when weld direction matters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rapid-layer entities (connecting lines between cutouts and perimeter)
were being rendered in the split preview. Filter them out, matching
the same pattern used in DrawingSplitter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a Split button column to the DXF converter grid. Clicking it
builds a temporary Drawing from the item's visible entities and opens
SplitDrawingForm; if the user confirms, the split results replace the
original item in GetDrawings().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the main drawing splitting algorithm that orchestrates splitting
a Drawing into multiple pieces along split lines using Clipper2 polygon
clipping. After clipping, recovers original arcs by matching clipped edges
back to perimeter entities, stitches in feature edges from ISplitFeature
where polygon edges lie on split lines, and normalizes each piece's origin.
Key fix from plan: filters rapid-layer entities before ShapeProfile
construction so cutouts are properly separated from perimeters.
Includes 7 integration tests covering vertical/horizontal splits, three-way
splits, property copying, origin normalization, cutout assignment, and
grid (cross) splits.
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>
PolygonHelper.ExtractPerimeterPolygon always used OffsetSide.Right
assuming CCW winding, but DXF imports can produce CW winding. This
caused the spacing polygon to shrink inward instead of expanding
outward, making parts overlap during nesting.
Now detects winding direction via polygon signed area and selects
the correct OffsetSide accordingly.
Also adds save_nest MCP tool and a BOM-to-nest builder utility
(tools/NestBuilder) for batch-creating nest files from Excel BOMs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cut-off parts use absolute coordinates in their programs, causing
Program.BoundingBox() to span from the origin to the cut-off position.
This made cut-offs invisible to GetLargestBoxVertically/Horizontally
since the oversized box straddled the cursor instead of acting as a
boundary. Derive thin obstacle boxes directly from CutOff definitions
and apply PartSpacing offset so fills respect spacing from cut lines.
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>
Load IPostProcessor plugin DLLs from Posts/ directory (same convention
as the WinForms app) and run them after nesting via --post <name>.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous default of 0.125 was too large for typical use, causing
cut-off lines to be pushed unnecessarily far from parts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds project dependency in .sln and project reference in OpenNest.csproj
so the Cincinnati post processor DLL builds before the main app, ensuring
it's always available in the Posts/ directory.
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>
Select cut-offs by clicking their lines instead of a grip point.
Drag is axis-constrained with live regeneration during movement.
Selected cut-off highlighted with bright blue 3.5px line.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Polygon.OffsetEntity now computes proper miter-join offsets using edge
normals and winding direction, with self-intersection cleanup. CutOff
exclusion zones use geometric perimeter offset instead of scalar padding,
giving uniform clearance around parts regardless of cut angle.
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>
- Regenerate cut-off programs after RotateSelectedParts so cut lines
update when parts are rotated, not just moved
- Change default CutDirection from TowardOrigin to AwayFromOrigin so
cuts start at the origin axis
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract MakePoint/AxisBounds/CrossAxisBounds helpers in CutOff to
eliminate repeated axis-dependent branching
- Simplify BuildProgram loop from 4 code paths to 2
- Use static EmptyExclusions to avoid per-part list allocations
- Fix double event subscription in ActionCutOff constructor
- Dispose debounce timer in DisconnectEvents
- Remove redundant BuildPerimeterCache call in OnMouseDown
- Extract TotalCutLength test helper, remove duplicate test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace CutOff.BuildPerimeterCache (List<Shape>) with Plate.BuildPerimeterCache
(Dictionary<Part, Entity>) throughout. Consolidate two Regenerate overloads into
a single method with optional cache parameter. Fix Shape intersection bug where
non-intersecting entities added spurious Vector.Zero points to results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The NFP engine was a thin wrapper that delegated all single-drawing
operations to DefaultNestEngine. Remove the engine and its registry
entry to reduce dead code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RemnantFiller: add placed parts as a single envelope obstacle instead
of individual bounding boxes to prevent the next drawing from filling
into inter-row gaps. Remove the topmost bounding-box part to create a
clean rectangular boundary.
PairsFillStrategy: guard against recursive invocation — remnant fills
within PairFiller create a new engine that runs the full pipeline,
which would invoke PairsFillStrategy again causing deep recursion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap the ComboBox drawing selector with the same DrawingListBox control
used in EditNestForm, placed in a resizable SplitContainer. Add selection
highlighting and HideQuantity option to DrawingListBox. Show a centered
loading message while computing, and allow switching drawings mid-compute.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test drawings used CCW winding, causing OffsetSide.Left to produce
inward offsets. The BestFit pipeline then positioned pairs so actual
shapes overlapped, failing all 1232 candidates. Changed to CW winding
to match CNC convention where OffsetSide.Left = outward.
Also fixed EdgeStartSequencer test: centerPart at (25,55) was only 4.5
from the top edge (plate Y=60), closer than midPart at (10,10). Moved
to (25,25) for correct ordering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add "Sheet Cut-Off" menu item to the Plate menu that activates
ActionCutOff placement mode on the active PlateView.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a new action that lets users place cut-off lines on plates by
clicking. The action auto-detects horizontal vs vertical axis based on
mouse proximity to plate edges, shows a dashed preview line that
follows the cursor, and adds the cut-off on left click. Pressing
Escape returns to selection mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add DrawCutOffs and DrawCutOffGrip methods to PlateView for rendering
cut-off lines and selection grip handles. Includes CutOffSettings and
SelectedCutOff properties, plus GetCutOffAtPoint for hit-testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make quantity trimming direction-aware: DefaultNestEngine uses TrimAxis
(virtual property on NestEngineBase) so HorizontalRemnantEngine removes
topmost parts instead of rightmost. Rename ShrinkAxis.Height → Length
for consistency with Width/Length naming used throughout the codebase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CutOff computes cut segments along a vertical or horizontal axis,
excluding zones around existing parts with configurable clearance.
CutOffSettings controls part clearance, overtravel, minimum segment
length, and cut direction (toward/away from origin).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Height shrink now uses HorizontalRemnantEngine (minimizes Y-extent)
and width shrink uses VerticalRemnantEngine (minimizes X-extent).
IterativeShrinkFiller accepts an optional widthFillFunc so each
shrink axis can use a different fill engine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Evaluate pair candidates in parallel batches instead of sequentially
- Add GridDedup to skip duplicate pattern/direction/workArea combos
across PairFiller and StripeFiller strategies
- Replace crude 30% remnant area estimate with L-shaped geometry
calculation using actual grid extents and max utilization
- Move FillStrategyRegistry.SetEnabled to outer evaluation loop
to avoid repeated enable/disable per remnant fill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RotateSelectedParts was calling Program.Rotate() directly on shared
Program instances, bypassing Part's copy-on-write (EnsureOwnedProgram).
Parts created via CloneAtOffset share the same Program, so rotating one
part would rotate all parts with the same reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace stored property setters (BestPartCount, BestDensity, NestedWidth,
NestedLength, NestedArea) with computed properties that derive values from
BestParts, with a lazy cache invalidated on setter. Add internal
ProgressReport struct to replace the 7-parameter ReportProgress signature.
Update all 13 callsites and AccumulatingProgress. Delete FormatPhaseName
in favor of NestPhase.ShortName() extension.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove redundant early-return branches and unify loop body — Floor(remaining/length2) already returns 0 when remaining < length2, so both branches collapse into one. 14 tests cover all edge cases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds methods to permanently disable/enable strategies by name. Disabled
strategies remain registered but are excluded from the default pipeline.
SetEnabled (used for remnant fills) takes precedence over the disabled
set, so explicit overrides still work.
Pipeline test now checks against active strategy count dynamically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PairFiller previously only filled the main grid with pair patterns,
leaving narrow waste strips unfilled. Row/Column strategies filled
their remnants, winning on count despite worse base grids.
Now PairFiller evaluates grid+remnant together for each angle/direction
combination, picking the best total. Uses a two-phase approach: fast
grid evaluation first, then remnant filling only for grids within
striking distance of the current best. Remnant results are cached
via FillResultCache.
Constructor now takes Plate (needed to create remnant engine).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract ConvergeFromAngle to deduplicate ~40 lines shared between
ConvergeStripeAngle and ConvergeStripeAngleShrink. Reduce BuildGrid
from 7 to 4 params and FillRemnant from 6 to 2 by reading context
fields directly. Remove unused angle parameter from FillRemnant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two new engine classes subclassing DefaultNestEngine that override
CreateComparer, PreferredDirection, and BuildAngles to optimize for
preserving side remnants. Both registered in NestEngineRegistry and
covered by 6 integration tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PairFiller now accepts an optional IFillComparer (defaulting to
DefaultFillComparer) and uses it in EvaluateCandidates and
EvaluateCandidate/FillPattern instead of raw FillScore comparisons.
PairsFillStrategy passes context.Policy?.Comparer through.
StripeFiller derives _comparer from FillContext.Policy in its
constructor and uses it in Fill() instead of FillScore comparisons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FillContext gains a Policy property (init-only) carrying the IFillComparer
- DefaultNestEngine.Fill sets Policy = BuildPolicy() on every context
- RunPipeline now uses context.Policy.Comparer.IsBetter instead of IsBetterFill
- RunPipeline promoted to protected virtual so subclasses can override
- BuildAngles/RecordProductiveAngles overrides delegate to angleBuilder
- RunPipeline calls virtual BuildAngles/RecordProductiveAngles instead of angleBuilder directly
- TODO comment added in group-fill overload for Task 6 Comparer pass-through
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add virtual comparer, direction, and angle-building hooks to NestEngineBase
so subclasses can override scoring and direction policy. Rewire IsBetterFill
to delegate to the comparer instead of calling FillScore directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements two IFillComparer strategies that preserve axis-aligned remnants:
VerticalRemnantComparer minimizes X-extent, HorizontalRemnantComparer minimizes
Y-extent, both using a count > extent > density tiebreak chain. Includes 12
unit tests covering all tiebreak levels and null-guard cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extracts the fill result scoring contract into IFillComparer with a DefaultFillComparer implementation that preserves the existing count-then-density lexicographic ranking via FillScore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Convergence loop now uses FillLinear internally to measure actual
waste with geometry-aware spacing instead of bounding-box arithmetic
- Each candidate pair is tried in both Row and Column orientations to
find the shortest perpendicular dimension (more complete stripes)
- CompleteStripesOnly flag drops partial stripes; remnant strip is
filled by a full engine run (injected via CreateRemnantEngine)
- ConvergeStripeAngleShrink tries N+1 narrower pairs as alternative
- FillResultCache avoids redundant engine runs on same-sized remnants
- CLAUDE.md: note to not commit specs/plans
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The convergence loop now ensures the pair starts with its short side
parallel to the primary axis, maximizing the number of pairs that fit.
Also adds ConvergeStripeAngleShrink to try N+1 narrower pairs, and
evaluates both expand and shrink results to pick the better grid.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>