From d76005745cd8bf26355ef0168251cd2a19554cdd Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Sun, 19 Jul 2026 23:55:48 +0300 Subject: [PATCH] ifcgeom: fix --site-local-placement with nested IfcSites #6102 With decomposed sites the placement chain was truncated at each element's nearest IfcSite ancestor, so products under different sub-sites were expressed in different frames and lost their relative positions. Truncate only at the outermost matching placement instead, so all products end up in the single root site frame. Single-site files and site-owned geometry (#7654) behave as before. Generated with the assistance of an AI coding tool. --- src/ifcgeom/mapping/IfcObjectPlacement.cpp | 52 ++++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/ifcgeom/mapping/IfcObjectPlacement.cpp b/src/ifcgeom/mapping/IfcObjectPlacement.cpp index f307fdb2e8..bf8364d18e 100644 --- a/src/ifcgeom/mapping/IfcObjectPlacement.cpp +++ b/src/ifcgeom/mapping/IfcObjectPlacement.cpp @@ -24,6 +24,42 @@ using namespace ifcopenshell::geometry; #include taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { + auto places_ignored_object = [this](const IfcSchema::IfcObjectPlacement* placement) { + auto places = placement->PlacesObject(); + for (auto iter = places->begin(); iter != places->end(); ++iter) { + if ((placement_rel_to_type_ && (*iter)->declaration().is(*placement_rel_to_type_)) || + (placement_rel_to_instance_ && (*iter)->as() == placement_rel_to_instance_)) { + return true; + } + } + return false; + }; + + auto placement_parent = [](const IfcSchema::IfcObjectPlacement* placement) -> const IfcSchema::IfcObjectPlacement* { +#ifdef SCHEMA_IfcObjectPlacement_HAS_PlacementRelTo + return placement->PlacementRelTo(); +#else + if (auto local = placement->as()) { + return local->PlacementRelTo(); + } + return nullptr; +#endif + }; + + // Only the outermost matching placement is ignored, so that with nested + // sites all products remain positioned in one common coordinate system. + auto is_outermost_match = [&](const IfcSchema::IfcObjectPlacement* placement) { + if (!places_ignored_object(placement)) { + return false; + } + for (auto parent = placement_parent(placement); parent; parent = placement_parent(parent)) { + if (places_ignored_object(parent)) { + return false; + } + } + return true; + }; + if (placement_rel_to_type_ || placement_rel_to_instance_) { using QueueItem = std::pair; std::deque q = {{inst, 0}}; @@ -36,12 +72,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { continue; } - auto self_places = placement->PlacesObject(); - for (auto iter = self_places->begin(); iter != self_places->end(); ++iter) { - if ((placement_rel_to_type_ && (*iter)->declaration().is(*placement_rel_to_type_)) || - (placement_rel_to_instance_ && (*iter)->as() == placement_rel_to_instance_)) { - return taxonomy::make(); - } + if (is_outermost_match(placement)) { + return taxonomy::make(); } // Look for two levels deep, we want to know if we're at or *above* the @@ -93,13 +125,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { bool parent_placement_ignored = false; if (relative_to && (placement_rel_to_type_ || placement_rel_to_instance_)) { - IfcSchema::IfcProduct::list::ptr parent_places = relative_to->PlacesObject(); - for (auto iter = parent_places->begin(); iter != parent_places->end(); ++iter) { - if ((placement_rel_to_type_ && (*iter)->declaration().is(*placement_rel_to_type_)) || - (placement_rel_to_instance_ && (*iter)->as() == placement_rel_to_instance_)) { - parent_placement_ignored = true; - } - } + parent_placement_ignored = is_outermost_match(relative_to); } taxonomy::matrix4::ptr result;