BOM import was skipping BendDetectorRegistry.AutoDetect and
Bend.UpdateEtchEntities, so parts imported via BOM had no etch
or bend lines. Now matches the CadConverterForm import behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an item was too large for every plate option, its dimensions dominated
the global min-dimension filter, causing all candidate plates to be rejected.
This made auto-nesting exit immediately with no results even when the other
items could fit. Oversized items are now excluded from the filter so the
remaining items nest normally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Try all valid best fit pairs instead of only the first when qty=2,
picking the best via IsBetterFill comparer (fixes suboptimal plate
selection during auto-nesting)
- Pre-compute best fits across all plate sizes once via
BestFitCache.ComputeForSizes instead of per-size GPU evaluation
- Early exit plate optimizer when all items fit (salvage < 100%)
- Trim slide offset sweep range to 50% overlap to reduce candidates
- Use actual geometry (ray-arc/ray-circle intersection) instead of
tessellated polygons for slide distance computation — eliminates
the massive line count from circle/arc tessellation
- Add RayArcDistance and RayCircleDistance to SpatialQuery
- Add PartGeometry.GetOffsetPerimeterEntities for non-tessellated
perimeter extraction
- Disable GPU slide computer (slower than CPU currently)
- Remove dead SelectBestFitPair virtual method and overrides
Reduces best fit computation from 7+ minutes to ~4 seconds for a
73x25" part with 30+ holes on a 48x96 plate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When plate costs are equal (e.g. all zero), the optimizer now picks the
plate size with the tightest density instead of the smallest plate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BOM import now loads the nest template to populate plate size, part
spacing, edge spacing, and quadrant instead of hard-coding defaults.
Spacing columns are shown per material+thickness group on the Groups
tab so each combo can be adjusted independently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The timer-based list update only removed depleted drawings but never
added them back when they became un-depleted (e.g., after deleting a
part from the plate).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Circle.ToPoints() always generated CCW points regardless of the Rotation
property, so reversing a circle contour in the CAD converter had no effect.
Now negates the step angle when Rotation is CW, matching Arc.ToPoints behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidated two stateless classes into one unified API: Dxf.Import(),
Dxf.GetGeometry(), Dxf.ExportPlate(), Dxf.ExportProgram(). Export
state moved into a private ExportContext. Removed bool+out pattern
from GetGeometry in favor of returning empty list on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spline import now uses SplineConverter (arc-based) so the configurable
precision parameter is obsolete. Removed the setting from the options
dialog, DxfImporter property, Settings files, and all callsites.
Hardcoded 200 as the sampling density for the intermediate point
evaluation that feeds into SplineConverter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PlateListChanged handler was setting tabControl1.SelectedIndex = 0,
which forced the UI to the plates tab whenever a sentinel plate was
auto-created during part placement, disrupting the workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add LightGray separator lines between items to visually distinguish
adjacent quantity bars. Preserve scroll position and selection when
updating the drawing list by saving/restoring TopIndex and SelectedItem.
Use incremental item removal instead of full list rebuild when hiding
depleted drawings. Wrap list modifications in BeginUpdate/EndUpdate to
reduce flicker.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed add and remove plate buttons from the plate header panel.
The plate tab toolbar now has add/remove buttons with the remove
button state driven by PlateManager.CanRemoveCurrent. MainForm's
Plate > Remove menu item also syncs on plate change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this, RemoveEmptyPlates would destroy the sentinel with no
recovery, and tail-plate subscriptions would go stale after plate list
mutations. Added tests for both scenarios.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EditNestForm now holds a Document instead of a bare Nest field,
eliminating duplicated LastSavePath, LastSaveDate, and SaveAs logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all backward-compat wrapper calls on EditNestForm (LoadFirstPlate,
LoadLastPlate, LoadNextPlate, LoadPreviousPlate, IsFirstPlate, IsLastPlate,
EnsureSentinelPlate, CurrentPlateIndex, PlateCount) with direct access to
activeForm.PlateManager. Remove the now-unused wrapper methods and properties
from EditNestForm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace direct plate collection event handlers, navigation methods, and
sentinel logic in EditNestForm with PlateManager delegation. Navigation
buttons, list selection, export, and plate removal now route through
PlateManager. Backward-compatible delegating wrappers kept for MainForm
until Task 7.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OnPlateAdded now navigates to the new plate when suppressNavigation is
false, matching the original EditNestForm behavior. Fixed CanRemoveCurrent
test to account for this auto-navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- EnsureSentinel() maintains exactly one trailing empty plate, suppressing navigation events during mutation
- Reactive tail subscriptions (PartAdded/PartRemoved on last two plates) call EnsureSentinel automatically; re-subscribed after each plate list change
- BeginBatch()/EndBatch() defers sentinel enforcement during bulk operations
- GetOrCreateEmpty() returns or creates an empty plate; RemoveCurrent() removes the current plate with index clamping; CanRemoveCurrent guards deletion
- 13 new tests (30 total PlateManager tests), all passing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drawings created by BomImportForm and MCP InputTools were missing color
assignments, causing them to render with default empty color instead of
the standard part color palette. Moved PartColors and GetNextColor() to
Drawing in Core so all consumers share one definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces PlateChangedEventArgs and PlateManager in OpenNest.Core to centralize plate navigation logic (CurrentIndex, LoadFirst/Last/Next/Previous/At, IsFirst/IsLast). Includes full xUnit test coverage (17 tests) verifying navigation, event firing, and disposal unsubscription.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FileListControl loses focus when interacting with other controls on the
form, making arrow key navigation stop working. Intercept Up/Down at
the form level via ProcessCmdKey and forward to the file list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Strategies now promote results to IsOverallBest when they beat the
pipeline best, so the UI updates immediately on improvement rather
than waiting for each phase to complete
- PlateView only updates the main view on overall-best results, fixing
intermediate angle-sweep layouts leaking to the plate display
- Skip Row/Column strategies for rectangle parts (redundant with Linear)
- Intercept Escape key at MainForm level via ProcessCmdKey so it always
reaches the active PlateView regardless of focus state
- Restore keyboard focus to PlateView after fill progress form closes
- Remnant engines use SelectBestFitPair for orientation-aware pair
selection; DefaultNestEngine tries both landscape and portrait pairs
- RemnantFiller preserves more parts during topmost-part removal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Always keep a trailing empty plate so users can immediately place parts
without manually adding a plate. Auto-appends a new sentinel when parts
land on the last plate; trims excess trailing empties on removal.
Plate list now shows Parts count and Utilization % columns. Empty plates
are filtered from save and export. Sentinel updates are deferred via
BeginInvoke to avoid collection-modified exceptions and debounced to
prevent per-part overhead on bulk operations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a right-click "Delete" option on the drawings tab that removes the
selected drawing and all its placed parts from every plate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebuild the dialog from a flat layout into grouped sections: engine
selector at top, Parts group with rotation columns and summary label,
Options group, collapsible Plate Optimizer with single-field size
parsing, and a clean button bar. Adds engine sync between dialog and
toolbar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>