Add the additive per-plate clamps array (id, x, y, length, width) to the
nest DTO with writer and reader support. Files without the key load with
zero zones; an individually malformed entry is dropped while the rest of
the nest loads. A plate carrying only clamp definitions now survives the
writer's empty-plate filter; genuinely empty plates remain omitted.
Format documented in docs/nest-file-format.md.
Introduce ClampZone: an axis-aligned rectangular keep-out in plate
coordinates (id + detached Box), validated finite, positive-size and
identified. Plate.ClampZones is a first-class collection alongside
Parts/CutOffs with a ClampZonesChanged event for view refresh. Zones are
never parts: quantity, utilization, cut sequence and CNC output ignore
them (locked by tests); they carry no z-height reasoning.
Windows CI proved the default single-plate fixture is not notice-free;
the details box now surfaces the SegmentedCut notice that the old text
wall buried. Assert the real content instead of a clean box.
The dialog buried its verdict in a scrollable text box that restated the
same numbers per plate behind a paragraph-length warning. Replace it
with:
- a bold colored status line stating exactly what Apply will do
(Ready / Blocked / No new cut-offs / Error with the reason);
- the occupied span, used span, separator X and retained tail as
compact caption+value cells with tooltips instead of four sentences;
- the warning paragraph reduced to one amber line with the full text
in its tooltip;
- a per-plate table (cut-offs, spans, tail, Ready/Blocked/No change)
for the All Plates command, replacing repeated text blocks;
- the details box now lists only issues and notices, prefixed by plate
number when planning a nest, instead of echoing every measurement.
Planning, preview, apply and rollback behavior is unchanged; tests and
the cutoffs document follow the new presentation.
Nests converted from another CAM system carry its seam overcut inside the
clean contour: the last cut runs on past the contour's own start along its
first cut, then a straight jog returns to the start. The ring then touches
itself at its seam, so neither the cutting planner nor the overlap analyzer
could certify it and the whole plate refused.
SeamOvercut recognizes exactly that shape (same line, or same circle and
direction) and trims it from an owned copy; the retraced tail encloses no
area, so material is unchanged and the drawing is never edited. Any other
self-contact still refuses. Arcs of one circle whose centers were each derived
from their own endpoints agree only within Epsilon, so their spans are now
compared at the seam tolerance: shared spans still refuse as overlap, while
arcs that only meet at a joint or lie apart no longer do.
Plates with parts nested in other parts' cutouts then failed to route, since
the host is cut last and every straight approach from the middle of a crowded
cutout crosses a finished neighbour. CuttingNestedOrder tours each host's
inner parts as one run ending beside the host's own contours, hosts follow
the parts beside them, and learned rules still win. Outside entries are
ordered by clear sight lines to the next cut, the look-ahead toward a host is
its nearest contour point rather than its centre, a cutout containing the
arrival point is routed from that point, and a pass that teaches an order
rule ends the round instead of repeating the blocked approach.
Tests: SeamOvercutTests and CuttingNestedOrderTests; the cutting-planning,
diagnostics and overlap tests in OpenNest.Tests pass (1023 passed, 6 skipped).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaterialConfig only ever had one grade per material name, so the tree
and GetMaterial/GetParameters couldn't represent e.g. Mild Steel A36
vs. A1011 as distinct, independently-parameterized entries. Prep work
for an upcoming drawing database that will store material + grade per
drawing and needs to resolve cut parameters by that pair.
- MachineConfig gains GetMaterial(name, grade), GetGrades(name), and a
GetParameters(material, grade, thickness) overload, all keyed through
the existing SharedListNames normalization. The name-only overloads
stay for back-compat ("first grade found").
- MachineConfigForm's tree gains a real tier: Machine -> Material name
(synthetic grouping node) -> Grade -> Thickness. Add/remove gained
"Add Grade" and split "Delete Material" (all grades) from "Delete
Grade" (one entry). Renaming a grade's Name relocates its node to the
matching (or a new) group on focus-loss, keeping the tree in sync
without a full reload.
- No schema/JSON changes; existing machine files keep working as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The form's static chrome (tree/detail split, Save/Export/Import bar) was
built entirely in the regular constructor, so Visual Studio's WinForms
designer had no InitializeComponent() to read and showed a blank/broken
surface. Move that static layout into MachineConfigForm.Designer.cs and
add a private parameterless constructor for the designer to instantiate,
following the same pattern already used by EditNestForm. The dynamic,
data-driven detail panel content stays in the regular constructor/methods.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the flat label/textbox list and always-destructive +/- button
row with a tabbed detail panel (General/Lead In/Lead Out/Cut Off/Plate
Sizes for thicknesses) and a right-click context menu on the tree.
Move add/remove/edit to a single "editing session" model: mutations
only touch the in-memory tree until Save, which also prompts on close
if there are unsaved changes. Tree labels now update live instead of
requiring a full reload, which previously collapsed the tree and lost
selection on every add/remove.
Reuse existing domain data instead of free text: Lead Type and Cut Off
Direction become dropdowns sourced from the real option sets elsewhere
in the codebase (CuttingPanel's lead types, OpenNest.CutDirection), and
Plate Sizes becomes a checklist seeded from OpenNest.Shapes.PlateSizes
with a custom-size fallback. Assist Gas becomes an editable dropdown.
Numeric fields show their unit (in/mm) and have sane per-field ranges
instead of a blanket 10000 max. Input controls are capped to a fixed
width instead of stretching across the panel, and the tree pane is
widened to make room for longer machine/material/thickness names.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Report, dialog and diagnostic text across post verification, cutoff
planning, alignment and cutting-plan review repeatedly told the user
what checks weren't (safety certifications, proof, guarantees) instead
of what to do. Reworded these to state the same caveats affirmatively
(inspect X, confirm Y, review Z) without changing the underlying
safety guidance, and updated the tests/docs that pinned the old wording.
Lets the desktop app self-update like CncSender, but only restarts after
the user confirms via a clickable status-bar prompt -- an open, unsaved
nest must never be discarded automatically. The feed URL is a blank-by-
default, per-machine setting (File > Options), since it's operator
infrastructure that doesn't belong in source control; Deploy-OpenNest.ps1
packages releases for whatever feed host/path the operator supplies.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Point-in-material used exact Y comparisons, but rings are only nominally
closed: capture admits closure seams and PEP-converted or rotated geometry
carries sub-micro-inch vertex jogs. When a jog straddled the test ray's Y,
the two edges at that shared vertex disagreed which side it was on and the
parity flipped, so a point ten inches outside a material read as interior.
LeadMaterialSnapshot reported leads as entering a neighbour they could not
touch, and the enclosing-perimeter check refused to capture materials whose
rings it misclassified.
Snap the ray comparison by the admitted seam tolerance so both edges at a
shared vertex decide together. Features larger than the tolerance still
count, and containment of a point within the tolerance of an edge stays as
ambiguous as before. On the 93-plate corpus 83 plates are untouched, one
previously refused plate now plans and replays, and 48/93 plates reach
Ready. Accepting more leads also changed one plate's first-found route to a
longer rapid path; that is search ranking, recorded with the measurements.
Also drops an extent-prefilter fast path measured at -6.8% corpus time: it
cleared leads whose native contact was reported beyond any fixed bbox
margin, which NearbyMaterialCheckTests deliberately pins as a refusal.
Capture and preparation agree that a nominal contour may close within
NominalContourClosure.Tolerance inclusively, which converted drawings rely on:
the sampled production corpus carries vertex gaps up to 1e-6. Replay
verification still compared those rings at the exact geometry epsilon, so the
one joint a rotated tool path legitimately bridges refused the whole plate
after the search had already spent its budget.
Census of 91 plates from 60 stored nests: every searched plate containing such
a seam refused with contour-accounting (4 plates, 76.5s of wasted search, worst
71.7s for one 11-part plate), and none of the 42 ready plates had one. Replay
now carries that same admitted endpoint gap as positional and length slack for
the affected ring only. Direction sign, coverage order, retrace detection, ring
counting and selected-emission binding keep the exact epsilon, and capture's
closure admission is unchanged.
With the fix the census yields 45 ready plates instead of 42, no plate changed
any other way, and expansion counts are identical wherever status matched.
P260805-01's 16-part plate plans in 0.76s rather than refusing after 4,129
expansions.
Tests: SeamClosureReplayTests covers seams of 0, 5e-7 and 1e-6 plus four
accounting faults (missing edge, reversed edge, interior shift beyond the
admitted seam, second circuit) that must still refuse, and the beyond-tolerance
capture refusal. OpenNest.Tests 3751, OpenNest.Engine.Tests 690 and
OpenNest.IO.Tests 79 pass; Windows-only projects cannot build on Linux.