Commit Graph
458 Commits
Author SHA1 Message Date
aj 17e504fb70 feat(cutting): navigate diagnostic rows in enlarged planner 2026-10-10 11:37:57 -04:00
aj 1c6d02c480 perf(cutting): try a bounded entry catalogue before full search 2026-10-09 21:26:40 -04:00
aj d328c5f691 perf(cutting): generate ranked alternatives only when visited 2026-10-09 20:40:10 -04:00
aj e897258a85 feat(cutouts): reconcile repeated frames and inserts through one solve 2026-10-09 20:36:43 -04:00
aj 1b9d8a36d8 fix(pipeline): certify bound programs and cancellation before commit 2026-10-09 20:28:05 -04:00
aj b750475228 fix(pipeline): refuse changed drawings before binding or commit 2026-10-09 20:15:17 -04:00
aj 0b29c683b9 feat(cutouts): trial physical composite reconciliation in pipeline 2026-10-09 18:57:31 -04:00
aj 71bd964344 test(cutouts): trace accepted lattice and NFP poses in one run 2026-10-09 17:53:22 -04:00
aj 1d50c6ce60 feat(cutouts): compare measured ratio routes and certify leftovers 2026-10-09 17:47:12 -04:00
aj 962ae8c825 feat(cutouts): propose bounded mixed lattice and NFP inserts 2026-10-09 17:20:41 -04:00
aj 575412a464 feat(cutouts): propose certified single inserts with inner-fit geometry 2026-10-09 16:59:11 -04:00
aj 8628c69098 fix(ui): settle shift-click cloned parts with repeated directional pushes 2026-10-08 18:57:39 -04:00
aj 7e887921ea fix(cutting): expose every unverified warning before consent 2026-10-08 13:50:12 -04:00
aj 81f3c006ab merge: integrate best-effort cutting on current master 2026-10-08 13:34:05 -04:00
aj 1c923976a8 fix(cutting): explain missing leads and suggest clearance remedies 2026-10-07 17:50:31 -04:00
aj 9196d9157f fix(cutting): reuse clear candidates across side coverage scans 2026-10-07 16:05:25 -04:00
aj 7468483a10 fix(cutting): retain closest-arrival fallback for the last part 2026-10-07 15:56:20 -04:00
aj 432eb54633 fix(cutting): validate choice ownership before cached verdicts 2026-10-07 15:53:28 -04:00
aj 64459f7bc8 fix(cutting): try endpoint alternatives before hole combinations 2026-10-07 15:21:09 -04:00
aj 414e9b4e75 fix(cutting): prefer hole entries facing the next contour 2026-10-07 14:58:23 -04:00
aj 76f73f7a87 fix(cutting): exclude scribes from downstream pierce targets 2026-10-07 14:46:30 -04:00
aj 6b7d0d7f90 feat(cutting): chain preferred hole entries to the actual pierce
PreferredContourEntries resolves one entry per hole by walking the
proposed hole route BACKWARD from the already-chosen perimeter entry.
Each hole ranks against the downstream contour's ACTUAL emitted pierce
(first non-rapid motion of the owned probe emission, native rounding
included), so the cut chain flows toward the outside start rather than
chaining on nominal points that rounding may move.

Catalogue is the S04 merged list (preferred kinds where present,
compass/arc fallbacks otherwise — pure circles included); ordering is
the S05 facing/tier/travel ranker; feasibility is one S08-bounded pick
per hole through caller-supplied S07 verdicts. A hole with no feasible
candidate yields an explicit no-preference proposal naming the contour
and reason — never a silent skip, never a partial chain. Arrival proxies
(part arrival for the first hole, previous hole centre otherwise) keep
the pass deterministic and circularity-free.

Deterministic (repeat runs identical), cancellation-aware, ownership-
checked, source program untouched, fresh owned probe programs only.
This is a recommended-v1 proposal: nothing is installed into the search
and no rapid is certified here — S12 wires it and must handle a blocked
preferred rapid.

Tests: circle pair flows hole->hole->outside via actual pierces (each
pierce strictly nearer downstream than the opposite compass pierce);
rectangular holes pick right-facing corners under all-clear evaluation;
blocked ideal candidate falls back to next-ranked on the same hole;
blocked hole yields honest no-preference finding; permutation integrity
in route order excluding the perimeter; determinism; foreign perimeter
refused; cancellation; fingerprint/ownership/fresh-program invariants.
Gate 9/0/0; full Core 3580/25skip/0; cross-build 0 errors; scoped
format verify 0; facing-drop and silent-skip mutants killed; nominal-
vs-actual-pierce mutant documented near-equivalent.
2026-10-07 13:18:05 -04:00
aj fa3c5e348e feat(cutting): propose hole paths toward the perimeter entry
CuttingHoleOrder proposes the cut order of a part's remaining holes as
an OPEN path arrival -> every hole centre exactly once -> the fixed
perimeter entry, reusing CuttingPartOrder's bounded nearest-neighbour +
2-opt/Or-opt machinery (no exhaustive permutations, no new routing
invention). A proposal only: the search still certifies every rapid and
emitted lead; nothing is installed into the search yet.

CuttingPartOrder gains an optional endpoint: the terminal edge joins
EVERY improvement delta (2-opt tail edge on both sides; OrOpt gap to the
open end measures the endpoint distance), so heuristics see route moves
that re-point the path at the entry rather than only final scoring. No
endpoint keeps whole-part semantics byte-identical (control test);
precedence, prerequisites and tie policy unchanged.

Tests: empty/one hole; asymmetric fixture where the endpoint changes the
ORDER (open route ends at C, entry-facing route ends at B) with each
route asserted equal to its brute-force optimum computed from
coordinates; permutation integrity excluding the perimeter; random 7-set
within 1.05x brute force; deterministic tie order; cancellation; legacy
no-endpoint equality. Mutants dropping the terminal edge from OrOpt or
TwoOpt deltas are killed.
2026-10-07 11:43:26 -04:00
aj d4544ea358 fix(cutting): aim perimeter entries toward the next part
No-hole parts now choose the outside entry through the S03-S08 pipeline
at the part boundary: rank the native catalogue toward the NEXT cut's
placed-material centre, lazily certify each emitted lead with the shared
validator, cap at maxEntries with side coverage. The next cut is the
next unfinished part on the supplied order (re-read after every
learned-order replan) or, in sequence-free fallback, the nearest
dependency-ready remaining part with stable ordinal ties; the last part
has no target and ranks by tier then arrival distance — never the plate
origin. Target and arrival are converted to prepared LOCAL coordinates
exactly once; geometry is already rotated.

The look-ahead rank survives Follow's ordering: children sort by
nearest source first (the sequence-free tour stays nearest-first), then
contour, then the selection rank — plain OrderBy(Distance) can no longer
undo the facing. Measured fixture (three squares, 0.15 leads, origin
start): legacy cut every sheet at its arrival-nearest lower-left corner
with 10.5-unit cross-sheet rapids totalling 21.0; look-ahead cuts the
+X-facing corner with the same 21.0 total but each inter-part rapid now
starts at the facing edge instead of trailing across the whole sheet.

Uncertain validator answers are never precheck-refused: they skip the
selected slots but flow to the emitted-prefix Check and complete replay,
which stay the authority — a refused-looking incomplete is reported,
not hidden. A fully evaluated catalogue with no fitting lead surfaces
'No tested lead-in fits on part N, contour M'; budget exhaustion stays a
budget finding. Lead prechecks count separately from DFS expansions; the
pipeline itself is one counted expansion per contour. Full backtracking,
dependencies, locked/fixed programs, cutoff handling, emitted-prefix
Check and complete replay are unchanged; budgets and CuttingPartOrder
untouched. Hole parts keep the legacy path until S12.
2026-10-07 11:01:08 -04:00
aj acd1f21293 feat(cutting): cap feasible entries with side fallback coverage
ContourEntrySelection: lazy bounded selection over one contour's ranked
candidates. Greedy global rank order until the cap (default 16) fills or
the finite catalogue ends; blocked and incomplete candidates never
consume a slot, and once selection settles the untouched tail is never
evaluated (EvaluatedCount/EvaluatedKeys pin the counts, not timings).
At caps of 4+ a corrective scan represents every side of the candidate
bounding rectangle that HAS a feasible candidate — lazily chasing each
missing side and replacing the worst selected candidate only when every
other covered side survives; one corner may cover two sides. The cap is
never exceeded and points are never manufactured on infeasible sides;
caps 1-3 obey cap+ranking first, as promised. Verdicts memoize per
attempt; duplicate geometric points never take two slots.

Honest metadata: Choices/EvaluatedCount/Shortfall/Reason with None,
Exhausted and Incomplete kept distinct — 'no tested lead-in fits' is
producible only from a fully evaluated Exhausted catalogue; incomplete
checks and cancellation are never presented as geometric impossibility.
Sides are exactly the ranker's bounding-rectangle geometry. No search
changes and no larger search budget.
2026-10-07 06:25:16 -04:00
aj 2399cc3936 feat(cutting): precheck emitted entry leads with the shared validator
ContourEntryFeasibility: one adapter over the EXISTING LeadPathValidator
for one owned candidate — emit through the S06 diagnostic seam, read at
the placement position, certify emitted lead-in AND lead-out against ALL
placed material. Only complete+clear qualifies; blocked and incomplete
keep their reasons as distinct verdicts (an incomplete check never
reads as clear). Not a new collision implementation and not a plan
approval: NoLeadIn passes vacuously and the complete-plan missing-lead
check still runs later; rapids and pierce clearance stay with their
existing checkers.

One instance is one captured planning attempt: verdicts cache per exact
owned choice + node context (settings/placement are fixed per instance),
never static, never across instances. Lazy: only the checked candidate
is emitted; EvaluationCount counts validator executions for cost tests.
Foreign/malformed emissions are Incomplete verdicts with reasons, never
crashes; cancellation propagates without poisoning the cache. No search
changes and no broad-phase skips.
2026-10-07 05:27:05 -04:00
aj 12206ed8fd feat(cutting): offer unverified best-effort plans for imperfect geometry 2026-10-06 14:44:53 -04:00
aj 5af44b251c feat(engine): make Default choose between Irregular and Rectangles per job
Default is the engine every front end uses when none is named. It now runs
Irregular, then Rectangles, checks both layouts with NestLayoutCheck and keeps
the best: valid first, then fewest unplaced parts, then lowest salvage-credited
cost; ties keep Irregular. A candidate that throws or returns nothing is
skipped, cancellation stops the search, and only the chosen layout's plate
commits are reported. Neither engine wins every job in the lane benchmarks,
and Rectangles adds little time while also covering Irregular's invalid
layouts.

The registry lists Default first and no longer maps the name to Fill; fill
strategy callers still read Default as Fill. A future circle/ring engine joins
as another candidate.
2026-10-06 12:26:10 -04:00
aj 0a1d021bf7 refactor(engine): rename the Default engine and fill strategy to Fill
The multi-phase lattice fill (linear, pairs, rectangle best-fit, remainder) is
no longer meant to be the engine used by default, so it gets a name for what
it does. The registry lists it as Fill and maps the old name Default to it;
PlateFillService, PlateNesterFactory and NestJobOptions use Fill, and every
fill-strategy caller still accepts Default. Console --autonest now validates
engine names through NestingEngineRegistry.ResolveName so renamed names work.

Layouts are unchanged: Default and Fill resolve to the same fillers and the
golden layouts pass under the new name.
2026-10-06 12:11:03 -04:00
aj bc061488ee merge: integrate refactor/menubar-reorg
Brings in the Plate menu reorganization, Database-mode drag-and-drop
open, the SavedNestsForm look fixes and the Irregular block-fill work
budget fix.

Conflicts:
- MainForm.Designer.cs: keep the branch's Plate menu order (Arrange
  submenu, View in CAD last) and add master's Plan Cutting item ahead
  of the lead-in commands.
- SavedNestsForm.cs: keep master's browser rewrite (details, preview,
  columns) and apply the branch's intent to it: open maximized with
  MaximizedBounds clear of the taskbar, the lighter grid palette on all
  three grids, and B/KB/MB/GB file sizes.
2026-10-06 06:39:41 -04:00
aj 4e6419fd2c fix(cutting): fall back to a full order search when learning stops
Review found a plate the previous search planned that the tour now
refused. Left leads in on its left and right on its right, so right
straight after left crosses left and left straight after right crosses
right. Learning "right before left" then contradicted "left before
right" and the search returned ConstraintConflict, although cutting a
third part above them in between is safe.

A blocked approach only proves that one part cannot follow the parts
cut so far from that position, not a global order, so learned rules
stay a heuristic. Once nothing new can be learned, the remaining budget
now goes to a full search over every ready part, nearest first (the
search used before the tour). A rule contradicting an order already
required is still skipped rather than ending learning early.
2026-10-06 00:04:05 -04:00
aj 2419a9e2e3 fix(cutting): give a new part order the plate's expansion budget
The desktop batch searched for a new part order with the flat 20000
default and kept the current order with 400 expansions per part. A new
order now also plans contour order and entries for every part (about
260 expansions per part on a dense grid), so a 100-part plate ran out
of budget and fell back to the current order. Both attempts now get
PlateBudget: 400 per part, at least 20000. The constants are renamed
MinimumExpansionBudget and ExpansionsPerPart to match.
2026-10-05 23:41:16 -04:00
aj b8f3d3bf0a fix(cutting): order parts as a short tour and learn blocked approaches
Free-order planning was one depth-first search over parts, contours and
entries. A dead end at one part backtracked through every entry
combination of the part before it (about 1,450 for a square with two
holes) before trying another part order, so a 4 x 4 grid of such parts
ran out of its 20000 expansions (and 200000) although cutting it row by
row is safe.

The whole-part order is now an open travelling-salesman path over part
centres from the start point: nearest neighbour, then 2-opt reversals
and Or-opt moves of one to three parts, never placing a part before a
cutoff or nested-part prerequisite. The existing search then plans
contour order and entries along that order. If a part cannot be
reached without crossing parts already cut, the search learns "cut it
before those", backs up to just before the earliest of them, keeps the
parts cut before that point and re-plans the rest from the tool
position there. An attempt stops backtracking after 8 x entries x
contours expansions without getting further, so it learns instead of
retrying the entries of every earlier part. When nothing new can be
learned the result is a refusal, as before. A preserved order is
planned exactly as before.

16- and 36-part grids, in row order and shuffled, are now ready within
the default budget (they were NoSolutionWithinBudget); a 144-part grid
plans in about half a second.
2026-10-05 23:41:11 -04:00
aj 8590f21c9c fix(cutting): block overlapping plates and preview only fresh plans
Review fixes for the Plan Cutting batch and dialog:

- A plate whose clean part material overlaps, or cannot be checked
  for overlap, is no longer ready, whatever its route. The batch
  captures each plate's material with PlateOverlapAnalyzer on the
  owner thread, analyzes it on the worker, and names both parts.
  Before, two overlapping squares were Ready and Apply regenerated
  them (plan section 4.2: overlap warnings are not waived).
- BuildPreview returns null for a refused plate, whose program graphs
  may be unsafe to copy (an unsupported instruction's Clone ran, and a
  cyclic subprogram overflowed the stack), and for a plate that
  changed after planning, which drew the replayed program at the live
  pose. The dialog then shows no preview and says why.
- The dialog plans with its own copy of the caller's settings, shows
  a failure message if a plan cannot be presented, and has a worker
  seam so the close-while-planning test holds the worker instead of
  racing a slow search. Form tests now observe the planning task.
2026-10-05 21:39:58 -04:00
aj 9a4f73b924 feat(cutting): plan and apply plates as one desktop batch
CuttingPlanBatch captures every scoped plate on the owner thread with
an owned copy of the confirmed cutting parameters, plans them on a
worker with per-plate progress and cancellation, and returns a
proposal that applies all or nothing through CuttingPlanService.Apply.

A free-order search that ends NoSolutionWithinBudget is retried once
with the current part order, captured up front so the worker never
reads live plates; the proposal reports that the order was kept. A
kept order gets 400 expansions per part (at least 20000), since it
still searches contour order and entries.

Only after Apply succeeds does each plate keep its own copy of the
settings. The proposal builds detached preview plates (quantity zero)
and describes each plate's status and findings with part numbers.
2026-10-05 21:21:38 -04:00
ajandClaude Sonnet 5 eda4a846f0 fix(irregular): charge nested block-fill cost against the work budget
BlockCatalog's PrepareBlocks pre-pass ran a full independent NFP-based
pack (PrivatePlateFill.Run) for up to 8 attempts per part type, but that
cost never touched the engine's WorkCounter/WorkBudget. For jobs with
several part types at double-digit quantities, these uncounted nested
solves could burn the entire 5-minute timeout inside the first sheet of
the first strategy variant, so Solve() never returned and nothing was
ever committed.

Charge quantity^2 * outline vertex count against the shared counter
before each nested fill, and stop issuing new block proposals once the
budget is exhausted, so the existing effort governor actually sees and
bounds this cost.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-05 09:29:46 -04:00
aj 32db086384 perf(irregular): cap surplus rows in private stripe fills 2026-10-05 09:03:49 -04:00
aj b8afcc6fdd perf(cutting): build finished ordinals once per search expansion
The readiness filter rebuilt the finished-part list for every candidate.
Build one set per expanded node; the candidates and their order are
unchanged.
2026-10-05 00:29:47 -04:00
aj 1d8c5fe274 fix(cutting): create plate-scoped requests through a factory
A CuttingPlanRequest constructor overload taking a Plate made the existing
detached call new CuttingPlanRequest(null) ambiguous (CS0121). Plate scope
is now requested with CuttingPlanRequest.ForPlate, and the result summary
describes dependencies and Apply as they now behave.
2026-10-05 00:28:59 -04:00
aj e43a7c99d0 fix(cutting): find nested parts from material bounds
The containment prefilter used the inner part's whole clean-program
bounds, which include rapid endpoints and scribe marks. A remote rapid or
mark could push those bounds outside the host and drop a genuine
inner-before-host prerequisite. Candidate pairs now use the material
extent only (cut and display motions); containment is still proven on
native material.
2026-10-05 00:27:31 -04:00
aj 4bbd8454b5 feat(cutting): order cutoffs and nested parts before their hosts
The cutting planner now accepts cutoffs on plate-scoped requests and
plans whole-part prerequisites captured from owned values:

- A cutoff precedes every part its nominal span crosses, using the same
  rule and drawing-reference matching as automatic sequencing; a cutoff
  without a definition precedes every part. Cutoffs stay fixed programs,
  need no lead-in and never become rapid obstacles; rapids into and out
  of them are still checked.
- A part proven, on native clean material, to lie inside a cutout of
  another part precedes that host. Touching or crossing boundaries are
  ambiguous and refuse; a part in a concave pocket has no dependency.
- Both searches only expand ready parts, a preserved order that breaks a
  prerequisite is a constraint conflict, and final replay rechecks the
  captured prerequisites instead of trusting the search.
2026-10-05 00:01:29 -04:00
aj f3bca26fb9 refactor(sequencing): share the cutoff crossing rule with cutting planning
Move the nominal-span cutoff rule unchanged into CuttingDependencyGraph so
automatic sequencing and the cutting planner apply the same dependency.
2026-10-04 23:50:58 -04:00
aj 9c530ca7d2 feat(cutting): apply verified plans atomically
Plate-scoped cutting plan requests now record the plate's exact state at
capture, and CuttingPlanService.Apply installs Ready, replayed proposals
for a whole scope at once:

- Any change after capture (order, pose bits, program reference or
  in-place content, drawing program, lock/lead-in flags, settings,
  quantity, size, quadrant or cutoff definitions) returns Stale with
  nothing changed.
- Order changes without PartAdded/PartRemoved, so drawing quantities and
  sentinel plates are untouched; ObservableList.Reorder exposes the same
  operation and Plate.PartsReordered is raised once per changed plate.
- Regenerated parts receive owned copies of the replayed program and of
  the settings captured with the request; fixed programs stay in place.
- An install failure restores every plate exactly; an observer failure
  after publication is reported as a refresh error, not a rollback.
2026-10-04 23:48:28 -04:00
aj ebc54f0653 refactor(cutting): move owned program copies into Core
Core's commit freshness check needs the same lossless program copy the
planner uses at capture. Move it unchanged apart from its namespace.
2026-10-04 23:47:56 -04:00
aj 6f7652ee83 fix(cutting): preserve clean geometry through shared graph rotation 2026-10-04 22:26:22 -04:00
aj e8d974a2db fix(cutting): certify complete selected contour programs 2026-10-04 22:02:56 -04:00
aj 9f7580a486 fix(cutting): bind replay proposals to exact captured pose bits 2026-10-04 21:56:49 -04:00
aj fb7ef6fd0d fix(cutting): retain authored motion metadata in owned programs 2026-10-04 21:56:49 -04:00
aj e00aa113a0 fix(cutting): validate original graphs before capture clones 2026-10-04 21:56:49 -04:00
aj 70d886e4cf feat(cutting): jointly plan contour order and entry points 2026-10-04 21:12:55 -04:00