You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split out of #3219. The exit-cap fix in #3340 corrects the six opaque wall layers on that model. Two thin IfcCovering layers stay wrong, for a different reason, and this is that reason.
Symptom
Reporter's model (anonymized.ifc from the #3219 thread), wall #392764 / #394469. Each wall is decomposed into 4 layers; the thin covering layer of each comes out of the void cut as a torn, non-watertight shell:
mesh
tris
edges
edges with degree != 2
part_208407_novoid
188
282
0
part_208407_cut
408
710
198
part_412191_cut
742
1432
662
part_208380_cut (opaque layer, for contrast)
392
588
0
Rank this on the bad-edge count, not on volume. signed_volume on an open shell is true volume plus T·N/3, so the figure that shows up for #208407 (about 1889) is not a volume — roughly 20.3 m2 of unbalanced vector area at a ~279 m frame offset produces it.
Setting the flush-cap pad to 0.0 collapses the six opaque layers to near-zero error but leaves both coverings open and wrong (+8.31 and +102.9 abs error). So the pad is not the cause here.
Per-opening isolation on the covering at pad = 0: every single cutter behaves (worst delta -0.0397 m3), yet the cumulative 22-opening result is broken. This is an accumulation or batching failure on a thin host, not any one cutter.
Very likely the same root, reduced to a minimal case
The randomized CSG property test finds this independently on pristine main (reproduced in a clean sandbox with no source changes):
cargo test -p ifc-lite-geometry --test csg_property_test
with this seed in rust/geometry/tests/csg_property_test.proptest-regressions:
The kernel removes 1.0 m3 when subtracting a box that only touches. That is the whole defect class in one reproducer, with no IFC involved. I did not commit the regressions file, because doing so turns the suite red for every unrelated PR.
A false claim in the code, measured false
accept_cut in rust/geometry/src/router/voids/coaxial_union.rs carries:
the watertightness of the cut is guaranteed upstream: each cutter is mesh_is_closed_exact, and the kernel's conformity gate rejects a non-conforming arrangement
Measured at subtract_prisms -> subtract_mesh_many on this model, the FIRST cut:
host 188 tris (closed=true) vol 2.5913 cutters=1 -> 236 tris (closed=false) vol 2.5541
Closed in, open out, on cutter number one. The subsequent 21 cuts then run on a progressively broken shell. Adding if mesh_is_closed_exact(result) && !mesh_is_closed_exact(&cut) { return false; } to accept_cut moved #208407 a long way (1889 -> 100) without fixing it, and changed nothing else in the suite. Worth doing, not sufficient alone.
What the reference implementation does
ifcopenshell never feeds a touching cutter to its kernel. On this model it logs:
and returns identical volume with opening subtraction enabled and disabled. manifold3d independently puts the intersection volume of every (part, opening) pair at 0.0000 m3.
An operand-elimination pre-filter of that shape was prototyped during the #3219 investigation and healed both coverings on this model, but its predicate was a point sample and an adversarial pass showed it silently keeping a real 0.2 m mullion inside an opening footprint with no diagnostic. It needs a conservative predicate before it is shippable.
Suggested order
Fix the touching-operand case in the kernel, using the proptest seed above as the gate. That is the root, and it needs no IFC fixture.
Enforce closed-in/closed-out in accept_cut and correct its doc comment.
Split out of #3219. The exit-cap fix in #3340 corrects the six opaque wall layers on that model. Two thin
IfcCoveringlayers stay wrong, for a different reason, and this is that reason.Symptom
Reporter's model (
anonymized.ifcfrom the #3219 thread), wall #392764 / #394469. Each wall is decomposed into 4 layers; the thin covering layer of each comes out of the void cut as a torn, non-watertight shell:part_208407_novoidpart_208407_cutpart_412191_cutpart_208380_cut(opaque layer, for contrast)Rank this on the bad-edge count, not on volume.
signed_volumeon an open shell is true volume plusT·N/3, so the figure that shows up for #208407 (about 1889) is not a volume — roughly 20.3 m2 of unbalanced vector area at a ~279 m frame offset produces it.Why it is independent of #3340
Setting the flush-cap pad to 0.0 collapses the six opaque layers to near-zero error but leaves both coverings open and wrong (+8.31 and +102.9 abs error). So the pad is not the cause here.
Per-opening isolation on the covering at pad = 0: every single cutter behaves (worst delta -0.0397 m3), yet the cumulative 22-opening result is broken. This is an accumulation or batching failure on a thin host, not any one cutter.
Very likely the same root, reduced to a minimal case
The randomized CSG property test finds this independently on pristine main (reproduced in a clean sandbox with no source changes):
with this seed in
rust/geometry/tests/csg_property_test.proptest-regressions:A's max z and B's min z are the same f64, so the boxes touch on a face with zero-volume overlap and
vol(A-B)must equalvol(A):The kernel removes 1.0 m3 when subtracting a box that only touches. That is the whole defect class in one reproducer, with no IFC involved. I did not commit the regressions file, because doing so turns the suite red for every unrelated PR.
A false claim in the code, measured false
accept_cutinrust/geometry/src/router/voids/coaxial_union.rscarries:Measured at
subtract_prisms->subtract_mesh_manyon this model, the FIRST cut:Closed in, open out, on cutter number one. The subsequent 21 cuts then run on a progressively broken shell. Adding
if mesh_is_closed_exact(result) && !mesh_is_closed_exact(&cut) { return false; }toaccept_cutmoved #208407 a long way (1889 -> 100) without fixing it, and changed nothing else in the suite. Worth doing, not sufficient alone.What the reference implementation does
ifcopenshell never feeds a touching cutter to its kernel. On this model it logs:
and returns identical volume with opening subtraction enabled and disabled. manifold3d independently puts the intersection volume of every (part, opening) pair at 0.0000 m3.
An operand-elimination pre-filter of that shape was prototyped during the #3219 investigation and healed both coverings on this model, but its predicate was a point sample and an adversarial pass showed it silently keeping a real 0.2 m mullion inside an opening footprint with no diagnostic. It needs a conservative predicate before it is shippable.
Suggested order
accept_cutand correct its doc comment.