Commit Graph

47 Commits

Author SHA1 Message Date
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
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
43a27df73c fix: handle multi-part pattern copy distance in FillLinear
FindPatternCopyDistance now checks every pair of parts across adjacent
patterns so that multi-part patterns (e.g. interlocking pairs) maintain
correct spacing between ALL parts, not just the bounding boxes. The
original single-part logic is preserved as a fast path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 20:55:12 -05:00
90b89a5dfa feat: add FillRectangleBestFit strategy and remove false overlap rejection
- Remove IsBetterValidFill overlap gate for FillLinear results; the
  geometry-aware spacing in FillLinear is sufficient and the overlap
  check produced false positives on parts with arcs/curves, causing
  valid grid layouts to be rejected in favor of inferior pair fills.
- Add FillRectangleBestFit strategy that uses BestCombination to mix
  normal and rotated orientations, filling remnant strips for higher
  part counts on rectangular parts.
- All Fill overloads now compare linear, rectangle best-fit, and
  pair-based strategies, picking whichever yields the most parts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 20:00:38 -05:00
0c14d7f854 fix: align best-fit sweep to include offset=0 for grid arrangements
The perpendicular sweep started at perpMin (e.g. -8.75) which with
coarse step sizes never landed on offset=0, missing the perfect
side-by-side and stacked same-orientation patterns for rectangular parts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 19:24:37 -05:00
5bebfcb612 feat: wire GpuPairEvaluator into NestEngine with auto-detection
NestEngine.CreateEvaluator factory delegate allows injection of GPU
evaluator from UI layer. GpuEvaluatorFactory.Create attempts GPU,
returns null (CPU fallback) if unavailable. All NestEngine call sites
wired up.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:27:15 -05:00
b83d09c3a7 refactor: extract IPairEvaluator interface from PairEvaluator
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:19:05 -05:00
bc411949b8 fix: use offset geometry for moving part in FillLinear spacing
Offset the moving shape's geometry by PartSpacing instead of adding
spacing linearly to the copy distance. This guarantees minimum clearance
in all directions for curved/complex shapes, not just along the slide axis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:10:48 -05:00
a6949a390d feat: convert OpenNest.Engine to .NET 8 SDK-style project
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 13:22:09 -05:00
afca2068cc feat: integrate best-fit pair finding into NestEngine.Fill
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:03:03 -05:00
955446a2f4 feat: add BestFitFinder orchestrator with hull edge angle strategies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:54:26 -05:00
7a0aac9f59 feat: add BestFitFilter and TileEvaluator for pair filtering and tiling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:51:44 -05:00
2a8b2dfdee feat: add PairEvaluator with overlap detection and rotating calipers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:49:26 -05:00
21e1c959d3 feat: add RotationSlideStrategy with directional push contact algorithm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:45:28 -05:00
86fee08670 feat: add PairCandidate, BestFitResult, and IBestFitStrategy for best-fit pair finding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:42:43 -05:00
0573cb2f6d feat: fill open area and optimize pattern rotation via convex hull
ActionClone.Fill() now computes the largest open rectangle from the
cursor position (trying both vertical and horizontal) and passes it
to the engine, so fills no longer overlap existing parts.

Pattern fills try all convex hull edge angles to find the rotation
that maximizes part count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:51:21 -05:00
40b40ca4ba feat: unify ActionAddPart into ActionClone and add group fill support
Merge ActionAddPart into ActionClone by adding a Drawing constructor,
eliminating the redundant class. ActionClone now handles both adding
new parts from a drawing and cloning selected part groups. Added
Ctrl+F fill support for groups using FillLinear pattern tiling, and
adopted quadrant-aware push directions from ActionAddPart. Refactored
FillLinear to extract shared helpers and add a Fill(Pattern) overload
for tiling arbitrary part groups across the work area.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 09:56:48 -05:00
5807255931 feat: replace Fill method with geometry-aware FillLinear algorithm
Fill(NestItem) now uses actual part geometry instead of bounding boxes.
Also fixes Fill(NestItem, maxCount) which previously threw NotImplementedException.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:53:02 -05:00
be8b499880 feat: add NestEngine.FillLinear with 4-config rotation/axis optimization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:01:41 -05:00
98818d32e3 feat: add full plate fill with pattern-level geometry-aware tiling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:58:35 -05:00
1193091338 feat: add FillRow method for single-axis geometry-aware fill
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:56:44 -05:00
b8d9155cd0 feat: add FillLinear class with geometry-aware copy distance
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:54:06 -05:00
f4779b878c feat: add Pattern class for grouping parts with relative positions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:52:11 -05:00
67a66e10fd Move geometry primitives to OpenNest.Geometry namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:02:12 -05:00
8d9aebb83f Move math utilities to OpenNest.Math namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:51:16 -05:00
2d956fd3f7 Restructure project layout to flatten directory structure
Move all projects from Source/ to repository root for simpler navigation.
- Remove External/ dependency DLLs (will use NuGet packages)
- Remove Installer/ NSIS script
- Replace PartCollection/PlateCollection with ObservableList
- Add packages.config for NuGet dependencies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 20:29:12 -05:00