feat(cam-geom): NestedRegion validated multi-loop region #135

Merged
patman merged 3 commits from patman/nested-region into patman/min-loop-gap 2026-08-24 02:25:45 +00:00 AGit
Owner

Island lane I-b. NestedRegion (single/classify/outer/islands/to_stock) with RegionError refusals: depth-2 nesting, multiple outer loops, no outer loop, ambiguity passthrough. The validated input type plan_rings_region consumes. Opus review PASS.

Island lane I-b. NestedRegion (single/classify/outer/islands/to_stock) with RegionError refusals: depth-2 nesting, multiple outer loops, no outer loop, ambiguity passthrough. The validated input type plan_rings_region consumes. Opus review PASS.
feat(cam-geom): NestedRegion validated multi-loop region
Some checks failed
CI / build-test (pull_request) Failing after 4s
29154bfe0f
Adds `cam_geom::region`, a new module holding `NestedRegion` and
`RegionError`: the validated outer-plus-islands region type the
island-clearing design puts in cam-geom rather than cam-clear
(docs/superpowers/island-clearing-design.md, step I-b / ID-2, ID-3).
cam-clear consumes a validated region instead of a raw `&[Contour]`, so
the winding normalization and depth-shape checks live once, next to the
`classify_nesting` classifier they are built on, and stay reusable by any
future consumer (a cam-depth island relaxation, for instance).

What it adds:

- `NestedRegion { outer, islands }` with private fields and no public
  constructor other than the two validating ones, so every live instance
  satisfies the outer-CCW / islands-CW convention by construction.
- `NestedRegion::single(outer)`, the no-island case, normalizing via
  `orient_ccw`. Returns `Result` purely so both constructors stay
  interchangeable at call sites.
- `NestedRegion::classify(loops)`, which runs `classify_nesting`, requires
  exactly one depth-0 loop, requires every other loop at depth 1, and
  normalizes winding regardless of input orientation — outer to CCW,
  islands to CW.
- `NestedRegion::outer` / `islands` accessors and `to_stock()`, which hands
  the region to the existing multi-boundary machinery: `StockState`'s exact
  engagement oracle and `largest_inscribed_circle`'s seed search are already
  island-aware given CW island boundaries, so the seed avoids islands with
  no new code (design ID-10).
- `RegionError` with `NestingTooDeep { depth }` (depth >= 2, a pocket cut
  into an island: refused in v1 rather than recursed, ID-3),
  `MultipleOuterLoops` (multi-pocket, out of scope), `NoOuterLoop` (empty
  input — not in the design's list, added to fail closed rather than panic
  on an absent outer loop), and `Nesting(NestingError)` propagating
  ambiguous-containment / crossing-loops / query failures verbatim through
  a `From` impl.

Invariants: construction is the only path to a `NestedRegion`, so outer is
always CCW and every island always CW; a wrongly-wound input island is
normalized, not rejected, and its interior is correctly excluded from
`to_stock()`; refusals here are geometry-shape refusals only — whether a
validated region can actually be cleared with a given tool is a cam-clear
concern and is deliberately not decided here.

Tests: single-loop identity; square-with-island classification with
orientation normalization asserted on both loops; depth-2 rejected as
`NestingTooDeep`; two depth-0 loops rejected as `MultipleOuterLoops`; empty
input rejected as `NoOuterLoop`; ambiguous containment propagated; a CCW
(wrong-winding) island normalized to CW with `to_stock()` excluding its
interior; `to_stock()` agreeing with `region_contains` on the raw loop set;
a polygonal island's interior excluded; and a proptest checking
`to_stock()` containment against the hand-computed answer over randomly
placed islands.

Co-authored-by: patman-assist <patrick-ai@kgroo.co>
chore: normalize comments
Some checks failed
CI / build-test (pull_request) Failing after 5s
f0f11ac643
Co-authored-by: patman-assist <patrick-ai@kgroo.co>
style: rustfmt --edition 2024 drift fix
All checks were successful
CI / build-test (pull_request) Successful in 8m37s
dbd01dcf90
Reformats files that predate the runner's current rustfmt; no code changes.

Co-authored-by: patman-assist <patrick-ai@kgroo.co>
patman merged commit 2aaf65ccf0 into patman/min-loop-gap 2026-08-24 02:25:45 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
patman/KGROO!135
No description provided.