Commit Graph

513 Commits

Author SHA1 Message Date
896cb536dd feat(console): add --engine CLI flag to select nest engine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:49:43 -04:00
493f7f837a fix(engine): sort hull edge angles by longest edge first
Longest edges produce the flattest tiling rows and should be tried
first. Also deduplicates angles before sorting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:49:35 -04:00
c94beb51a4 feat(engine): try interlocking pair patterns in remainder strips
FillLinear now accepts optional RemainderPatterns that are tried in
leftover strips after the main grid fill, improving packing density
when pair patterns fit the narrow residual space.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:49:27 -04:00
c2c723f86f refactor(engine): clean up unused imports after extraction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:41:30 -04:00
29d58cc8af test(engine): add integration smoke tests for engine refactor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:39:51 -04:00
5bb637f3e2 refactor(engine): use RemnantFiller in NestEngineBase.Nest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:38:53 -04:00
75cb6b2bac refactor(engine): rewire StripNestEngine to use extracted helpers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:37:59 -04:00
c077649734 refactor(engine): rewire DefaultNestEngine to use extracted helpers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:35:25 -04:00
319eace472 refactor(engine): extract RemnantFiller for iterative remnant filling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:33:00 -04:00
c8587929b5 refactor(engine): extract PairFiller from DefaultNestEngine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:31:51 -04:00
14048b0b7c refactor(engine): make BuildRotatedPattern and FillPattern internal static
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:30:37 -04:00
a7f27480e9 refactor(engine): extract AngleCandidateBuilder from DefaultNestEngine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:29:52 -04:00
094b1e9f00 refactor(engine): extract ShrinkFiller from StripNestEngine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:28:24 -04:00
31dbbbeedc refactor(engine): extract AccumulatingProgress from StripNestEngine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:26:53 -04:00
ba7ded14b5 build: add InternalsVisibleTo for OpenNest.Tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:25:46 -04:00
4c4e8c37fb docs: add engine refactor implementation plan
11 tasks across 3 chunks: extract AccumulatingProgress, ShrinkFiller,
AngleCandidateBuilder, PairFiller, RemnantFiller, then rewire both
engines and NestEngineBase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:05:51 -04:00
0fb3a216e1 docs: fix PairFiller API signature and ShrinkFiller axis mapping
Add plateNumber parameter to PairFiller.Fill signature.
Document strip direction to ShrinkAxis mapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:51:17 -04:00
1ed6401ba3 docs: address spec review feedback for engine refactor
Fix line ranges, clarify ForceFullAngleSweep forwarding for
BruteForceRunner, document ShrinkFill two-axis independence,
acknowledge NestEngineBase.Nest behavioral improvement, add
progress reporting detail for PairFiller.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:49:04 -04:00
a510ac2acb docs: add engine refactor design spec
Extract shared algorithms from DefaultNestEngine and StripNestEngine
into focused helper classes: PairFiller, AngleCandidateBuilder,
ShrinkFiller, RemnantFiller, AccumulatingProgress.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:44:59 -04:00
5553fe6386 fix(ui): compute polylabel once from unrotated drawing
Compute the label point from the base drawing's unrotated program
and rotate the cached point instead of recomputing on each rotation.
Prevents label jitter caused by arc discretization differences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:55:29 -04:00
79a3410bb9 feat(ui): position part labels at polylabel center
Replace PathPoints[0] label positioning with polylabel algorithm
to place part ID labels at the visual center of each part. Labels
are now centered and readable even in dense nests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:46:47 -04:00
b647769b51 feat(ui): position part labels at polylabel center
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:44:29 -04:00
909b697b78 test(geometry): add PolyLabel tests for L, C, triangle, thin rect, hole
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:42:49 -04:00
44657a86b8 feat(geometry): add PolyLabel algorithm with square test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:40:25 -04:00
45bda1b2ac docs: add polylabel part label positioning implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:36:45 -04:00
65bde123ed fix(engine): fix remnant finder missing L-shaped and split remnants
Two bugs caused the remnant finder to miss valid empty regions:

1. RemoveDominated used an 80% overlap-area threshold that incorrectly
   removed L-shaped remnants. A tall strip to one side would "dominate"
   wide strips above/below it even though they represent different usable
   space. Replaced with geometric containment check — only remove a box
   if it's fully inside a larger one.

2. FindTieredRemnants split remnants at the obstacle envelope boundary,
   and both pieces could fall below minDimension even though the original
   remnant passed the filter (e.g., 6.6" remnant split into 5.35" + 1.25"
   with minDim=5.38"). Added fallback to keep the original unsplit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:03:23 -04:00
d0faf1e2b6 docs: update polylabel spec with hole support, caching fix, fallback
Address review feedback: add holes parameter for parts with cutouts,
cache label point in program-local coords to survive zoom/pan, add
fallback for degenerate geometry, use ShapeProfile for outer contour
identification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:39:18 -04:00
e7864f9dc8 docs: add polylabel part label positioning design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:37:00 -04:00
66050c68f6 feat(engine): add CompactIndividual to Compactor (disabled in strip nester)
Add plate-free Push overload and CompactIndividual method that pushes
each part individually against all others as obstacles. Disabled in
StripNestEngine pending investigation — compaction opens irregular gaps
that the remnant finder scatters parts into.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:35:07 -04:00
195e29da52 fix(ui): rotate grouped parts around shared center with dedup
Parts created by fill operations share a Program instance via
CloneAtOffset. RotateSelectedParts called Part.Rotate on each,
compounding Program.Rotate on the shared object (1x, 2x, 3x…)
and producing inconsistent bounding boxes. Fix tracks rotated
programs with a HashSet, rotates locations around the group center,
and anchors the bounding box corner to prevent drift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:11:07 -04:00
0b9a42e84c fix(engine): use smallest remaining part as minimum remnant size
Skip remnants that are too small to fit any remaining part, avoiding
wasted fill attempts. Recalculated each iteration as quantities deplete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:51:30 -04:00
00ccf82196 fix(engine): apply shrink loop to remnant fills in StripNestEngine
Remainder items were being filled into the full remnant box without
compaction. Added ShrinkFill helper that fills then shrinks the box
horizontally and vertically while maintaining the same part count.
This matches the strip item's shrink behavior and produces tighter
layouts that leave more usable space for subsequent items.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:50:14 -04:00
a41a08c9af fix(engine): use local quantity tracking in StripNestEngine remnant loop
The iterative remnant fill was mutating shared NestItem.Quantity objects,
causing the second TryOrientation call (left) to see depleted quantities
from the first call (bottom). Use a local dictionary instead so both
orientations start with the full quantities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:23:32 -04:00
3d23943b69 fix(engine): use RemnantFinder for iterative remnant filling in StripNestEngine
Replace the single-pass guillotine split approach with RemnantFinder-based
iteration. After each fill, re-discover all free rectangles and try all
remaining items again until no more progress is made. This fills gaps that
were previously left empty after the initial strip + remainder layout.

Also change ActiveWorkArea border color from orange to red.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:11:28 -04:00
51b482aefb test: add RemnantFinder edge cases and FillScore comparison tests
RemnantFinder: obstacle clipping, overlapping obstacles, iterative
workflow, grid pattern, no-overlap invariant, constructor/AddObstacles.
FillScore: count-vs-density ordering, operators, Compute edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:02:15 -04:00
6419f6b8a2 feat: report ActiveWorkArea in NestProgress from ReportProgress
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:58:17 -04:00
4911d05869 feat: wire ActiveWorkArea from NestProgress to PlateView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:57:51 -04:00
2b4f7c4e80 feat: draw active work area as dashed orange rectangle on PlateView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:56:37 -04:00
2c62f601ca feat: add ActiveWorkArea property to NestProgress
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:55:54 -04:00
2bda7c9f0f refactor: remove StripNestResult.RemnantBox
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:54:19 -04:00
9d99e3a003 refactor: update MCP tools to use RemnantFinder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:53:48 -04:00
b42348665f refactor: remove Plate.GetRemnants(), replaced by RemnantFinder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:53:29 -04:00
4d30178752 refactor: replace ComputeRemainderWithin with RemnantFinder in Nest()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:51:25 -04:00
2b578fa006 refactor: remove NestPhase.Remainder enum value and switch cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:50:48 -04:00
78c625361e refactor: remove remainder phase from DefaultNestEngine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:50:26 -04:00
dd3a2b0e9a refactor: remove UsableRemnantArea from NestProgress and UI 2026-03-16 12:47:57 -04:00
9b21a0c6d7 fix: update FillWithPairs debug logging after FillScore simplification 2026-03-16 12:47:11 -04:00
5b9e6c28e4 refactor: simplify FillScore to count + density, remove remnant tracking 2026-03-16 12:46:53 -04:00
ecdf571c71 feat: add RemnantFinder with edge projection algorithm 2026-03-16 12:45:35 -04:00
f5ab070453 test: add RemnantFinder tests (red) 2026-03-16 12:44:53 -04:00