mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-10 23:34:14 -04:00
411 lines
27 KiB
Markdown
411 lines
27 KiB
Markdown
# Unified cutting planner: direct-XY proposals
|
|
|
|
`OpenNest.Engine.CuttingPlanning.CuttingPlanService` plans contiguous whole-part
|
|
programs. It can retain fixed programs or jointly choose internal contour order,
|
|
entries and whole-part order using explicitly confirmed cutting parameters. It
|
|
returns an owned proposal; `Apply` installs Ready plate-scoped proposals atomically
|
|
after an exact freshness check. The desktop opens it from `Plate > Plan Cutting...` and
|
|
`Nest > Plan Cutting (All Plates)...` (see [Desktop workflow](#desktop-workflow)); the older
|
|
automatic sequencing and lead-in assignment commands remain until they are retired.
|
|
|
|
## Capture before worker planning
|
|
|
|
Create a `CuttingPlanRequest`, then call `Capture` while source placements,
|
|
programs and settings are stable. Pass that snapshot to `Plan` on a worker;
|
|
`Plan(request)` combines these steps synchronously. The modeled starting point
|
|
defaults to `Vector.Zero`, not a discovered controller position.
|
|
|
|
```csharp
|
|
var request = CuttingPlanRequest.ForPlate(plate, startPoint: start,
|
|
confirmedParameters: parameters, expansionBudget: 20000,
|
|
maxEntries: 16, preservePartOrder: false);
|
|
var snapshot = CuttingPlanService.Capture(request, cancellationToken);
|
|
var result = CuttingPlanService.Plan(snapshot, cancellationToken);
|
|
```
|
|
|
|
- A plate-scoped request (`CuttingPlanRequest.ForPlate`) plans the plate's current
|
|
parts and records its exact state for `Apply`. A detached part list (`new CuttingPlanRequest(parts, ...)`)
|
|
plans the same way but can never be applied. An empty plate is a Ready no-op.
|
|
- Omitting `confirmedParameters` preserves the original fixed-program contract:
|
|
locked and unlocked programs stay fixed; only whole-part order may change.
|
|
- Supplying confirmed parameters enables regeneration for unlocked placements.
|
|
`eligibleParts` can restrict it to an explicit reference-based subset; an empty
|
|
subset retains all programs but still checks their leads against owned material.
|
|
Locked placements never regenerate. Foreign or duplicate eligible identities
|
|
are invalid; eligibility without confirmed parameters is invalid.
|
|
- `preservePartOrder` fixes whole-part order, not eligible internal contour choices.
|
|
- Parameters are caller-confirmed inputs. The service does not recover missing
|
|
operator settings or silently change lead styles to find a solution.
|
|
|
|
Capture owns clean geometry, placed programs and required settings. Source `Part`
|
|
references are identity handles only: workers never read their mutable state.
|
|
Clean geometry accounts for the base program's existing rotation before applying
|
|
placement rotation; placement translation is applied once. Subprogram copying
|
|
must not rotate shared programs through their property setters. No live drawings
|
|
are attached to preview plates, so capture/search do not change quantity accounting.
|
|
Planning works from this historical snapshot; Apply compares it with live state.
|
|
Original
|
|
clean and executable graphs are type/mode-checked before cloning can erase unknown
|
|
semantics. Exact placed/proposed copies preserve authored motion feed/exact-stop
|
|
flags, symbolic bindings and shared subprogram identity; unsupported graphs are
|
|
refused. The geometry-only clean transform uses per-parent copies so legacy rotation
|
|
does not visit a globally shared descendant twice; it never changes the fixed payload.
|
|
|
|
## Cutting dependencies
|
|
|
|
Capture builds whole-part prerequisites from owned values, and both the search and
|
|
the final replay enforce them:
|
|
|
|
- A cutoff precedes every part its nominal line crosses within its span, using the
|
|
same rule as automatic sequencing: nominal position and limits against the part's
|
|
placed bounds, matched by drawing reference, never by name or trimmed segments. A
|
|
cutoff whose definition is missing precedes every part. Cutoffs need a
|
|
plate-scoped request; they are always fixed programs, need no lead-in, are not
|
|
material for lead validation and, being open cuts, never become rapid obstacles.
|
|
Rapids into and out of them are still checked.
|
|
- A part whose perimeter lies strictly inside a cutout of another part precedes
|
|
that host. Material bounds (never rapids or scribe marks) only select candidate
|
|
pairs; containment is proven on native clean material. Touching or crossing boundaries, or material that cannot be
|
|
captured, refuse as `UnsupportedGeometry` naming both parts. A part in a concave
|
|
pocket outside the host's material has no dependency.
|
|
- A preserved manual order that violates a prerequisite, or a cycle, is a
|
|
`ConstraintConflict`. Replay rechecks the captured prerequisites and refuses a
|
|
violating order rather than trusting the search.
|
|
|
|
## Search and exact output
|
|
|
|
With regeneration, the bounded deterministic search plans internal contour order
|
|
and native entry candidates part by part along a whole-part order. Internal
|
|
contours precede their own perimeter; parts remain contiguous. Backtracking can
|
|
revisit an earlier entry when a later part cannot be reached safely. For multi-part
|
|
requests containing regenerated holes, it first tries one ranked hole chain for
|
|
each outside endpoint. This lets a later blocked approach change the previous
|
|
part's departure without first exhausting combinations of its earlier holes.
|
|
This preferred pass uses the same emitted-motion checks, shared expansion budget
|
|
and stall limit as the retained search; on failure the full entry/hole-order
|
|
backtracking pass remains available. Single-part and no-hole requests keep their
|
|
existing search order.
|
|
|
|
A preserved order is followed as given. Otherwise the order is 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. If a part on that order cannot be
|
|
reached without crossing parts already cut, the search learns "cut this part
|
|
before those", backs up to just before the earliest of them and re-plans the rest
|
|
from the tool position there; parts cut before that point are kept. An attempt
|
|
stops backtracking after a stall of 8 x entries x contours expansions without
|
|
getting further, so it learns instead of retrying every entry combination of the
|
|
parts before it. A rule that would contradict an order already required is skipped.
|
|
"Cut before" rules are a heuristic (a part blocked straight after another may be
|
|
reachable via a third), so once nothing new can be learned the remaining budget
|
|
goes to a full search that tries every ready part, nearest first; only when that
|
|
also fails is the result a refusal.
|
|
|
|
Candidates use native closest points, vertices, midpoints and circle angles in
|
|
stable order, capped by `maxEntries`. Circle rounding, clamping, corner resolution
|
|
and tab trimming happen during emission. Validation uses the actual emitted
|
|
motions, never the nominal entry point alone. Existing lead styles are not
|
|
shortened, disabled or substituted as a search fallback.
|
|
|
|
Whole-circle candidates rank by distance to their next-cut target; for a hole,
|
|
that is the next contour's actual pierce. They do not reward a diagonal point as
|
|
if it were a bounding-box corner. All eight
|
|
compass options remain available, including the four polar points at 0°, 90°,
|
|
180° and 270°, and every alternative still passes the emitted-lead and rapid checks.
|
|
Without a next-cut target, the arrival-based rule is unchanged. Polygon corner
|
|
preferences are unchanged.
|
|
|
|
`Round Lead-In Angles` remains an explicit cutting setting: a 90° increment snaps
|
|
circular-hole starts to the four polar directions; 45° also permits diagonals.
|
|
Snapping can increase reuse of identical hole subprograms and reduce output for
|
|
posts that support that reuse, at the cost of a less direct departure. It is not
|
|
a clearance exemption or a guaranteed file-size reduction. The planner does not
|
|
silently enable rounding, change the increment, or bypass checks on the rounded
|
|
motions.
|
|
|
|
Every candidate rapid is checked against contours already completed, including
|
|
earlier holes in the same part. Future contours are not yet obstacles. Rapid
|
|
checks skip completed contours whose extents are more than 0.001 clear of the
|
|
rapid, ten times the widest band any native contact query allows beyond an
|
|
extent; anything closer, touching included, gets the full native check. Lead
|
|
checks examine every other part's material: native line/circle queries can report
|
|
rounding contacts for long leads far from small circles, and leads keep those
|
|
results. An arc's
|
|
extent is its whole supporting circle widened to the distance at which the native
|
|
contact query still counts it as touched (for a very small arc up to 0.0001 beyond
|
|
its radius). Nothing is skipped when either extent has a nonfinite bound or reaches
|
|
beyond 1e6, where rounding of large supports can exceed any fixed margin. Actual
|
|
lead-in and lead-out line/arc paths must stay in target scrap and avoid other
|
|
placed material; holes in other parts remain scrap. Tangent/coincident contacts
|
|
outside the genuine target contour joint and numerically uncertain queries refuse.
|
|
Material capture supports a simple closed perimeter minus disjoint, non-nested
|
|
holes; unsupported topology is not a bounding-box approximation. A line meeting a
|
|
tangent arc at a shared vertex, such as a fillet, is an ordinary joint: an exact
|
|
contact that the native query rounds away is not uncertain at a line endpoint the
|
|
other curve already touches, while a contact anywhere else on the line still refuses.
|
|
|
|
Source parts rank by modeled travel (material-centre distance at a holed-part
|
|
boundary); within a part, preferred contour order and facing-entry rank precede
|
|
travel. Ties use stable source/contour/entry ordinals. Hash values and drawing names
|
|
are not tie breakers.
|
|
|
|
When the next whole part is fixed by the tour or a part is already active, the
|
|
search generates and checks contour alternatives on demand in contour/entry rank
|
|
order. It does not emit every sibling before trying the first. Backtracking still
|
|
visits the remaining alternatives when needed; none is pruned. The free whole-part
|
|
fallback still evaluates all ready sources before ranking their travel distances.
|
|
|
|
The expansion budget counts evaluated candidates and frontier ranking, including
|
|
rejections, before emission; it is not a wall-clock timeout. An unvisited sibling
|
|
consumes no additional DFS-prefix emission work or per-sibling expansion charge;
|
|
entry-catalogue prechecks still emit isolated contours and consume budget.
|
|
Consequently a bounded search can
|
|
reach a different frontier and report different rejected approaches than eager
|
|
expansion did. All visited candidates retain their lead/rapid checks, and a selected
|
|
plan still receives fresh independent replay. Callers can cancel. Exhaustion returns
|
|
a refusal, not an unranked fallback or proof of geometric impossibility.
|
|
|
|
## Automatic outside entries and look-ahead
|
|
|
|
An unlocked part's outside entry is chosen automatically toward the NEXT cut: the ranker orders the native candidate
|
|
catalogue by the facing side(s) of the next part's placed-material centre, and
|
|
the shared lead validator certifies each emitted lead lazily until up to
|
|
`maxEntries` feasible candidates remain. At caps of four or more, a corrective
|
|
scan reconsiders memoized clear candidates for each missing side before evaluating
|
|
more of the catalogue; a later side cannot lose a usable point merely because
|
|
an earlier side's scan passed it. Replacements preserve other covered sides and
|
|
global rank, never exceed the cap, and stop evaluating the tail once coverage
|
|
settles. Smaller caps retain rank priority rather than promising all-side coverage.
|
|
The next cut is the next unfinished part in a supplied order — recomputed
|
|
after every learned-order replan — or, in the full fallback search, the nearest
|
|
dependency-ready remaining part, stable-ordinal ties; the last part has no
|
|
target and ranks by tier then distance to the tool's arrival. Between source
|
|
parts the tour stays nearest-first; the look-ahead rank only orders the entries
|
|
inside one part's contour stage, so distance sorting cannot undo the facing.
|
|
Uncertain (numerically incomplete) validator answers are not geometric refusals:
|
|
those candidates can fill remaining retained slots for emitted-prefix checking and
|
|
complete replay. The total retained entry count stays within `maxEntries`. A
|
|
part/contour with no fitting lead in its fully evaluated catalogue is reported
|
|
as "No tested lead-in fits on part N, contour M"; budget exhaustion stays a
|
|
budget finding and incomplete checks are never presented as geometric
|
|
impossibility. Lead prechecks are reported separately and their requests also consume
|
|
the shared expansion budget before native work.
|
|
|
|
For a holed part, the search chooses an outside endpoint before cutting any hole.
|
|
Each endpoint branch builds an open hole-centre route from the original arrival to
|
|
that endpoint, using bounded nearest-neighbour, 2-opt and Or-opt improvement.
|
|
Preferred hole entries are then resolved backward from the outside's actual emitted
|
|
pierce, ignoring scribe marks: each hole faces the following contour's actual pierce.
|
|
Convex corners lead the preference tiers, then straight midpoints/tangent joints,
|
|
then native fallbacks. Reflex/cusp corners remain manual-only.
|
|
|
|
This preference orders the search; it never certifies a rapid or prunes alternate
|
|
retained entries or hole orders. A different outside endpoint recomputes its hole
|
|
preference. Every standalone emitted prefix is replayed from the original part
|
|
arrival and a copy of the checker from before that part, not from the previous
|
|
prefix (which would double-consume holes and scribes). Holes are cut once, the
|
|
outside last, and scribes once. Locked programs remain exact. A crossed preferred
|
|
route must recover through checked backtracking or return a refusal, never unsafe
|
|
`Ready`.
|
|
|
|
Selected programs are replayed from the beginning with a fresh checker and fresh
|
|
lead validation, without regenerating them or trusting cached search verdicts.
|
|
Before replay, expected-emission geometry is independently built from the owned
|
|
choices/settings, not from the candidate payload. Replay checks actual selected
|
|
code against it and independently accounts for directed native boundary coverage:
|
|
no partial, duplicated, retraced or reversed cuts, except the exact selected tab.
|
|
Equivalent subdivisions and merged collinear moves remain valid. Captured source
|
|
identity and pose binding use exact scalar bits, not geometric tolerance.
|
|
Arrival positions use actual departures, including lead-outs and subprograms.
|
|
`ProposedOrder` retains source identities/poses; `CopyProgram()` returns an
|
|
independent deep copy of each exact captured/generated program. `ContourChoices`
|
|
are nominal choice metadata, not a substitute for reading actual execution.
|
|
Neither obtaining a proposal nor copying its programs installs them on live parts.
|
|
|
|
## Results and refusal
|
|
|
|
`Ready` and `IndependentlyReplayed` describe the modeled proposal only. In the
|
|
no-parameter fixed route, replay checks rapid crossings, missing leads and
|
|
incomplete retention; it does not add regeneration-mode material/lead checks.
|
|
In regeneration mode, replay also checks actual lead paths and contour accounting
|
|
against owned clean material. Neither mode certifies final NC, production cutting
|
|
readiness or physical machine safety.
|
|
|
|
Findings and source ordinals use the original zero-based source list, not proposed
|
|
sequence positions. Strict service refusals contain no proposed order. The desktop batch may
|
|
produce a separate `BestEffort` proposal as described below; it never labels it `Ready` or
|
|
sets `IndependentlyReplayed`.
|
|
|
|
- `ConstraintConflict`: fixed programs or explored fixed routing violate the
|
|
modeled constraints. Locked internal crossings cannot be repaired by regeneration.
|
|
- `UnsupportedGeometry`: unsupported motion/material semantics or an incomplete
|
|
check. Open nominal outlines, ambiguous release states or containment, cutoffs
|
|
in a detached part list and scribe-only source drawings are not silently accepted.
|
|
- `InvalidInput`: malformed/missing/duplicate placements or settings, invalid
|
|
geometry, empty input, invalid eligibility or nonpositive bounds.
|
|
- `NoSolutionWithinBudget`: the bounded/capped search found no complete proposal;
|
|
it does not prove no possible geometric route exists.
|
|
- `Cancelled`: capture, search or replay cancelled without live mutation.
|
|
|
|
Malformed original executed graphs are refused, not salvaged. Valid but incomplete
|
|
old programs can regenerate from clean geometry. Regenerated programs retain genuine
|
|
configured tab gaps; stale tab settings do not establish retention. In confirmed-
|
|
parameters mode, locked/ineligible programs must cover the complete directed clean
|
|
boundary: an open fixed program has no certified selected tab metadata and is refused,
|
|
not repaired, even if it may have been intentionally tabbed. The no-parameter route
|
|
retains its narrower compatibility contract. A lead-out that may bridge a tab, or
|
|
a malformed emitted arc, is refused, not automatically repaired. Tabbed lead-outs
|
|
leave from the trimmed cut end, but a lead-out after an open contour still needs
|
|
manual review of its retention gap, so confirmed-parameters planning refuses it.
|
|
|
|
## Apply
|
|
|
|
```csharp
|
|
var commit = CuttingPlanService.Apply(results, cancellationToken); // one result per plate
|
|
```
|
|
|
|
Call it on the thread that owns the plates, with Ready, independently replayed
|
|
results from plate-scoped requests; anything else is `InvalidInput`. Apply never
|
|
replans. Each plate is compared exactly with the state captured with its request:
|
|
part list instance and order, plate quantity/size/quadrant and settings, cutoff
|
|
definitions, and for every part its program reference and exact content (an
|
|
in-place edit counts), drawing program and cutoff classification, pose bits,
|
|
lead-in/lock flags, settings (reference and exact content) and bounds. Any
|
|
difference on any plate returns `Stale` and changes nothing, so a proposal that
|
|
changes a plate can be applied once; an unchanged (no-op) proposal stays current.
|
|
A malformed live program is also `Stale`, not an exception. A part repeated on
|
|
two plates of one scope is `InvalidInput`. Caller-confirmed planning settings are
|
|
input, not plate state: editing a separate confirmed-settings object after
|
|
capture does not stale the result (confirmed settings that are also a part's or
|
|
the plate's live settings are live state, and editing them does). Settings are
|
|
compared member by member, and only the exact built-in settings types are
|
|
supported (the same set regeneration copies): a plate-scoped request whose part
|
|
or plate settings, or any lead-in, lead-out, tab, sequencing or assignment
|
|
object inside them, is another type (a subclass included) returns
|
|
`UnsupportedGeometry` without running that type's code. A settings object
|
|
replaced by such a type after capture makes `Apply` return `Stale`. Detached
|
|
part-list requests do not capture settings and are unaffected.
|
|
|
|
The whole scope is validated and its bounds staged first; cancellation is checked
|
|
immediately before the install. Order changes through `ObservableList.Reorder`
|
|
semantics: same references, no `PartAdded`/`PartRemoved`, so drawing quantities,
|
|
sentinel plates and plate lists are untouched. Regenerated parts receive a fresh
|
|
owned copy of the replayed program and of the settings captured with the request,
|
|
keep their pose and lock, and are marked as having lead-ins. Fixed programs are
|
|
not replaced. An exception during install restores every plate exactly and returns
|
|
`Failed`. The installer itself is internal: it trusts these owned payloads and
|
|
checks only root program references, so `CuttingPlanService.Apply` is the only
|
|
public path. After the whole scope is installed, each changed plate raises
|
|
`Plate.PartsReordered` once; an observer exception is reported in `RefreshErrors`
|
|
on an `Applied` result, not as a rollback.
|
|
|
|
## Desktop workflow
|
|
|
|
`Plate > Plan Cutting...` plans the active plate and `Nest > Plan Cutting (All Plates)...`
|
|
plans every plate that has parts. Both open one dialog built on
|
|
`OpenNest.Engine.CuttingPlanning.CuttingPlanBatch`:
|
|
|
|
- The dialog starts from the plate's cutting settings (or the last-used settings) and plans
|
|
at once. `Cutting Settings...` edits them and `Keep the current part order` fixes the
|
|
whole-part order; either change replans. The settings are confirmed parameters: every
|
|
unlocked part's lead-ins are regenerated. Locked parts keep their exact programs;
|
|
the strict route checks them, while any best-effort warnings require explicit review below.
|
|
- A missing or zero-length lead-in is reported directly, rather than as a search-limit
|
|
failure. Open `Cutting Settings...`, choose a lead-in other than `None` with a nonzero
|
|
length on the affected `External`, `Internal`, or `Arc / Circle` tab, then replan.
|
|
Locked programs require manual lead editing or unlocking before regeneration.
|
|
When a lead hits another part, the finding suggests more spacing or a shorter lead;
|
|
when no tested entry fits, it suggests reducing lead-in length and, if neighbours
|
|
obstruct it, spacing the parts farther apart. These are suggestions, not guaranteed
|
|
fixes: replanning runs the same strict checks; an unverified fallback requires
|
|
separate warning acceptance and is not approval to post or cut.
|
|
- The desktop free-order batch first tries up to eight entries per contour for at most
|
|
1,000 expansions. Only a Ready proposal that passes independent replay is used;
|
|
otherwise it runs the original 16-entry search with its entire plate budget.
|
|
Cancellation stops both attempts. This short pass can choose a different valid
|
|
route, and a hard plate can take up to 1,000 extra expansions before the full
|
|
attempt. The current-order retry still uses its original 16-entry budget.
|
|
Returned expansion counts describe the retained attempt, not a discarded
|
|
short pass plus its full-cap retry.
|
|
- Every plate is captured on the UI thread and checked and planned on a worker. Clean part
|
|
material is checked for overlaps with the pre-post overlap analyzer; known overlapping parts
|
|
still block that plate whatever its route. Incomplete checks remain visible warnings, never
|
|
a clear result (see [pre-post verification](post-verification.md)). A free-order search that ends
|
|
`NoSolutionWithinBudget` is retried once with the current part order, and the summary
|
|
says the order was kept. Both are allowed 400 expansions per part (at least the
|
|
default 20000), because both still plan contour order and entries for every part.
|
|
- The summary lists every plate: ready plates with part counts and rapid travel, others
|
|
with their status and findings. Unverified proposals show every overlap and route
|
|
warning in the scrollable summary before the acceptance checkbox is used; they do
|
|
not truncate later parts' warnings. Finding part numbers are the plate's current order, as the
|
|
editor numbers them. The preview shows the active plate detached from the nest (quantity
|
|
zero, so drawing quantities do not change) in the proposed order with the proposed
|
|
programs. Ready and best-effort proposals can be previewed only while the plate still
|
|
matches capture. Best-effort previews are labelled `UNVERIFIED`. Refused program graphs
|
|
are never copied, and changed plates require replanning.
|
|
- Apply requires usable output for every plate and remains all or nothing. A ready batch
|
|
can apply immediately; an unverified batch requires the unchecked, per-proposal
|
|
`I reviewed the warnings. Apply this unverified plan.` checkbox. Replanning clears it.
|
|
Both use the same owned-program, freshness and rollback boundary. After it applies, each plate keeps its own copy of the
|
|
confirmed settings, which also become the saved defaults. `Stale` keeps the dialog open
|
|
and asks for a replan; nothing changes.
|
|
- Closing or cancelling while planning cancels the worker and keeps the dialog open until
|
|
it stops. Planning and Apply refuse to start while a nesting or plate operation runs.
|
|
The dialog plans with its own copy of the settings, and posts progress and results to
|
|
the thread it was created on rather than to whichever context is current.
|
|
- `PlateView` follows `Plate.PartsReordered`: it redraws parts in the plate's order (the
|
|
numbers it draws are the cutting order), rebuilds their graphics and marks the overlap
|
|
check out of date. Both the editor and preview build outlines and lead paths in the
|
|
drawing-local frame, then apply the part placement once. Absolute (G90) programs
|
|
therefore follow moves and clones just like incremental (G91) programs; displaying a
|
|
part does not rewrite its program or coordinate mode.
|
|
|
|
### Best-effort fallback for imperfect geometry
|
|
|
|
When strict planning reports unsupported or incomplete geometry, the desktop batch
|
|
automatically attempts a bounded, deterministic best-effort proposal. Touching, intersecting
|
|
or numerically uncertain material boundaries need not prevent lead-in generation when their
|
|
closed executable contours are readable. The fallback reuses the existing contour emitter:
|
|
internal contours first, the largest bounding perimeter last, nearest entry from the preceding
|
|
departure, with the confirmed lead styles. It retains source part order except to satisfy
|
|
proven cutoff and nested-part prerequisites. `Keep the current part order` still refuses an
|
|
order that contradicts a proven prerequisite. Uncertain containment is named as a warning.
|
|
|
|
No source contours are repaired, removed, simplified, or silently closed. Locked programs
|
|
and cutoffs remain exact. Unknown/null/recursive instruction graphs, suppressed or nonfinite
|
|
motions and contours the emitter cannot represent still refuse. This fallback is for
|
|
incomplete geometry checks, not every constraint conflict or exhausted search. Known
|
|
inter-part material overlap still blocks the entire batch.
|
|
|
|
The result retains strict refusal findings and available rapid-check findings, and is
|
|
explicitly not a certificate of lead clearance, rapid travel or material containment.
|
|
Inspect the preview and warnings before accepting it. A route with only an incomplete
|
|
overlap check may likewise be accepted with warnings. `CanApply` and parameterless batch
|
|
`Apply()` remain strict; `CanApplyWithWarnings` and `Apply(acceptWarnings: true)` are the
|
|
explicit review path. `CuttingPlanService.Apply` still rejects best-effort results directly.
|
|
Acceptance never skips the separate pre-post checks or grants posting consent.
|
|
|
|
## Remaining integration boundaries
|
|
|
|
The service does not establish clean-material non-overlap, scrap release by open
|
|
cutoff cuts or sheet edges, or physical retention strength. It does not write CNC
|
|
or set posting consent. A `Ready` proposal can still be unsuitable for cutting.
|
|
|
|
Later slices route the lead-in side panel's automatic assignment through the planner
|
|
and retire the legacy automatic paths. Windows interaction,
|
|
supplied-job coverage and actual posted order remain separate acceptance gates.
|
|
Fresh [pre-post verification](post-verification.md) is still required; it is not a
|
|
physical safety qualification.
|
|
|
|
## Portable regression gate
|
|
|
|
```sh
|
|
dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Release --filter 'FullyQualifiedName~CuttingPlanning|FullyQualifiedName~PostVerificationAnalyzerTests'
|
|
```
|
|
|
|
Synthetic fixtures exercise whole-part routing and internal-hole crossing repair,
|
|
locked/ineligible refusal, actual native lead paths, shared subprograms, ownership,
|
|
entry/whole-part backtracking, deterministic budgets, cancellation and fresh replay
|
|
rejection. Retained emission characterizations cover styles, winding, corner rules,
|
|
circle rounding/clamping and tabs; unsupported cases remain explicit refusals.
|