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.
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.
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.
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.
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.
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.
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.
The readiness filter rebuilt the finished-part list for every candidate.
Build one set per expanded node; the candidates and their order are
unchanged.
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.
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.
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.
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.