Commit Graph
100 Commits
Author SHA1 Message Date
ajandClaude Opus 4.6 6916f5ecca fix: replace SplitContainer with Panel+Splitter layout in CadConverterForm
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>
2026-03-24 21:30:48 -04:00
ajandClaude Sonnet 4.6 e1bcb7498f feat: add SplitDxfWriter for split DXF output with bend data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:43:40 -04:00
ajandClaude Opus 4.6 a7f8972722 feat: add bend line rendering and grain warning in PlateView
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:39:29 -04:00
ajandClaude Opus 4.6 6d1a3f5e2c feat: rebuild CadConverterForm with sidebar+preview layout
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>
2026-03-24 20:36:20 -04:00
ajandClaude Sonnet 4.6 52eca5f5c2 feat: add FilterPanel with layers, colors, linetypes, bend lines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:32:27 -04:00
aj 3bce45be5f feat: add FileListControl owner-drawn file list 2026-03-24 20:30:32 -04:00
ajandClaude Sonnet 4.6 3f0a4c57b5 feat: add CollapsiblePanel reusable control
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:28:49 -04:00
ajandClaude Sonnet 4.6 ededc7b6b4 feat: draw entities in actual DXF colors with pen caching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:27:46 -04:00
ajandClaude Sonnet 4.6 5f74afeda1 feat: add IBendDetector interface, SolidWorks implementation, and registry
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>
2026-03-24 20:26:27 -04:00
ajandClaude Sonnet 4.6 574a8f2c38 feat: add DxfImporter.Import returning entities + CadDocument
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:23:53 -04:00
ajandClaude Sonnet 4.6 dd2892a9fe feat: serialize/deserialize bends in nest file format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:22:55 -04:00
ajandClaude Sonnet 4.6 7056f8816f feat: add Bends property to Drawing, GrainAngle to Plate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:19:53 -04:00
ajandClaude Sonnet 4.6 c2a470f79c feat: add Bend domain model and BendDirection enum to OpenNest.Core
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>
2026-03-24 20:18:52 -04:00
ajandClaude Opus 4.6 39f8a79cfd feat: replace Clipper2 with direct entity splitting in DrawingSplitter
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>
2026-03-24 18:19:47 -04:00
ajandClaude Opus 4.6 df18b72881 feat: add SplitLineIntersect helper for entity-splitline intersection
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>
2026-03-24 18:04:21 -04:00
ajandClaude Opus 4.6 cd8adc97d6 feat: overhaul SplitDrawingForm — EntityView, draggable feature handles, UI fixes
- 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>
2026-03-24 14:26:43 -04:00
ajandClaude Opus 4.6 ba7aa39941 feat: add groove depth and weld gap options to spike-groove split
- 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>
2026-03-24 13:30:39 -04:00
aj 5d93ddb2c4 fix: swap plate dimension labels — Length first, Width second 2026-03-24 13:21:11 -04:00
aj 15b2043048 fix: rename Plate Height label to Plate Length 2026-03-24 13:20:09 -04:00
ajandClaude Opus 4.6 aa8b6f3d9e fix: use plate size correctly when split axis is forced
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>
2026-03-24 13:18:57 -04:00
ajandClaude Opus 4.6 3686d074e6 feat: add split axis selector to auto-fit options
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>
2026-03-24 13:14:54 -04:00
ajandClaude Opus 4.6 8f1a3fb6b7 fix: filter rapid-move entities from SplitDrawingForm preview
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>
2026-03-24 13:07:37 -04:00
aj 60ce297d6a feat: add split drawing feature for oversized parts 2026-03-24 12:47:39 -04:00
aj addd7acc3c docs: add split drawing feature to architecture documentation 2026-03-24 12:21:45 -04:00
ajandClaude Sonnet 4.6 d91ffccfa3 feat: add Split button to CadConverterForm to open SplitDrawingForm
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>
2026-03-24 12:20:22 -04:00
ajandClaude Opus 4.6 adb8ed12d7 feat: add SplitDrawingForm UI dialog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:18:31 -04:00
ajandClaude Opus 4.6 4acd8b8bad feat: add DrawingSplitter core split pipeline
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>
2026-03-24 12:13:37 -04:00
ajandClaude Sonnet 4.6 d7b095cf2d feat: add AutoSplitCalculator for fit-to-plate and split-by-count
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 12:03:18 -04:00
ajandClaude Sonnet 4.6 499e0425b5 feat: add SpikeGrooveSplit implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 12:01:40 -04:00
ajandClaude Sonnet 4.6 c2c3e23024 feat: add WeldGapTabSplit implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 11:59:45 -04:00
ajandClaude Sonnet 4.6 5afb311ac7 feat: add ISplitFeature interface and StraightSplit implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 11:58:00 -04:00
ajandClaude Sonnet 4.6 765a862440 feat: add SplitLine and SplitParameters models
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 11:56:00 -04:00
ajandClaude Opus 4.6 b970629a59 feat: add material library resolution, assist gas support, and UI fixes
- 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>
2026-03-23 22:00:57 -04:00
ajandClaude Opus 4.6 072915abf2 fix: detect winding direction for correct part spacing offset
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>
2026-03-23 21:57:23 -04:00
ajandClaude Opus 4.6 aeeb2e4074 fix: treat cut-offs as area selection boundaries with proper spacing
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>
2026-03-23 12:14:58 -04:00
ajandClaude Opus 4.6 a2f7219db3 fix: add proper spacing between G-code words in Cincinnati post output
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>
2026-03-23 05:46:46 -04:00
ajandClaude Opus 4.6 7e4040ba08 feat: add post processor support to console app
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>
2026-03-23 01:51:45 -04:00
ajandClaude Opus 4.6 0246073b31 fix: reduce default cut-off part clearance from 0.125 to 0.02
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>
2026-03-23 01:07:33 -04:00
ajandClaude Opus 4.6 4801895321 chore: add Cincinnati post processor build dependency to solution
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>
2026-03-23 01:07:28 -04:00
ajandClaude Opus 4.6 833abfe72e feat: add optional M98 part sub-programs to Cincinnati post processor
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>
2026-03-23 00:43:44 -04:00
ajandClaude Opus 4.6 379000bbd8 feat: auto-copy Cincinnati post processor DLL to Posts/ on build
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>
2026-03-23 00:12:47 -04:00
ajandClaude Opus 4.6 5936272ce4 refactor: move ProgramVariableManager to Core, add config file support
- 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>
2026-03-23 00:08:31 -04:00
ajandClaude Opus 4.6 da8e7e6fd3 feat: interactive cut-off selection and drag via line hit-testing
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>
2026-03-22 23:49:27 -04:00
ajandClaude Opus 4.6 53d24ddaf1 feat: implement Polygon.OffsetEntity and use geometric offset for cut-off clearance
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>
2026-03-22 23:46:18 -04:00
ajandClaude Opus 4.6 8efdc8720c fix: review fixes — culture-invariant formatting, sealed config, threshold boundary
- 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>
2026-03-22 23:45:22 -04:00
ajandClaude Sonnet 4.6 ca8a0942ab feat: add CincinnatiPostProcessor implementing IPostProcessor
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>
2026-03-22 23:41:06 -04:00
ajandClaude Opus 4.6 8c3659a439 feat: add CincinnatiSheetWriter for per-plate subprogram emission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:38:24 -04:00
ajandClaude Sonnet 4.6 95a0815484 feat: add CincinnatiPreambleWriter for main program and variable declaration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:35:27 -04:00
ajandClaude Opus 4.6 e9caa9b8eb feat: add CincinnatiFeatureWriter for per-feature G-code emission
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:33:06 -04:00
ajandClaude Sonnet 4.6 95a0db1983 feat: add SpeedClassifier for FAST/MEDIUM/SLOW cut distance classification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:29:14 -04:00
ajandClaude Sonnet 4.6 a323dcc230 feat: add ProgramVariable and ProgramVariableManager for macro variable declarations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:29:10 -04:00
ajandClaude Sonnet 4.6 24cd18da88 feat: add CoordinateFormatter for Cincinnati G-code coordinate output
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:29:06 -04:00
aj 5d26efb552 feat: add CincinnatiPostConfig and supporting enums 2026-03-22 23:26:16 -04:00
ajandClaude Sonnet 4.6 60c4545a17 feat: add OpenNest.Posts.Cincinnati project for Cincinnati CL-707 post processor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:25:16 -04:00
ajandClaude Opus 4.6 4db51b8cdf fix: regenerate cut-offs on part rotation and default cut direction
- 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>
2026-03-22 22:58:47 -04:00
ajandClaude Opus 4.6 1c561d880e refactor: simplify CutOff methods and fix ActionCutOff issues
- 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>
2026-03-22 22:51:52 -04:00
ajandClaude Opus 4.6 17fc9c6cab feat: RegenerateCutOffs uses geometry-based perimeter cache
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:44:15 -04:00
ajandClaude Opus 4.6 4287c5fa46 refactor: CutOff uses Dictionary<Part, Entity> instead of index-based list
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>
2026-03-22 22:42:32 -04:00
ajandClaude Opus 4.6 a735884ee9 feat: add Plate.BuildPerimeterCache with ShapeProfile and convex hull fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:34:14 -04:00
ajandClaude Opus 4.6 22554b0fa3 refactor: extract CollectPoints from FindBestRotation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:30:59 -04:00
ajandClaude Opus 4.6 48b4849a88 fix: ShapeProfile selects perimeter by largest bounding box area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:29:00 -04:00
ajandClaude Opus 4.6 f79df4d426 refactor: remove NfpNestEngine
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>
2026-03-22 21:11:53 -04:00
ajandClaude Opus 4.6 ebb18d9b49 fix: prevent RemnantFiller interleaving and PairFiller recursion
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>
2026-03-22 21:11:47 -04:00
ajandClaude Opus 4.6 31a9e6dbad feat: replace best-fit viewer dropdown with DrawingListBox in split view
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>
2026-03-22 20:53:43 -04:00
ajandClaude Opus 4.6 a576f9fafa fix: correct test helper winding and edge sequencer test data
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>
2026-03-22 20:31:50 -04:00
ajandClaude Opus 4.6 9453bb51ce docs: update CLAUDE.md with cut-off feature documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:02:42 -04:00
ajandClaude Opus 4.6 ad58332a5d feat: regenerate cut-offs after part drag and fill operations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:01:03 -04:00
ajandClaude Opus 4.6 d4f60d5e8e feat: add cut-off selection, drag-to-reposition, and delete
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:57:26 -04:00
ajandClaude Opus 4.6 3ea05257eb feat: wire up cut-off action in MainForm menu
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>
2026-03-22 19:54:09 -04:00
ajandClaude Opus 4.6 7e49ed620b feat: add ActionCutOff for placing sheet cut-offs
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>
2026-03-22 19:50:38 -04:00
ajandClaude Opus 4.6 57bd0447e9 feat: add PlateView cut-off rendering with grip handle
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>
2026-03-22 19:46:49 -04:00
ajandClaude Opus 4.6 07d6f08e8b feat: engine-specific TrimAxis and rename ShrinkAxis.Height to Length
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>
2026-03-22 19:43:29 -04:00
ajandClaude Opus 4.6 2f19f47a85 feat: serialize/deserialize cut-off definitions in nest file format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:40:01 -04:00
ajandClaude Opus 4.6 d58a446eac feat: add Plate.CutOffs collection with materialization and transform support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:36:05 -04:00
ajandClaude Opus 4.6 5fc7d1989a feat: add CutOff and CutOffSettings domain classes with segment generation
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>
2026-03-22 19:32:33 -04:00
ajandClaude Opus 4.6 3f6bc2b2a1 feat: guard Plate quantity/utilization/overlap for IsCutOff parts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:14:28 -04:00
ajandClaude Opus 4.6 7681a1bad0 feat: add Drawing.IsCutOff flag for cut-off support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:12:49 -04:00
ajandClaude Opus 4.6 a548d5329a chore: update NestProgressForm designer layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:09:07 -04:00
ajandClaude Opus 4.6 07012033c7 feat: use direction-specific engines in StripNestEngine
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>
2026-03-21 23:09:02 -04:00
ajandClaude Opus 4.6 92b17b2963 perf: parallelize PairFiller candidates and add GridDedup
- 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>
2026-03-21 23:08:55 -04:00
ajandClaude Opus 4.6 b6ee04f038 fix: use Part.Rotate() in PlateView to avoid mutating shared Programs
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>
2026-03-21 23:08:47 -04:00
ajandClaude Sonnet 4.6 8ffdacd6c0 refactor: replace NestPhase switch statements with attribute-based extensions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:49:44 -04:00
ajandClaude Opus 4.6 ccd402c50f refactor: simplify NestProgress with computed properties and ProgressReport struct
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>
2026-03-21 19:44:45 -04:00
ajandClaude Sonnet 4.6 b1e872577c feat: add Description/ShortName attributes to NestPhase with extension methods
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:38:54 -04:00
ajandClaude Opus 4.6 9903478d3e refactor: simplify BestCombination.FindFrom2 and add tests
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>
2026-03-21 17:07:43 -04:00
ajandClaude Opus 4.6 93a8981d0a feat: add Disable/Enable API to FillStrategyRegistry
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>
2026-03-21 15:19:27 -04:00
ajandClaude Opus 4.6 00e7866506 feat: add remnant filling to PairFiller for better part density
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>
2026-03-21 15:19:19 -04:00
ajandClaude Opus 4.6 560105f952 refactor: extract shared convergence loop and reduce parameter counts in StripeFiller
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>
2026-03-21 14:22:29 -04:00
ajandClaude Opus 4.6 266f8a83e6 docs: update CLAUDE.md with fill goal engines architecture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:58:35 -04:00
ajandClaude Sonnet 4.6 0b7697e9c0 feat: add VerticalRemnantEngine and HorizontalRemnantEngine
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>
2026-03-21 12:57:33 -04:00
ajandClaude Sonnet 4.6 83124eb38d feat: wire IFillComparer through PairFiller and StripeFiller
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>
2026-03-21 12:53:28 -04:00
ajandClaude Sonnet 4.6 24beb8ada1 feat: wire IFillComparer through FillHelpers, Linear, and Extents strategies
- FillHelpers.FillPattern gains optional IFillComparer parameter; falls back to FillScore when null
- LinearFillStrategy.Fill replaced with FillWithDirectionPreference + comparer from context.Policy
- ExtentsFillStrategy.Fill replaced with comparer.IsBetter, removing FillScore comparison
- DefaultNestEngine group-fill path resolves Task 6 TODO, passing Comparer to FillPattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:49:59 -04:00
ajandClaude Opus 4.6 ee83f17afe feat: wire FillPolicy into DefaultNestEngine and FillContext
- 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>
2026-03-21 12:46:30 -04:00
ajandClaude Sonnet 4.6 99546e7eef feat: add IFillComparer hooks to NestEngineBase
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>
2026-03-21 12:43:45 -04:00
ajandClaude Sonnet 4.6 4586a53590 feat: add FillPolicy record and FillHelpers.FillWithDirectionPreference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:41:07 -04:00
ajandClaude Sonnet 4.6 1a41eeb81d feat: add VerticalRemnantComparer and HorizontalRemnantComparer
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>
2026-03-21 12:38:23 -04:00
ajandClaude Sonnet 4.6 f894ffd27c feat: add IFillComparer interface and DefaultFillComparer
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>
2026-03-21 12:36:04 -04:00
ajandClaude Opus 4.6 0ec22f2207 feat: geometry-aware convergence, both-axis search, remnant engine, fill cache
- 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>
2026-03-21 10:12:31 -04:00
ajandClaude Opus 4.6 3f3d95a5e4 fix: orient pair short side along primary axis before convergence
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>
2026-03-21 09:22:13 -04:00
ajandClaude Opus 4.6 811d23510e feat: add RowFillStrategy and ColumnFillStrategy with registry integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:48:00 -04:00