From cad1eecb6987828c27e095a18e94b4af11301fbd Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Sun, 19 Jul 2026 21:35:49 +0300 Subject: [PATCH] ifcgeom: keep one copy of repeated faces in faceset helper duplicate removal When an IfcConnectedFaceSet lists the same IfcFace instance multiple times, the faceset helper registered the loop identity in duplicates_ and wires() then skipped every occurrence of that identity, so the face vanished from the shell entirely instead of being deduplicated to a single copy. The resulting shell had holes, sewing produced a non-manifold first/second boolean operand, and opening subtraction was discarded (Boolean operation yields non-manifold result), leaving the element with no geometry. Count how many occurrences of a loop identity are redundant and skip only that many in wires(), so exactly one copy of each repeated face is still built. In 418--walls--segfault.ifc the opening of wall #2543 repeats 84 of the 350 unique faces of its closed shell (once to three times over the 518 entries), which previously dropped those 84 faces and yielded a 0-vertex wall. With this change the opening operand is manifold and the wall gets a closed, dimensionally correct body. No output changes on the other 257 files in test/input. Generated with the assistance of an AI coding tool. --- src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h | 1 + src/ifcgeom/kernels/opencascade/faceset_helper.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h index e4f3e299d2..b731a5704a 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -86,6 +86,7 @@ private: private: OpenCascadeKernel* kernel_; std::set duplicates_; + std::map duplicate_skips_remaining_; std::map vertex_mapping_; std::map, TopoDS_Edge> edges_; double eps_; diff --git a/src/ifcgeom/kernels/opencascade/faceset_helper.cpp b/src/ifcgeom/kernels/opencascade/faceset_helper.cpp index e86276f1c6..d5a32c30bf 100644 --- a/src/ifcgeom/kernels/opencascade/faceset_helper.cpp +++ b/src/ifcgeom/kernels/opencascade/faceset_helper.cpp @@ -108,6 +108,7 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper( vertex_mapping_.clear(); duplicates_.clear(); + duplicate_skips_remaining_.clear(); edge_use.clear(); @@ -174,6 +175,7 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper( if (edge_sets.find({loop->external.get_value_or(false), segment_set}) != edge_sets.end()) { duplicate_faces++; duplicates_.insert(loop->identity()); + duplicate_skips_remaining_[loop->identity()]++; continue; } edge_sets.insert({loop->external.get_value_or(false), segment_set}); @@ -250,7 +252,10 @@ bool IfcGeom::OpenCascadeKernel::faceset_helper::wire(const ifcopenshell::geomet } bool IfcGeom::OpenCascadeKernel::faceset_helper::wires(const ifcopenshell::geometry::taxonomy::loop::ptr loop, NCollection_List& wires) { - if (duplicates_.find(loop->identity()) != duplicates_.end()) { + // Skip only the redundant occurrences, keep one copy of the face. + auto it = duplicate_skips_remaining_.find(loop->identity()); + if (it != duplicate_skips_remaining_.end() && it->second > 0) { + --it->second; return false; } TopoDS_Wire wire;