Commit Graph
1615 Commits
Author SHA1 Message Date
aj 775ece5d21 fix(expand): certify disk and cutoff paths and report invalid input 2026-10-13 21:49:28 -04:00
aj cad1083287 test(expand): bound pinned relaxation passes 2026-10-13 21:31:57 -04:00
aj 4183af4142 fix(expand): retain convex exits and report tiny clamp overlap 2026-10-13 21:31:57 -04:00
aj 73be301ebf test(expand): verify cancellation after scratch movement 2026-10-13 21:31:57 -04:00
aj fdc6f074ee fix(expand): distinguish part blockers from plate edges 2026-10-13 21:31:57 -04:00
aj dc4b07ddf5 fix(expand): separate parts touching cutout walls 2026-10-13 21:31:57 -04:00
aj 6c76617905 fix(expand): clip translations against material along their path 2026-10-13 21:31:57 -04:00
aj 43e08965b9 fix(expand): preserve entry clearances and retry blocked pairs 2026-10-13 21:31:57 -04:00
aj ddd1e422be feat(cutoffs): cut sheet-separating cut-offs after every part 2026-10-13 21:09:35 -04:00
aj b9a387b517 feat(desktop): choose cutting-planner cut-off settings in the automatic cut-off dialog 2026-10-13 20:55:24 -04:00
aj 9f2ece37ed feat(desktop): add automatic cut-offs, existing-work banner and clear button to the cutting planner 2026-10-13 20:39:43 -04:00
aj 81cccfc610 feat(cutoffs): stage revertible automatic cut-offs and clear applied cutting work 2026-10-13 20:39:43 -04:00
aj c107ab13f7 fix(cutoffs): preserve exact integral scores in strip search
Avoid shortest round-trip decimal text for already-integral doubles. The failing tie regression and the independent 9,163-case exact-integer sweep now keep the fewer-pierce result.
2026-10-13 20:12:44 -04:00
aj 57450ecea8 fix(cutoffs): honor candidate budget and truthful cut-length ties
Reserve nominal-line slots in the candidate lattice, compare rounded lengths in exact micro-units before pierce ties, and report real fixed-grid cut length. Per-line notices name local increases instead of negative savings. Failing-first regressions cover dense spacing, decimal ties and the legacy summary.
2026-10-13 20:01:48 -04:00
aj b245290664 test(cutoffs): verify shifted lines through apply save and rerun 2026-10-13 19:45:22 -04:00
aj 6d5d963654 feat(cutoffs): move strip lines off clamps 2026-10-13 19:41:57 -04:00
aj 178954e2df feat(cutoffs): keep existing cut-offs as strip boundaries 2026-10-13 19:41:57 -04:00
aj f63fee7392 feat(desktop): add minimum strip and cut-length summary to automatic cutoffs 2026-10-13 19:34:02 -04:00
aj 5857e7fc69 feat(cutoffs): shift strip lines to shorten cuts 2026-10-13 19:29:32 -04:00
aj 15055aef4d feat(cutoffs): measure a cut-off line without building its program 2026-10-13 19:29:32 -04:00
aj c57e0ded83 fix(diagnostics): keep tiny clamps beyond coarse chords in the overlap check
Check Overlaps dismissed a tiny clamp by the part's coarse material bounds
before the fine tolerance-free test ran, but that material is inscribed at
the 0.001 chord tolerance: real material in the sliver beyond a convex arc
(a short arc cap, a rotated crescent) lay outside those bounds and the clamp
read as clear. For clamps below the tolerance the bounds are widened by the
chord tolerance before rejecting.

PM 407f5eb7-329f-41e4-947e-64ad52aa4034 (review s6-delta3-review.md)
2026-10-13 18:15:50 -04:00
aj f2cc471a33 fix(clamps): test tiny clamps against both arc flattenings
Circumscribing every arc holds all material along convex arcs but drops a
sliver along concave ones (a notch), so a tiny clamp 6e-7 into the material
beside a notch read as clear. Tiny-clamp checks now test the part flattened
both ways (all arcs inscribed, all arcs circumscribed): along any arc one of
the two holds the real material, so together they find every real overlap.

PM 407f5eb7-329f-41e4-947e-64ad52aa4034 (delta review s6-delta2-review.md)
2026-10-13 18:15:50 -04:00
aj 07cba5719f fix(io): write an empty sheet whose rail differs only in removal time
The writer's sentinel rule compared zones only, so an empty sheet following
a rail that differs from the default only in clamp removal minutes was
dropped on save although ChangePlateDefaults treats it as configured stock.
The rule now also requires the sheet to follow the default rail itself.

PM 1469b4e0-dc59-416b-8118-560b726aa1e1
2026-10-13 18:15:50 -04:00
aj 9df9a802cd fix(clamps): flatten part material conservatively for tiny clamps
Fine inscribed flattening still let a native arc cut 6.2e-7 into a 1e-5
clamp unseen: less than one chord's sag, so the layout check, interactive
fill and Check Overlaps all called it clear. Tiny-clamp checks now
circumscribe perimeter arcs and inscribe cutout arcs (lines stay exact), so
the polygons hold every bit of real material. Any real overlap is reported;
the cost is a possible report within the tolerance of an arc, never a false
clear. The analyzer builds the same polygons instead of a second
OverlapMaterial read.

PM 407f5eb7-329f-41e4-947e-64ad52aa4034 (delta review s6-delta-review.md)
2026-10-13 18:15:50 -04:00
aj 1bcead4b3a fix(io): write empty clamp sheets whose spacing differs from the defaults
The writer omitted an empty rail sheet when its size, quadrant and zones
matched the defaults, ignoring spacing, so a sheet left at the old part or
edge spacing after a defaults change (configured stock under C17) was
dropped on save. The comparison now includes edge and part spacing.

PM 455f6763-f0aa-40da-b9ad-11de4b2ea95b
2026-10-13 18:15:50 -04:00
aj d9273c889f fix(io): keep a sheet's own clamp rail and its removals through save and reload
Commit can attach the stock's rail to a sheet whose nest has no (or another)
default rail, but only the default rail was saved and the reader re-attached
sheets only to it, so the removed clamp and its time vanished on reload. A
sheet following a rail other than the default now saves it in an optional
per-plate clampRail (read leniently, omitted otherwise), and the reader
re-attaches the sheet to it.

PM 22daec3d-dabb-4aae-95f7-c1d090d5dcdf
2026-10-13 18:15:50 -04:00
aj 4226c0f902 fix(engine): find the fewest clamps to remove, not only neighbouring runs
Recovery only tried contiguous runs of clamps, so a part with a notch over
a middle clamp had all three clamps removed although the two outer ones
were enough (D8: 'the fewest clamps that let it fit'). One probe per clamp
now finds the clamps every removal must include (a part that cannot fit with
only that clamp left needs it off; removal is monotone), and the search
tries every set by size, mandatory clamps plus the optional ones nearest the
origin first. Full-sheet parts resolve with one probe per clamp.

PM 3df03af2-35a6-49c2-9082-fc6208f5b708
2026-10-13 18:15:50 -04:00
aj 6908e00969 fix(clamps): judge tiny clamps against the part's real material
The tolerance-free rectangle test was fed approximated material: the layout
check passed it the 1e-4-grid validation outline (a strip of real material
that wholly contains a 1e-5 clamp could vanish), and interactive fill and
the overlap check passed 0.001 inscribed arc chords (a clamp inside a disk
but outside its chords read as clear). All three now flatten the native
contours at a quarter of the clamp's smaller side, lines kept exact, so the
arc-chord sliver can never hold the clamp. Ordinary clamps are unchanged.

PM 407f5eb7-329f-41e4-947e-64ad52aa4034
2026-10-13 18:15:50 -04:00
aj e38980e54b docs(clamps): describe last-resort clamp removal and current clamp coverage
automatic-nesting.md still said interactive fill/push, the overlap check and
cutoffs ignore clamps, and nesting-engines.md listed the large-spacing and
tiny-zone defects fixed in this slice. Both now describe current behaviour,
and the new last-resort removal section states its limits (1000-probe budget,
sub-grid clearance) as settled with the user (plan D12-D15).
2026-10-13 18:15:50 -04:00
aj a1914e147a fix(engine): reuse clamp-removal probes across identical stock
Offering several identical sheets (same size, spacing and clamps) multiplied
every feasibility probe, so eight full-sheet parts on four 48x120 options
with the eight-clamp rail spent the 1000-probe budget and recovered nothing
although each part fits with its clamps removed. A probe's answer is now
reused for the same part, engine and physically identical stock.

Delta review (s3b-delta-review.md) policy gate 3; PM 0e655fdb-a99a-40d8-968a-1863d8068c3e
2026-10-13 18:15:50 -04:00
aj 64ebda2f6f fix(engine): let clamp recovery move on to another stock with sheets left
Each blocked requirement was tied to the one stock where it needed the
fewest removals; once that stock's finite pool ran out the part stayed
unplaced even with another offered stock untouched. Recovery now runs in
rounds: a blocked part with demand left takes its best removal among the
stocks it has not tried that still have sheets (that stock's own minimal
run, C11). A variant used in several rounds stays one stock entry.

PM bf4438ac-929a-405f-ba26-fcfaa9b1c55e
2026-10-13 18:15:50 -04:00
aj 360f58c101 fix(engine): record a committed clamp removal whatever the target sheet holds
SyncClampZones returned early when the empty target already held exactly the
solved reduced zones, and otherwise preferred the target's own rail over the
stock's, so a removal committed onto such a sheet kept no rail, removed ids
or time. The stock's rail now wins, and the rail/removal state is reconciled
even when the zones themselves need no replacement.

PM 60f7a673-455e-4a87-b865-a4806f417ee6
2026-10-13 18:15:50 -04:00
aj 6fc1a0ebdc fix(core): move an untouched trailing sheet to new nest defaults
Changing the nest defaults (Nest Info, Set as default, Edit Plate on an
empty sheet) left the editor's trailing empty sheet on the old size and
quadrant, so the writer's clamp-preserving empty-sheet rule saved it as a
stale extra sheet. PlateManager.ChangePlateDefaults wraps a defaults edit:
a trailing empty, cutoff-free sheet that exactly matched the old defaults
(and followed their rail with no removed clamps or explicit zones) takes the
new defaults; every other empty sheet keeps its configuration and is still
written.

PM cade6901-5f4a-4503-85bf-0ea681d9ca7f
2026-10-13 18:15:50 -04:00
aj 6b995ac40a fix(core): make a plate's removed-clamp set immutable
Plate.RemovedClamps exposed a HashSet (and every untouched plate shared one
static empty HashSet), so a downcast could change one plate's removal state
or every plate's at once without re-resolving zones. It is now an
ImmutableHashSet.

PM 6ded8ac4-84fd-4e8b-9a3b-6f41e494542c item (e)
2026-10-13 18:15:50 -04:00
aj 7ed40d4bb8 fix(core): charge removal time only for removed clamps on the sheet
Plate.ClampRemovalMinutes multiplied every listed removed id by the
removal time, including ids the rail places beyond the sheet after a resize
or does not know at all. Only removed clamps the rail resolves on this sheet
are pulled, so only those add time; the ids themselves stay listed (C13).

PM 6ded8ac4-84fd-4e8b-9a3b-6f41e494542c item (a)
2026-10-13 18:15:50 -04:00
aj 04d538b0a4 fix(io): omit removedClamps from sheets without a removal
Every plate was written with "removedClamps": [], so files without the
feature changed. The key is now omitted unless a clamp was removed, like
the other additive sections; nest-file-format.md documents removedClamps
and removalMinutes and their lenient reading.

PM 6ded8ac4-84fd-4e8b-9a3b-6f41e494542c item (c)
2026-10-13 18:15:50 -04:00
aj 65754957cc fix(io): keep the clamp rail when its saved removal time is invalid
A negative removalMinutes in a saved nest made the ClampRail constructor
throw, so the reader dropped the whole rail and every sheet lost its
removed-clamp state and time. An invalid value now falls back to the
default five minutes, like a missing one.

PM 6ded8ac4-84fd-4e8b-9a3b-6f41e494542c item (b)
2026-10-13 18:15:49 -04:00
aj 53db543d6b fix(io): read a malformed removedClamps value without failing the nest
The optional per-plate removedClamps list was deserialized as a typed
List<string>, so a string or a list of numbers threw JsonException and the
whole nest failed to open. It now reads leniently: a non-array value reads
as none removed and non-string entries are skipped. The saved zones load
either way, so a removed clamp's area is never silently re-blocked or lost.

PM 9f3d676b-8a1c-4d9a-b877-b5e01e70c3f0
2026-10-13 18:15:49 -04:00
aj 2b208c9b6a fix(engine): bound the clamp removal search
Recovery searched every contiguous clamp run per blocked part and stock with
whole-engine solves and no bound: 25 parts on four 20-clamp stocks cost
21,025 extra solves even though none could fit at all. Each stock is now
first probed once with every clamp removed (dismissing stock the part cannot
use and choosing the lanes worth asking); the run search goes size by size
across the remaining stock and stops at the first fit; and one recovery may
spend at most 1000 probe solves, after which it keeps the ordinary result.

PM 0e655fdb-a99a-40d8-968a-1863d8068c3e
2026-10-13 18:15:49 -04:00
aj b46310e01b fix(engine): solve each clamp removal on its own sheets within the stock pool
Phase 2 offered every removal variant to all residual demand in one solve,
each variant as unlimited stock. A sheet could take more clamps off than its
own parts needed (C11), and a finite pool could yield extra physical sheets
(C12). One root cause, one repair: each variant is now solved separately,
fewest removals first, with its own blocked parts plus unblocked residual
demand; a sheet is kept only when it holds a part that needed exactly that
removal; kept sheets come out of the base stock's remaining pool and the
plate cap. The expanded job reduces each finite base by what its variants
took, so validation sees the true inventory. Variants are cloned from the
exact sheet-frame zones instead of a translate round trip.

PM f91e054a-6f62-4080-91ed-2e1ce1f6eb7e; PM 245a555f-4f16-40b0-a3c9-430c661ad6a6
2026-10-13 18:15:49 -04:00
aj f19a825c4e fix(engine): treat a part as clamp-blocked only when no lane can place it
Last-resort removal treated the selected engine's failure to place a lone
part as proof it could not fit with the clamps in place. Rectangles packs
bounding boxes, so a concave part whose material clears a clamp lost that
clamp for nothing (C10). A part now counts as blocked only when the caller's
engine, Irregular (true material, NFP) and Rectangles all fail on every
offered stock; Default already runs both lanes.

PM c5984c6e-3882-4d15-8a42-8de76b4c2080
2026-10-13 18:15:49 -04:00
aj af50da7609 fix(engine): remove only rail clamps and always name them on the sheet
Last-resort removal ran on any clamped stock, but only a rail-following
plate can record which clamp to pull and its removal time: an explicit-zone
target lost the instruction entirely. NestPlateStock now carries the rail its
zones were resolved from (NestStockBuilder, DrawingJobMapper.FromPlate);
recovery removes clamps only from rail-backed stock, and commit falls back to
the stock's rail when the target plate has none. Runs are now ordered by
distance from the origin along the edge, not by zone list order.

PM 346d46ed-9336-4706-9392-52ed14d6bd33; PM 6ded8ac4 item (d)
2026-10-13 18:15:49 -04:00
aj 3bf63b74f9 fix(engine): keep the ordinary Auto Nest result when clamp recovery fails
A throwing feasibility probe or residual solve inside last-resort clamp
removal propagated out of NestPipeline.Run and discarded the usable ordinary
proposal. Recovery is optional: any non-cancellation failure now returns no
recovery; cancellation still propagates.

PM 1db4333f-96fc-4e3e-8643-47e1120f312c
2026-10-13 18:15:49 -04:00
aj d24e660286 fix(diagnostics): report parts covering a tiny clamp in the overlap check
Collision.Check finds no overlap region for a clamp smaller than its
tolerance, so Check Overlaps reported a part covering a 1e-5 clamp as clear.
Clamps below 0.001 are judged with KeepOutOverlap and reported as a finding
whose fragment is the whole clamp rectangle; flush and clear parts stay clear.

PM aa995123-295f-479c-adc4-b0cbcb7cf838
2026-10-13 18:15:49 -04:00
aj 9b5885e5a2 fix(clamps): judge tiny keep-outs tolerance-free in interactive fill
InteractiveClamps.MaterialInKeepOut flattened a sub-tolerance keep-out with
the 0.001 polygon tolerance and called a part covering a 1e-5 clamp clear,
so the fill safety net could pass it. Keep-outs below the tolerance now use
KeepOutOverlap. Adds the 1e-5 zone sweep through every fill strategy and a
push (characterization; both already stay clear).

PM b0062c73-40ef-440e-8efb-b94908f7c2d1
2026-10-13 18:15:49 -04:00
aj 8f0c0692fd fix(validation): check tiny clamp zones without outline collapse
A clamp zone smaller than the validation outline tolerance went through
ClipperBridge.OffsetForValidation, collapsed to a null outline and made
NestLayoutCheck (and Default scoring) throw. The keep-out is now an exact
four-vertex ring, and a keep-out below 0.001 on either axis is judged by a
tolerance-free rectangle test (KeepOutOverlap): positive-area overlap only,
hole-aware, never a silent clear. Tolerances are unchanged.

PM 6859db53-e39b-4135-85c6-4967037cf880
2026-10-13 18:15:49 -04:00
aj 3f0a2703fd fix(rectangles): keep large-spacing clamp blocks off the clamp
When part spacing exceeds a clamp's inflated span the per-axis block
interval [zone - clearance + s, zone + clearance] inverts. The old
collapse to the zone centerline was not conservative: a part could
start exactly on the centerline with material inside the clamp
(PM d86d22c9 - the probe rail Box(0,0,5,0.625) at part spacing 4
completed at y=0.3125). Per decision D11, an inverted span now blocks
a zero-extent line at the clamp edge facing the sheet interior:
zone max + clearance when the zone center is in the bin's lower half
on that axis, else zone min - clearance + s, so interior-side parts
keep the exact bottom-edge clearance and the unavoidable extra
exclusion lands toward the nearer sheet edge.

Failing-first: the PM probe fixture in quadrants 1-4 and two X-inverted
fixtures (mid-rail and far-rail) were red on ab50900 against an
independent material-vs-inflated-footprint oracle; all six now pass.
Non-inverted spans stay bit-for-bit identical: four keep-row layout
digests captured from the unfixed ab50900 packer are unchanged, the
between-clamp rows still pack the gap, and the three zero-clamp C2
baseline hashes are untouched. Full Engine suite 929 passed/2 existing
skips.
2026-10-13 18:15:49 -04:00
aj 83ae1a48f0 test(diagnostics): lock the start-point clamp rapid warning
D10 (user decision 2026-10-13, keep the warning): a clamp footprint covering
the modeled program start (0,0) makes the first rapid of every posting attempt
cross it, and that warning has no exemption and no persisted bypass. Lock the
behavior in all four quadrants with a physical-footprint oracle and a mutation
red (skipping rapids that start inside a zone reddens exactly the four
origin-covering rows). Document the design in docs/post-verification.md.
2026-10-13 18:15:49 -04:00
aj 92d8964339 fix(engine): return detached clamp zones from plate stock
NestPlateStock is documented as immutable input, but ClampZones handed
out its own zones, whose bounds are mutable. Editing one after the
stock was built moved the stock's clamp while plates materialized from
the stock (layout check, fill, posting) kept the original zone, so a
part or cut on the moved clamp validated and posted clear.

Return detached copies on every read, so the stock and its plates can
never disagree. Copies keep the exact bits; engine results are
unchanged.

Project Memory: opennest 14f93b26-2e41-41f1-b09f-87dc895bb4da
2026-10-13 18:15:49 -04:00
aj df2f1b796d fix(engine): keep every plate's clamp zones in the sheet frame
Plates the engine built from stock (DrawingJobMapper.CreatePlate, used
by the public result materializer, the layout check and the placement
fillers) stored placement-frame zones, while live plates store them
relative to the sheet's lower-left corner. The same Plate.ClampZones
therefore meant two frames: the new clamp cut checks double-translated
materialized plates in quadrants 2-4, and interactive fill and push on
live plates read sheet-frame zones as placement coordinates, so they
could put parts on clamps there.

Plates built from stock now keep the stock's input zones (sheet frame,
no lossy translate-and-back), and the layout check and interactive
clamp obstacles map through ClampZoneFrame.ToPlacement, which
reproduces the stock's placement zones bit for bit, so engine
placements and validation are unchanged.

Project Memory: opennest 14f93b26-2e41-41f1-b09f-87dc895bb4da
2026-10-13 18:15:49 -04:00