diff --git a/src/ifcgeom/mapping/IfcObjectPlacement.cpp b/src/ifcgeom/mapping/IfcObjectPlacement.cpp index 098236d7c1..a0a37dd30f 100644 --- a/src/ifcgeom/mapping/IfcObjectPlacement.cpp +++ b/src/ifcgeom/mapping/IfcObjectPlacement.cpp @@ -21,15 +21,37 @@ #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; +#include + taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement* inst) { if (placement_rel_to_type_ || placement_rel_to_instance_) { - // @nb this is not a full solution because we only look for the direct PlacesObject relationships of the current placement, - // a more complete solution should track whether this element sits above the element of which the placement is being ignored. - auto self_places = inst->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(); + using QueueItem = std::pair; + std::deque q = {{inst, 0}}; + while (!q.empty()) { + auto [placement_entity, depth] = q.front(); + q.pop_front(); + + auto placement = placement_entity->as(); + if (!placement) { + continue; + } + + auto self_places = placement->PlacesObject(); + inst->ReferencedByPlacements(); + 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(); + } + } + + // Look for two levels deep, we want to know if we're at or *above* the + // element we're ignoring, but we don't want to traverse the entire model. + if (depth < 2) { + auto refs = placement->ReferencedByPlacements(); + for (auto& ref : *refs) { + q.emplace_back(ref, depth + 1); + } } } }