Commit Graph

74 Commits

Author SHA1 Message Date
91281c8813 fix(engine): throttle progress reports to 150ms intervals
Parallel loops were flooding the UI with per-angle/per-candidate reports
faster than WinForms could render them. Use Interlocked timestamp checks
to report at most every 150ms, keeping descriptions readable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:51:29 -04:00
c2f775258d fix(ui): show live per-angle/per-candidate detail during nesting
Don't overwrite the Detail label with phase-level reports — let the
per-angle and per-candidate descriptions from the parallel loops remain
visible. Only clear the label on completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:48:58 -04:00
930dd59213 fix(ui): set Description in phase-level progress reports
ReportProgress was not setting Description, so the Detail row always
showed the default em-dash. Now each phase report includes a meaningful
description, and UpdateProgress always updates the label (resetting to
em-dash when null).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:40:25 -04:00
9cc6cfa1b1 fix(engine): add volatile to AnglePredictor lock field and Models content copy
- Mark _loadAttempted as volatile for correct double-checked locking
- Add Content item to copy Models/ directory to output for ONNX inference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:33:07 -04:00
e33b5ba063 feat(engine): integrate AnglePredictor into FindBestFill angle selection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:28:50 -04:00
8cc14997af feat(engine): add AnglePredictor ONNX inference class
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:27:44 -04:00
eee2d0e3fe feat(training): add training notebook skeleton and requirements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:27:41 -04:00
c466a24486 chore(engine): add Microsoft.ML.OnnxRuntime package
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:26:26 -04:00
71fc1e61ef feat(training): enable forced full angle sweep and store per-angle results
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:25:33 -04:00
8e46ed1175 feat(engine): pass per-angle results through BruteForceResult
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:22:38 -04:00
09ed9c228f feat(engine): add ForceFullAngleSweep flag and per-angle result collection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:21:14 -04:00
eb21f76ef4 feat(engine): add AngleResult class and Description to NestProgress 2026-03-14 20:18:38 -04:00
74272bea80 feat(engine): add perimeter-to-area ratio to part features
Adds PerimeterToAreaRatio (perimeter / area) as a spacing sensitivity
indicator for ML feature extraction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 14:41:30 -04:00
321c476b8b feat(engine): add ML feature extraction and brute-force runner
Add FeatureExtractor for computing geometric part features (convexity,
aspect ratio, circularity, bitmask) and BruteForceRunner for generating
training data by running the fill engine and recording results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:41:20 -04:00
10f9b5357c fix(engine): compute unused area from actual part areas
Calculate unused plate area by subtracting total part area from the
work area instead of relying on FillScore.UsableRemnantArea, which
could over-report available space.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:41:08 -04:00
a9aaab8337 refactor: use ShapeProfile perimeter for boundary and intersection
Replace shape-list iteration with ShapeProfile.Perimeter in both
Part.Intersects and PartBoundary, simplifying the logic and ensuring
only the outermost contour is used for collision detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:41:03 -04:00
d6ffa77f35 feat(console): improve training data collection and best-fit persistence
- Add verbose per-file and per-sheet-size console output during collection
- Skip already-processed parts at the sheet-size level instead of all-or-nothing
- Precompute best-fits once per part and reuse across all sheet sizes
- Clear best-fit cache after each part to prevent memory growth
- Save best-fits in separate bestfits/ zip entries instead of embedding in nest.json
- Filter to Keep=true results only and scope to plate sizes in the nest
- Set nest name to match filename (includes sheet size and part count)
- Add TrainingDatabase with per-run skip logic and SQLite schema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:39:24 -04:00
1440d2a16a refactor(engine): reorder phases and simplify fill pipeline
Run pairs phase first to establish a baseline before linear and
rect-best-fit phases. Replace IsBetterFill with direct FillScore
comparison. Simplify FillPattern to sequential iteration, reusing a
single FillLinear engine instance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:14:32 -04:00
152e057a46 perf(engine): inline bounding box computation in FillScore
Compute min/max bounds in a single pass alongside part area
accumulation, avoiding the separate GetBoundingBox() call and
redundant iteration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:14:28 -04:00
de70999975 refactor(engine): precompute hull angles during pair evaluation
Store hull edge angles in BestFitResult at evaluation time so they
don't need to be recomputed during the fill phase. Extract
GetHullEdgeAngles(Polygon) overload from FindHullEdgeAngles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:14:25 -04:00
183d169cc1 feat: integrate GPU slide computation into best-fit pipeline
Thread ISlideComputer through BestFitCache → BestFitFinder →
RotationSlideStrategy. RotationSlideStrategy now collects all offsets
across 4 push directions and dispatches them in a single batch (GPU or
CPU fallback). Also improves rotation angle extraction: uses raw geometry
(line endpoints + arc cardinal extremes) instead of tessellation to avoid
flooding the hull with near-duplicate edge angles, and adds a 5-degree
deduplication threshold.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:29:51 -04:00
97dfe27953 feat: add ISlideComputer interface and GPU implementation
ISlideComputer abstracts batched directional-distance computation so GPU
implementations can process all slide offsets in a single kernel launch.
GpuSlideComputer uses ILGPU with prepared edge data (precomputed inverse
deltas and min/max bounds) and caches stationary/moving buffers across
calls. GpuEvaluatorFactory exposes a singleton factory method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:29:43 -04:00
6b0bafc9de merge: integrate NFP-based autonesting from feature/nfp-autonest
Brings in the full NFP implementation: ConvexDecomposition, NoFitPolygon,
InnerFitPolygon, NfpCache, BottomLeftFill, SimulatedAnnealing optimizer,
and INestOptimizer interface. Resolves conflicts by keeping master's
progress reporting infrastructure alongside the new AutoNest methods,
and adapting RunAutoNest_Click to use NFP AutoNest with async/cancellation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:40:38 -04:00
ca685ac8ba feat(engine): add public Fill overloads returning List<Part> with progress/cancellation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:27:31 -04:00
93fbe1a9f8 feat(engine): add FindBestFill and FillWithPairs overloads with progress and cancellation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:26:32 -04:00
a97477fcdf feat(engine): add NestPhase enum and NestProgress data model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:24:55 -04:00
c68139e15e merge: resolve .gitignore conflict, keep both entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:22:45 -04:00
612b540d9d refactor: rename Size.Height to Size.Length across codebase
"Length" is more natural than "height" for flat plate materials.
Renames the field on OpenNest.Geometry.Size, Box.Height property,
and all references across 38 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:01:40 -04:00
a084819889 perf: comment out Debug.WriteLine calls in FillLinear
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 08:44:14 -04:00
2632b3dbf7 fix: resolve infinite loop in multi-plate autonest and wire into console
- Change while(true) to bounded for-loop (max 100 plates) in MainForm
- Use Drawing.Name comparison instead of reference equality for quantity deduction
- Add Math.Max(0, ...) guard to prevent negative quantities
- Tune SA parameters for faster convergence (cooling=0.995, minTemp=0.1, maxNoImprove=500)
- Add --autonest flag to OpenNest.Console for CLI-based NFP autonesting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 08:29:02 -04:00
3f3b07ef5d feat: add NFP-based mixed-part autonesting
Implement geometry-aware nesting using No-Fit Polygons and simulated
annealing optimization. Parts interlock based on true shape rather than
bounding boxes, producing tighter layouts for mixed-part scenarios.

New types in Core/Geometry:
- ConvexDecomposition: ear-clipping triangulation for concave polygons
- NoFitPolygon: Minkowski sum via convex decomposition + Clipper2 union
- InnerFitPolygon: feasible region computation for plate placement

New types in Engine:
- NfpCache: caches NFPs keyed by (drawingId, rotation) pairs
- BottomLeftFill: places parts using feasible regions from IFP - NFP union
- INestOptimizer: abstraction for future GA/parallel upgrades
- SimulatedAnnealing: optimizes part ordering and rotation

Integration:
- NestEngine.AutoNest(): new public entry point for mixed-part nesting
- MainForm.RunAutoNest_Click: uses AutoNest instead of Pack
- NestingTools.autonest_plate: new MCP tool for Claude Code integration
- Drawing.Id: auto-incrementing identifier for NFP cache keys
- Clipper2 NuGet added to OpenNest.Core for polygon boolean operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 08:08:22 -04:00
78ee65d946 fix: resolve grid overlap bug and parallelize fill loops
The push algorithm's copy distance formula (bboxDim - slideDistance)
produced distances smaller than the part width when inflated boundary
arc vertices interacted spuriously, causing ~0.05 unit overlaps between
all adjacent grid parts.

Two fixes applied:
- Clamp ComputeCopyDistance to bboxDim + PartSpacing minimum
- Use circumscribed polygons (R/cos(halfStep)) for PartBoundary arc
  discretization so chord segments never cut inside the true arc,
  eliminating the ChordTolerance offset workaround

Also parallelized three sequential fill loops using Parallel.ForEach:
- FindBestFill angle sweep (up to 38 angles x 2 directions)
- FillPattern angle sweep for group/pair fills
- FillRemainingStrip rotation loop

Added diagnostic logging to HasOverlaps, FindCopyDistance, and
FillRecursive for debugging fill issues.

Test result: 45 parts @ 79.6% -> 47 parts @ 83.1%, zero overlaps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 00:36:48 -04:00
0e3bf3ccaa feat: try fewer rows in FillRecursive when remainder strip yields more parts
When the greedy maximum-row tiling leaves a thin remainder, tries removing
the last row and re-filling the larger strip. Picks whichever total is
higher. Fixes cases where e.g. 4 rows + 11 remainder = 47 beats 5 rows = 45.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:19:42 -04:00
f3662465cf feat: try cardinal rotations in FillRemainingStrip for better strip fills
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:12:07 -04:00
4d250e3990 feat: use FillScore for fill result comparisons in NestEngine
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:11:38 -04:00
a5b7049ecc feat: add FillScore struct with lexicographic comparison
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:10:09 -04:00
079a44052c perf: pre-allocate tile list capacity and remove redundant bbox walks
Pre-size TilePattern result list from estimated copy count to avoid
resizing. Remove redundant Program.BoundingBox() call and UpdateBounds()
in MakeSeedPattern — the Part's BoundingBox is already correct.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:13:22 -04:00
94850795cf perf: replace O(p²) startOffset loop with O(p) pass in FindPatternCopyDistance
max(aUpper_i - bLower_j) across all pairs simplifies to
max(aUpper) - min(bLower), computed in a single pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:10:44 -04:00
53759c5877 perf: eliminate Program.Clone calls from copy-distance computations
FindCopyDistance and FindPatternCopyDistance were cloning entire Parts
(including deep Program copies) just to get offset locations for
GetLines. Compute offset locations directly instead. Also skip the
Pattern wrapper in TilePattern — clone parts directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:02:59 -04:00
8ad01bb7f8 perf: pre-compute directional edges in PartBoundary constructor
RotationDirection and direction filtering were recomputed on every
GetLines call. Pre-compute edges per PushDirection once in the
constructor so GetLines only translates cached edges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:59:06 -04:00
23b5358352 perf: add CloneAtOffset to skip re-rotation and bbox walk on part clones
Part.Clone() re-clones from the drawing's unrotated program, re-rotates,
and walks all CNC codes twice for bounding box — 4 O(c) passes per clone.
CloneAtOffset clones from the already-rotated program and computes the
bounding box arithmetically, reducing to 1 O(c) pass per clone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:53:02 -04:00
91908c1732 perf: optimize fill hot path — bbox pre-check and geometry inner loop
- Add bounding box rejection in HasOverlaps to skip expensive
  Part.Intersects (CNC→geometry conversion) for non-adjacent parts.
  Eliminates ~35% CPU in IsBetterValidFill for grid layouts.
- Optimize RayEdgeDistance: access Line fields directly instead of
  property getters (avoids Vector struct copies), inline IsEqualTo
  with direct range comparison (avoids Math.Abs), and precompute
  deltas for reuse in interpolation.
- Cache line endpoints in DirectionalDistance outer loop to avoid
  repeated struct copies in the inner loop.
- Add fill timer to ActionClone.Fill, displayed in PlateView status
  bar as "Fill: N parts in M ms".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:10:25 -04:00
3705d50546 feat: add remainder strip re-fill to improve pattern fill density
After the main fill, detect if the last column/row is an "oddball"
with fewer parts than the main grid. If so, remove those parts and
re-fill the remainder strip independently using all strategies
(linear, rect best-fit, pairs). Improves 30→32 parts on the test
case (96x48 plate with 30x7.5 interlocking parts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:15:42 -04:00
435a08074b feat: improve remnant fill with rotation sweep, smart pair selection, and partial pattern fill
Narrow remnant strips now get more parts by:
- Sweeping rotations every 5° when the strip is narrower than the part
- Including all pairs that fit the strip width (not just top 50 by area)
- Placing individual parts from incomplete pattern copies that still fit
- Using finer polygon tolerance (0.01) for hull edge angle detection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:33:06 -04:00
4196a30791 refactor: unify FillLinear tiling into recursive FillRecursive method
Extract MakeSeedPattern for shared part creation, and replace the
two-step primary/perpendicular tiling with a single FillRecursive
method that tiles along one axis then recurses perpendicular.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:00:09 -04:00
3220306d3a feat: add reverse push directions for concave interlocking and cache best-fit results
Add PushDirection.Right and PushDirection.Up to RotationSlideStrategy so
parts can approach from all four directions. This discovers concave
interlocking arrangements (e.g. L-shaped parts nesting into each other's
cavities) that the original Left/Down-only slides could never reach.

Introduce BestFitCache so best-fit results are computed once at step size
0.25 and shared between the viewer and nesting engine. The GPU evaluator
factory is configured once at startup instead of being wired per call
site, and NestEngine.CreateEvaluator is removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:02:41 -04:00
031264e98f refactor: pre-compute offset boundaries in FillLinear via PartBoundary
Extract offset polygon computation into PartBoundary, which builds
and caches inflated boundary polygons per unique part geometry.
FillLinear now uses symmetric half-spacing and reuses boundaries
across tiling passes, avoiding redundant offset calculations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:28:01 -04:00
99edad4228 refactor: extract responsibilities from NestEngine into focused classes
- Move BuildPairParts to BestFitResult.BuildParts() instance method
- Extract BinConverter (RectanglePacking) for Part/NestItem/Bin conversions
- Extract RotationAnalysis for FindBestRotation and FindHullEdgeAngles

NestEngine reduced from 484 to 287 lines — now purely orchestration,
strategy selection, and comparison logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:37:50 -05:00
b738d4c72c refactor: clean up NestEngine — collapse overloads, extract helper, remove dead code
- Fill(NestItem) and Fill(List<Part>) now delegate to their Box overloads
- Add Part.CreateAtOrigin() to replace repeated 4-line build-at-origin pattern
  used in NestEngine, RotationSlideStrategy, and PairEvaluator
- Remove dead code: FillArea overloads, Fill(NestItem, int), FillWithPairs(NestItem),
  ConvertTileResultToParts, PackBottomLeft.FindPointHorizontal, Pattern.GetLines/GetOffsetLines,
  unused count variable in FillNoRotation
- Simplify IsBetterValidFill to delegate to IsBetterFill after overlap check

NestEngine reduced from 717 to 484 lines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:31:15 -05:00
d3704378c2 refactor: replace VPattern/HPattern with unified FillGrid method
Consolidates two nearly-identical grid generation methods into a single
FillGrid method with a columnMajor parameter. Fixes bug in HPattern
where inner loop used rows instead of columns, and fixes FillNoRotation
silently discarding results by not adding them to Bin.Items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:27:11 -05:00