feat(cam-clear): island encirclement pass families #137
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "patman/island-families"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Island lane I-d. IslandFront, island_family, solve_island_step, island_pass_engagement, island_first_orbit_bound; ClearError::{IslandWrongWinding, IslandOffsetFailed}. Island pass engagements are REPORTED, not certified, in this commit — the margin bookkeeping lands in I-e3 (#140). Includes the OnBoundary oracle fix (narrow error match + checked_any guard) that root-caused a one-in-many-runs flake to a query degeneracy at exact-boundary sample points. Opus review PASS.
Adds the per-island outward encirclement family builder, the construction half of island-aware clearing (docs/superpowers/island-clearing-design.md, step I-d / ID-6, ID-10). Each island gets its own ordered chain of CW encirclement loops growing away from it, tightest orbit first, so the tool can orbit an island the same way it walks the outer wall inward. The coupled walk that decides where these chains actually stop is I-e; this builds one island's family in isolation, far enough for that caller to probe. What it adds: - `IslandFront { loops, max_engagement, certified }`, one island's chain plus parallel per-pass engagement bounds and certification flags. - `island_family`, the walk: build the island's tool-center limit loop via `offset_closed(island, r)`, then step outward with `solve_island_step` until accumulated growth reaches `max_extent` or `MAX_PASSES` is hit. - `solve_island_step`, the island analogue of `solve_ring`: bisects for the largest stepover in (floor, max_stepover] whose engagement stays at or below target. Growth uses a positive-distance `offset_closed` directly on the CW loop — per the design addendum, that is what dilates a CW loop outward; the `cleared_envelope` negative-then-reversed pattern would flip winding to CCW and mis-route engagement to the concave branch, undercounting by up to several tenths of a radian. There is no `min_convex_radius` floor to clamp toward on outward growth, so an offset failure here is a genuine construction limit (a concave notch collapsing as it is pushed out), not a topology to work around — it surfaces as `IslandOffsetFailed`. - `island_pass_engagement`, the sampled exact-distance union bound (I-c's `island_union_engagement`) evaluated at `sample_points` stations along a pass against the previous pass's grown envelope. Sampled rather than closed-form because the envelope need not be convex, so the section-3(b) convex over-count shortcut does not apply (ID-8); the cost is samples-many stock queries per pass, fine at CAM precompute time (design intent #3). - `island_first_orbit_bound`, the honest reported bound for the limit pass. Reading `island_pass_engagement` of the limit loop against the raw island measures penetration into the island, which is exactly zero by construction (the limit loop is tangent everywhere) and would report ~0 while the tool is in fact buried. Instead each island segment is evaluated at full-radius penetration through the same closed forms I-c verified, the max is taken, and the result is floored at pi — the outward-semicircle -in-virgin-material argument is a whole-disk argument that holds regardless of local island shape, including at concave notches where the bare per-segment formula reads below pi. Certification posture: every pass in an `IslandFront` is reported, never target-gated — `certified` is all-false in this commit. The stepover walk still target-gates its own stepover choice via `solve_island_step`'s bisection, keeping engagement magnitudes sane; only the flag downstream consumers read is honest that nothing here is a certificate yet. The sampled union bound has no adversarial h-perturbation margin and no inter-sample modulus of continuity, so between two sampled stations the true engagement can peak above either reading with nothing bounding the gap. The margin bookkeeping needed to promote entries to true is deferred to I-e, and `certified` is kept as a `Vec<bool>` rather than dropped so that promotion is per-pass and needs no shape change. Invariants: islands must arrive CW-wound and a CCW one is refused loudly (`IslandWrongWinding`) rather than silently re-oriented, since routing depends on the caller's winding — `NestedRegion::classify` already normalizes upstream of any real caller, so this catches a bypass. Loops are emitted innermost-first and are CW throughout, never a `cleared_envelope` -derived reversed loop. Overshoot contract, documented on the function: the extent check runs before each step and a solved step adds a full stepover, so the walk may exceed `max_extent` by up to one `max_stepover` — callers must not treat it as a collision-safety bound, and I-e runs its own `min_loop_gap` checks accordingly. Known degenerate case, documented rather than papered over: a zero-area island has signed area exactly 0.0, so `orient_ccw` is a no-op on it and the winding check fires regardless of its actual orientation. It is reported as `IslandWrongWinding` even though the real defect is zero-area. cam-geom's `signed_area` is private to `orient_ccw`, so distinguishing the two would mean widening cam-geom's public API — out of scope here. Tests: `circular_island_hand_check_pins_exact_engagement` (closed-form value pinned by hand); `island_family_first_orbit_engagement_is_not_near_zero` (the tangency trap that motivated `island_first_orbit_bound`); `square_island_family_pass_count_and_target_gated` (pass count and monotone engagement on the design's flagship square-island fixture); `tiny_island_below_tool_radius_still_builds_family`; `island_family_refuses_ccw_island`; `island_family_refuses_offset_collapse_at_sharp_concave_notch` (a V-notch under the miter limit at any positive offset); and two oracle-direction checks — `island_family_oracle_direction_holds_for_square_island` and the `island_family_oracle_direction_holds_at_every_sampled_pass` proptest — asserting the reported bound dominates exact `StockState::engagement` at every sampled tool center. Test-robustness fix folded in here (same owning commit as the tests it touches): `assert_island_family_oracle_direction` unwrapped `StockState::engagement`, which returns `QueryError::OnBoundary` when a sampled tool center lands exactly on the feature being measured. Because a pass and its feature are parallel offsets, that is a routine degeneracy of the *query*, not an oracle violation, and the rest of the suite already handles it with `Err(QueryError::OnBoundary) => continue`. Unwrapping made these proptests fail intermittently. Their case count also goes from 20 to 64: at 20 the degeneracy surfaced roughly once in a blue moon and was not reproducible run-to-run, which is exactly how it evaded earlier review; at 64 it surfaced immediately and stayed reproducible. Co-authored-by: patman-assist <patrick-ai@kgroo.co>