Skip to content

Thin-wall, occluder and gap transfer controls (#4381)

Controlled synthetic acceptance for the F4 rules "opposite thin-wall surfaces must not bleed into each other" and "missing regions stay unknown". Every fixture is generated in-test with an identity registration; nothing here is a real scan/IFC pair, and no registration accuracy is claimed. The controls prove the classification rules over the real planner, apply, reopen and atlas path.

Fixture

One IfcWall whose body is a 4 mm IfcTriangulatedFaceSet partition: front face at y=0 facing -Y, back face at y=0.004 facing +Y, one square metre each, styled solid green so any unknown texel is distinguishable from the scan. The scan is a set of textured quads with constant UVs into a 3×1 red/blue/yellow image. Settings: 64 texels/m, 20 mm maximum distance (five times the wall thickness, so the opposite capture is always within reach), normal agreement 0.8, 1 mm ambiguity, 5 mm behind-surface limit.

Native tests: rust/processing/src/appearance/transfer_acceptance_tests.rs (cargo test -p ifc-lite-processing appearance::transfer --lib). The same fixtures run over the real WASM boundary in scripts/lib/wasm-mesh-transfer-surfaces-contract.mjs (pnpm test:wasm-contract), asserting the same coverage classification counts.

Two-sided partition with a gap in the back capture

Front capture 1 mm in front of the front face over the whole face; back capture 1 mm behind the back face over x < 0.5 only.

Count Value
samples (4 centroids + 8,420 interior texels) 8,424
observed 6,340
unknown: incompatible normal 2,084
unknown: distance / ambiguous / behind 0 / 0 / 0
observed area estimate 1.504 m² of 2 m²

Reopened through the production geometry pipeline and sampled from the emitted atlas: front (0.3, 0, 0.5) and (0.8, 0, 0.5) are red, back (0.3, 0.004, 0.5) is blue, and the uncaptured back region (0.8, 0.004, 0.5) is green. The red front capture is only 5 mm from the back face, well inside the distance bound; it is refused by its opposite normal, so no red bleeds onto the back face and the prior appearance survives there.

Slab in front of an uncaptured region, nothing behind the wall

Front capture with a gap over x in [0.4, 0.6]; a 2 mm yellow slab 6–8 mm in front of that gap with both faces captured; no back capture at all.

Count Value
samples 8,424
observed 3,426
unknown: incompatible normal 4,212
unknown: behind the surface 748
unknown: ambiguous (seam band only) 38
unknown: distance 0
observed area estimate 0.813 m²

Front gap (0.5, 0, 0.5) is green: the nearest scan surface is the slab's wall-facing side, whose normal opposes the face, and nothing looks through it to the far slab face. Back face (0.2, 0.004, 0.5) is green by normal; back face under the slab (0.5, 0.004, 0.5) is green because the slab's wall-facing side agrees with the back normal but lies 10 mm beyond the 4 mm wall, deeper than the 5 mm behind-surface limit. Raising that limit to the full 20 mm distance bound reports zero behind samples and paints that back texel yellow: this is exactly the far-side bleed the limit exists to refuse. The 38 ambiguous samples sit in the millimetre seam band where the front capture's edge and the slab are equally near; none reaches a sampled interior point.

A one-sided yellow sheet 8 mm in front of the gap, facing the same way as the wall, is observed as the wall's appearance at the 20 mm bound and is unknown by distance at a 5 mm bound. Local nearest-surface matching cannot tell such a sheet from the wall without scanner viewpoints; the distance bound is the explicit control, and viewpoint adapters belong to F9.

Why there is no separate occlusion classification

The observation is always the geometric-nearest source surface, so no other source surface can lie between a target sample and its observation. Occlusion therefore reduces to the nearest-first refusals above: an occluder facing the wall is refused by normal, a near-tied pair is ambiguous, a farther one is distance, and a same-facing surface beyond the face is refused by the explicit behind-surface limit added here. maxBehindMetres is a required request field bounded by maxDistanceMetres; coplanar captures are tolerated within f64 rounding under a zero limit (the existing centroid-only control runs with 0).

Normal-load control

native-load.json records an interleaved base/branch/base/branch perf_probe run on AC20-FZK-Haus (five iterations each) on a shared host, not an idle machine. Best totals were 13/14 ms (base) and 13/13 ms (branch) with identical mesh, vertex and triangle counts and one identical ordered geometry fingerprint (25ac885b6ff4ad00) in every run. The changed sampler is not on the load path; this only shows normal loading is unaffected. No transfer-throughput, worker-pool or browser timing is claimed.

Not verified here

No browser Preview/Apply/Undo/export run was made for these fixtures; the existing workspace and full-target controls cover that path with the unchanged Apply/export code. No real paired capture and no independent held-out registration is involved; see what counts as validated.