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)
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
NestPlateStock takes an optional clamp-zone list, translated into the
placement frame exactly like WorkArea (quadrants 1-4) and detached from
caller boxes; empty input keeps stock bit-identical to before. Plate
mappings (DrawingJobMapper.FromPlate/CreatePlate, NestStockBuilder) map
zones across; materialized plates carry them for checking.
NestJobValidator rejects zones outside the sheet or duplicate ids.
RepeatedPlateGrouping and NestLayoutCheck treat clamp sets as stock
identity (C8), and the layout checker flags part material inside a zone
inflated by bottom-edge spacing with the shared spacing slack (C1/C5) -
no tolerance loosened.
build NestJob -> resolve engine by name -> Solve -> independent
NestLayoutCheck validation -> bind placements to caller drawings.
The pipeline never commits to plates and never mutates caller items;
validation failures are returned as messages naming real drawings
(unknown-requirement placements are reported, not dropped). Console,
MCP, API and desktop Auto Nest will adopt this path in later phases.
- NestPipeline.Run(request): registry-resolved engine, unknown names
list the registered engines; cancellation propagates untouched.
- NestPipeline.Run(engine, ...): stub/plug-in engines take the same path.
- NestResultBinder: pose semantics identical to NestResultMaterializer.
- NestLayoutCheck: Violations overload with per-requirement display names.
Tests: overlap stub -> violations w/ drawing names, no throw; ghost
placement -> violation + excluded from further checks; unknown engine
-> NotSupportedException listing engines; cancelled token -> no result;
happy path -> bound by reference, caller quantity untouched.
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.
Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).
Formatting is now enforceable: dotnet csharpier check . passes.
Task 5 of the whole-job engine API: add a geometry safety gate that
validates every candidate trial before the runner commits accounting.
- NestJobPlacementValidator: closed-contour validity, rotation-policy
compliance, work-area containment per quadrant, hole-aware material
overlap, and required part spacing. Overlap is interior-only, so
zero-clearance edge/corner contact remains a valid placement.
- NestJobValidator: route candidate validation through the geometry
gate; reject unusable/unclosed/degenerate contours up front.
- NestJobRunner: wrap candidate evaluation in a progress bridge that
tags legacy engine detail with the current candidate context.
- LegacyPlateNesterAdapter: forward IProgress to the legacy engine so
its progress surfaces under the active candidate.
- Tests: geometry (quadrants, rotations, touching, containment, holes,
empty stock, real Default/Strip smoke), validation, and cancellation
suites; repaired test fakes that emitted out-of-bounds or overlapping
placements the gate now correctly rejects.
Engine.Tests: 70 passed, 0 failed, 0 skipped in Debug and Release.
Windows-only OpenNest.Tests not run on Linux.
LegacyPlateNesterAdapter.Create now delegates to PlateNesterFactory
instead of carrying its own minimal Default-only switch, so built-in
strategy resolution has one source of truth. Behavior unchanged:
unknown keys still reject; all four built-ins now resolvable.
44 net8.0 tests pass in Debug and Release; 0 warnings.