Commit Graph

940 Commits

Author SHA1 Message Date
a6c2235647 fix: let DrawRapids track actual tool position through sub-programs
Don't restore pos after SubProgramCall expansion in DrawRapids — the
machine moves from hole to hole sequentially, so rapids should connect
from the previous hole's end to the next hole's center.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 18:26:39 -04:00
5c918a0978 fix: draw rapid move to hole center before sub-program lead-in
The rapid from the previous feature to the hole center is implied by
the SubProgramCall offset but wasn't being drawn. Now DrawRapids
renders this traverse before recursing into the sub-program.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:46:44 -04:00
92461deb98 fix: apply SubProgramCall offset additively and restore curpos after expansion
ConvertMode.ToIncremental skips SubProgramCalls when computing deltas,
so all code paths that expand SubProgramCalls must: (1) set curpos to
savedPos + Offset before expanding, and (2) restore curpos afterward
so subsequent incremental codes get correct deltas.

Fixed in ConvertProgram, GraphicsHelper (AddProgram, AddProgramSplit),
PlateRenderer (DrawRapids, DrawProgramPiercePoints, GetFirstPiercePoint),
and CutDirectionArrows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:40:05 -04:00
bc859aa28c feat: handle SubProgramCall offsets in BoundingBox and Rotate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:47:40 -04:00
09eac96a03 feat: handle SubProgramCalls in Cincinnati post feature splitting
SubProgramCalls are now treated as standalone features in the Cincinnati
post-processor. SplitByRapids emits them as single-element features
instead of splitting on rapids within sub-programs. A nest-level hole
sub-program registry deduplicates by content and assigns post numbers.
Sheet writers emit M98 calls with X/Y offsets for hole features, and
hole sub-program definitions are written after part sub-programs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:44:58 -04:00
df65414a9d feat: serialize and deserialize hole sub-programs in nest file format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:40:13 -04:00
4aed231611 feat: emit SubProgramCalls for circle holes in ContourCuttingStrategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:35:56 -04:00
c641b3b68e feat: expand SubProgramCalls with Offset in ConvertProgram
Inline sub-program geometry into the parent geometry list using Offset
as the starting curpos, replacing the Shape-wrapping approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:31:13 -04:00
f3b27c32c3 feat: add SubPrograms dictionary to Program with deep-copy support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:28:37 -04:00
c270d8ea76 feat: add Offset property to SubProgramCall for hole positioning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:26:55 -04:00
de6877ac48 feat: add option to round lead-in angles for circle holes
Snaps lead-in angles on ArcCircle contours to a configurable
increment (default 5°), reducing unique hole variations from
infinite to 72 max. Rounding happens upstream in EmitContour
so the PlateView and post output stay in sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:41:33 -04:00
3481764416 perf: use perimeter-only drawing in best fit pair evaluation
PairEvaluator was cloning the full CNC program (including all internal
cutouts) for every candidate. For parts with many holes (e.g. 952),
this caused O(n²) overlap checks and thousands of unnecessary polygon
tessellations per candidate.

Now extracts the perimeter shape once, builds a lightweight drawing
from it, and uses that for all Part.CreateAtOrigin calls. Cutouts are
irrelevant for best fit — only the outer boundary matters for pairing.

75x speedup on a 952-hole rectangle (30s → 0.4s).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:14:02 -04:00
640814fdf6 fix: marshal timer callbacks to UI thread to prevent GDI+ threading exception
System.Timers.Timer fires on thread pool threads, causing GraphicsPath
objects to be accessed concurrently by hover detection and OnPaint,
triggering "Object is currently in use elsewhere" in DrawParts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:41:18 -04:00
6a30828fad feat: optimize external lead-in placement using next-part pierce points
External lead-ins now sit on the line between the last internal cutout
and the next part's first pierce point, minimizing rapid travel. Cutout
sequencing starts from the bounding box corner opposite the origin and
iterates 3 times to converge the perimeter lead-in and internal sequence.
LeadInAssigner and PlateProcessor both use a two-pass approach: first
pass collects pierce points, second pass refines with next-part knowledge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:33:55 -04:00
786b6e2e88 fix: show cutting parameters dialog before assigning lead-ins
Auto-assign lead-ins silently reused existing plate parameters with no
way to change them after the first assignment. Now a dialog with the
full CuttingPanel is shown every time, pre-populated with the current
settings, so the user can review and modify before confirming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v0.1.0
2026-04-08 14:32:41 -04:00
ba89967448 fix: respect suppression state in filter panel and guard DetermineWinding
FilterPanel.LoadItem was hardcoding all layer and line type checkboxes
to checked, ignoring actual visibility state. Now reads Layer.IsVisible
and entity IsVisible to set correct checked state.

Also guard DetermineWinding against shapes with fewer than 3 polygon
points (defaults to CCW) to prevent crash when applying lead-ins.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:11 -04:00
b566d984b0 fix: preserve suppression state when reopening converter
LoadItem was resetting all entity visibility to true, overriding the
suppression state set by LoadDrawings. Now stores suppressed entity IDs
on FileListItem and re-applies after the reset. Also auto-unchecks
layers where all entities are suppressed, and syncs suppression state
back to the FileListItem when filters change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:32:18 -04:00
c1e6092e83 feat: add entity-based suppression with stable GUIDs
Entities now have a Guid Id for stable identity across edit sessions.
Drawing stores the full source entity set (SourceEntities) and a set of
suppressed entity IDs (SuppressedEntityIds), replacing the previous
SuppressedProgram approach. Unchecked entities in the converter are
suppressed rather than permanently removed, so they can be re-enabled
when editing drawings again.

Entities are serialized as JSON in the nest file (entities/entities-N)
alongside the existing G-code programs. Backward compatible with older
nest files that lack entity data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:25:48 -04:00
df86d4367b fix: update drawings in-place when editing in converter so parts reflect changes
EditDrawingsInConverter was replacing Drawing objects with new instances,
but Part.BaseDrawing is readonly — parts kept referencing the old drawings
with stale programs (e.g. etch lines that were removed). Now matches by
name and updates existing drawings in-place, then refreshes all parts.

Also fixes Part.Update() which applied rotation backwards and was missing
UpdateBounds() and lead-in state reset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:58:51 -04:00
40026ab4dc test: add SpatialQuery DirectionalDistance tests for circles, squares, and rounded rects
24 tests covering circle-to-circle, square-to-square, rounded rectangle,
mixed shape types, PushDirection overload, edge cases, and symmetry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:10:32 -04:00
b18a82df7a refactor: clean up SpatialQuery duplications and redundancies
Extract ArcToLineClosestDistance helper to eliminate duplicate Phase 3
arc-to-line loops, remove redundant MaxValue guard in curve-to-curve
check, consolidate CollectVertices overloads, and add entity-based
PushDirection overload for API consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 09:33:29 -04:00
f090a2e299 fix: add arc-to-line closest-point check in DirectionalDistance
Corner arcs from offset perimeters could slip past vertex sampling,
causing compactor push to undershoot by ~halfSpacing. Use ClosestPointTo
to find the actual nearest point on each arc to each line before firing
the directional ray.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:52:30 -04:00
55192a4888 chore: update ShapeLibraryForm designer layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:37:42 -04:00
7c28a35ad8 feat: add Edit Drawings in Converter button to reopen nest drawings in CadConverterForm
Adds a toolbar button on the Drawings tab that opens the CAD converter
pre-populated with the current nest drawings, allowing users to revisit
layer filtering, quantities, and other settings without re-importing.

Also fixes PlateView stealing focus from text inputs on mouse enter
and FilterPanel crashing when loaded before form handle is created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:37:20 -04:00
b2a723ca60 feat: add Shape Library UI with configurable shapes and flange presets
Add a Shape Library dialog (Nest > Shape Library) for creating drawings
from built-in parametric shapes. Supports configuration presets loaded
from JSON files — ships with 136 standard pipe flanges. Parameters use
TextBox inputs with architectural unit parsing (feet/inches, fractions).

- ShapeLibraryForm with split layout: shape list, preview, parameters
- ShapePreviewControl for auto-zoom rendering with info overlay
- ArchUnits utility for parsing architectural measurements
- SetPreviewDefaults() on all ShapeDefinition subclasses
- Convention-based config discovery (Configurations/{ShapeName}.json)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 07:44:03 -04:00
3dca25c601 fix: improve circle nesting with curve-to-curve distance and min copy spacing
Add Phase 3 curve-to-curve direct distance in CpuDistanceComputer to
catch contacts that vertex sampling misses between curved entities.
Enforce minimum copy distance in FillLinear to prevent bounding box
overlap when circumscribed polygon boundaries overshoot true arcs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 00:15:35 -04:00
ebc1a5f980 refactor: extract shared helpers in SpatialQuery
Pull duplicated vertex collection, edge conversion, sorting, and
ray-circle solving into reusable private methods. Delegate the
no-offset DirectionalDistance overload to the offset version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 00:15:30 -04:00
b729f92cd6 fix: correct compactor circle-to-circle directional distance
The vertex-to-entity approach in DirectionalDistance only sampled 4
cardinal points per circle, missing the true closest contact when
circles are offset diagonally from the push direction. This caused
the distance to be overestimated, pushing circles too far and
creating overlap that worsened with distance from center.

Add a curve-to-curve pass that computes exact contact distance by
treating the problem as a ray from one center to an expanded circle
(radius = r1 + r2) at the other center. Includes arc angular range
validation for arc-to-arc and arc-to-circle cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:51:09 -04:00
5d6e018b81 fix: preserve circle rotation direction through geometry round-trip
Circle.Rotation was lost in three places, causing reversed circles to
still offset inward instead of outward:
- ConvertGeometry.AddCircle hardcoded CCW instead of using circle.Rotation
- ConvertProgram.AddArcMove created Circle without setting Rotation from arc
- Shape.OffsetOutward/OffsetInward copied Circle without setting Rotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:38:23 -04:00
5163b02f89 fix: increase max zoom and handle GDI+ thread race in PlateView
Raise ViewScaleMax from 3000 to 10000 for deeper zoom. Catch
InvalidOperationException in hoverTimer_Elapsed when GraphicsPath is
concurrently used by the paint thread.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:01:50 -04:00
a59911b38a remove MicrotabLeadOut — redundant with normal tabs
MicrotabLeadOut was an unimplemented stub (Generate returned empty list)
that duplicated tab functionality. Existing saved configs with "Microtab"
selected will gracefully fall back to NoLeadOut.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:43:38 -04:00
810e37cacf feat: improve multi-plate nesting with multi-remnant filling and better zone scoring
- Iterate all remnants instead of only the first when packing and filling
- Improve ScoreZone with estimated part count and aspect ratio matching
- Cache bounding boxes in SortItems and remnants in TryPlaceOnExistingPlates
- Make TryConsolidateTailPlates loop until stable, trying all donor/target pairs
- Fix consolidation grouping to use BaseDrawing reference instead of name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:20:29 -04:00
8dfa45c446 refactor: rename PlateResult to PlateProcessingResult
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:20:14 -04:00
b223f69572 chore: add missing BendLineDialog designer resource
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:20:11 -04:00
98c574c2ad perf: defer Path.IsVisible hit-test to hover timer callback
Move the expensive per-part hit-test out of OnMouseMove and into
the hoverTimer callback. The hit-test now only runs once after
1000ms of stillness, not on every mouse move event.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:19:48 -04:00
30f1008fa9 feat: show hover tooltip only after 1000ms of mouse stillness
Add a hoverTimer that restarts on each mouse move over a part.
Tooltip only renders after the timer fires, hiding while the
cursor is in motion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:12:02 -04:00
41c20eaf75 feat: make hover tooltip follow the cursor
Update hoverPoint on every mouse move while over a part, not just
when the hovered part changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:59:14 -04:00
3a97253473 perf: add bounding box pre-check before Path.IsVisible in hover detection
Path.IsVisible was consuming 52% of CPU on mouse move. Add a cheap
GetBounds().Contains() check first so only parts under the cursor
hit the expensive GDI+ path test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:55:22 -04:00
3eab3c5946 fix: guard against null actionManager during PlateView construction
Plate setter is called in the constructor before actionManager is
initialized, causing a NullReferenceException on startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:45:41 -04:00
0e05ad04ea refactor: clean up PlateView after component extraction
Remove dead programIdFont field, unused imports (OpenNest.CNC,
System.ComponentModel, OpenNest.Math, System.Collections.ObjectModel).
PlateView is now 692 lines (down from 1035).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:20:28 -04:00
5ac985dc0f refactor: update PlateRenderer for SelectionManager cut-off list
PlateRenderer now checks Selection.SelectedCutOffs.Contains() instead
of comparing against a single SelectedCutOff property. Remove temporary
SelectedCutOff shim from PlateView and unused Designer assignment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:18:59 -04:00
865754611c refactor: extract PreviewManager from PlateView
Moves preview part lifecycle (stationaryParts, activeParts) into a dedicated
PreviewManager class. PlateView retains forwarding properties and methods for
backward compatibility. Adds Previews property for direct access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:18:17 -04:00
9db326ee5d refactor: extract ActionManager from PlateView
Move action lifecycle (currentAction, previousAction, SetAction, ProcessEscapeKey,
RestorePreviousAction, GetDisplayName) into a dedicated ActionManager class.
PlateView retains public forwarding methods and exposes Actions property.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:16:15 -04:00
25faba430c refactor: extract CutOffHandler from PlateView
Move cut-off drag interaction mechanics into a dedicated CutOffHandler
class, reducing PlateView complexity and following the same pattern
established by SelectionManager extraction in Task 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:13:42 -04:00
089df67627 refactor: extract SelectionManager from PlateView
Move all selection state and operations (SelectedParts, SelectedCutOffs, DeselectAll, SelectAll, AlignSelected, RotateSelectedParts, PushSelected, GetPartAt*, GetPartsFromWindow, DeleteSelected) into a new internal SelectionManager class. PlateView retains public forwarding methods and properties to preserve the existing API surface. SelectedCutOff property kept public for WinForms designer compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:09:08 -04:00
11884e712d fix: clear empty area below items in drawing list on resize
The WM_ERASEBKGND suppression from 3c4d00b left stale artifacts
in the non-item region when the control was resized. Fill only
the area below the last visible item so items still don't flicker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:23:30 -04:00
6bed736cf0 perf: use actual geometry instead of tessellated polygons for push distance
- Add entity-based DirectionalDistance overload to SpatialQuery that
  uses RayArcDistance/RayCircleDistance instead of tessellating arcs
  and circles into line segments
- Add GetOffsetPartEntities, GetPerimeterEntities, GetPartEntities to
  PartGeometry for non-tessellated entity extraction
- Update Compactor.Push to use native entities instead of tessellated
  lines — 952 circles = 952 entities vs ~47,600 line segments
- Use bounding box containment check to skip cutout entities when no
  obstacle is inside the moving part (perimeter-only for common case)
- Obstacles always use perimeter-only entities since cutout edges are
  inside the solid and cannot block external parts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:06:37 -04:00
c20a079874 refactor: clean up MultiPlateNester code smells and duplication
Extract shared patterns into reusable helpers: FitsBounds (fits-normal/
rotated check), OptionWorkArea (edge-spacing subtraction), DecrementQuantity,
TryWithUpgradedSize (upgrade-try-revert), FindSmallestFittingOption.
Add PlateResult.AddParts to consolidate dual parts-list bookkeeping.
Cache sorted plate options and add HasPlateOptions property. Introduce
MultiPlateNestOptions to replace 10-parameter Nest signature with a
clean options object. Fix fragile Drawing.Name matching with reference
equality in PackIntoExistingRemnants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 07:43:58 -04:00
804a7fd9c1 fix: check longest side against plate dimensions in best fit filter
The filter only checked ShortestSide against the plate's short dimension,
allowing results where the long side far exceeded the plate length.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 07:28:59 -04:00
3c4d00baa4 fix: suppress WM_ERASEBKGND to prevent drawing list flicker on quantity change
ListBox is a native Win32 control so ControlStyles.OptimizedDoubleBuffer
had no effect. The erase-then-redraw cycle on each Invalidate() caused
visible flashing. Suppressing WM_ERASEBKGND is safe because OnDrawItem
already fills the complete item bounds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 22:42:00 -04:00