Skip to content

Latent over-cut: solo_step ignores a batched-suffix's already-folded siblings #4023

Description

@BIMvoice

Summary

A latent over-cut in BooleanClippingProcessor::process_with_depth_inner's solo_step computation, found stranded on PR #3922's diverged branch (fix-issue-3919-roof-clip-gate) as its one remaining unmerged commit. #3922's own original fix already landed separately as f7872db62; this issue covers only the remaining unique change from that branch.

The defect

rust/geometry/src/processors/boolean/mod.rs computes, per DIFFERENCE chain:

let solo_step = spine.len() == 1;

solo_step answers a per-cutter question for the accept-gate rejection fallback in single_cutter_gate.rs: does this cutter have a sibling that can compensate for a gate-rejected subtract? true escalates to the riskier unbounded FallThrough clip; false picks the safer KeepUncut.

But the outer chain-resolution loop (process_with_depth_inner) retries try_union_polygonal_chain at every suffix level of a left-deep DIFFERENCE spine. When the top-level batch fails and a shorter suffix batches successfully, the caller's spine ends up holding only the single outermost node — spine.len() == 1 — even though that node's own cutter has siblings, already folded into the mesh by the successful suffix batch below it. The current formula can't tell that case apart from a genuine single-cutter shape (no sibling anywhere), so it reports "no sibling" and sends a gate rejection to the riskier FallThrough — an over-cut in exactly the case !solo_step exists to prevent.

Fix

Track whether the mesh entering the sequential-apply loop already came from a successful batch (based_on_batch), and only treat a length-1 spine as truly solo when it did not:

let solo_step = spine.len() == 1 && !based_on_batch;

Measured impact

Entering House.ifc's real chain directly at entity #2146 (issue #960 fixture, treated as a representation root): before the fix, an accept-gate rejection on #2146's own cutter over-cuts to max Z ≈ 2735.6 mm (FallThrough); after the fix, it correctly keeps the batched result un-cut at max Z ≈ 4475.3 mm (KeepUncut).

Status on main today

This defect is currently latent, not active, on main. Entering the #960 fixture at entity #2146 (as the stranded branch's regression test does) currently produces spine.len() == 2 on main, not 1 — so the buggy formula isn't reached by that particular fixture today. A scan of every DIFFERENCE-chain root in the only in-repo fixture with this shape (five chains, ~50 nodes total) found no node that simultaneously satisfies spine.len() == 1, based_on_batch == true, AND an actual accept-gate rejection at that node — so no current in-repo fixture makes this bug active. The accompanying regression test therefore passes both before and after the fix; it is a guard against a real, structurally-possible defect, not a live reproduction. The PR fixing this will state that plainly rather than presenting the guard test as a reproduction.

Where it's stranded

PR #3922's branch has diverged 1000+ lines from main in unrelated files. Its original fix already landed as f7872db62. This one commit (fixing solo_step, plus the three files it touches: mod.rs, single_cutter_gate.rs, chain_cycle_tests.rs) is the only unique, unmerged change left on that branch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

readyMaintainer-approved: in scope, wanted now, scoped. A PR may close this.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions