From c1fb953a82d1a0b4a7cdd3db3fb622e57da0455e Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 8 May 2024 10:26:24 +0200 Subject: [PATCH] Initial attempt at shared pointer storage of instances and weap ptr access in python --- src/ifcconvert/IfcConvert.cpp | 38 +++---- src/ifcparse/IfcFile.h | 102 ++++++++++++------ src/ifcparse/IfcParse.cpp | 159 +++++++++++++++-------------- src/ifcparse/parse_ifcxml.cpp | 2 +- src/ifcwrap/IfcParseWrapper.i | 99 +++++++++++++++--- src/serializers/GltfSerializer.cpp | 16 +-- src/serializers/SvgSerializer.cpp | 116 ++++++++++----------- 7 files changed, 317 insertions(+), 215 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 3f5b411f0d..e9d417a32a 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -1508,51 +1508,47 @@ namespace latebound_access { IfcParse::IfcGlobalId guid; latebound_access::set(inst, "GlobalId", (std::string) guid); } - return f.addEntity(inst); + return &*f.addEntity(inst); } } void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) { { - auto delete_reversed = [&f](const aggregate_of_instance::ptr& insts) { - if (!insts) { - return; - } + auto delete_range = [&f](const IfcParse::IfcFile::type_iterator_range_t& insts) { // Lists are traversed back to front as the list may be mutated when // instances are removed from the grouping by type. - for (auto it = insts->end() - 1; it >= insts->begin(); --it) { - IfcUtil::IfcBaseClass* const inst = *it; - f.removeEntity(inst); + for (auto it = insts.first; it != insts.second; ++it) { + f.removeEntity(&**it); } }; // Delete quantities auto quantities = f.instances_by_type("IfcPhysicalQuantity"); - if (quantities) { - quantities = quantities->filtered({ f.schema()->declaration_by_name("IfcPhysicalComplexQuantity") }); - delete_reversed(quantities); + for (auto& q : boost::make_iterator_range(quantities)) { + // @todo test iterator invalidation + if (q->declaration().name() == "IfcPhysicalComplexQuantity") { + f.removeEntity(&*q); + } } // Delete complexes - delete_reversed(f.instances_by_type("IfcPhysicalComplexQuantity")); + delete_range(f.instances_by_type("IfcPhysicalComplexQuantity")); auto element_quantities = f.instances_by_type("IfcElementQuantity"); // Capture relationship nodes std::vector relationships; auto IfcRelDefinesByProperties = f.schema()->declaration_by_name("IfcRelDefinesByProperties"); - if (element_quantities) { - for (auto& eq : *element_quantities) { - auto rels = eq->data().getInverse(IfcRelDefinesByProperties, -1); - for (auto& rel : *rels) { - relationships.push_back(rel); - } + for (auto& eq : boost::make_iterator_range(element_quantities)) { + auto rels = eq->data().getInverse(IfcRelDefinesByProperties, -1); + for (auto& rel : *rels) { + relationships.push_back(rel); } - - // Delete element quantities - delete_reversed(element_quantities); } + // Delete element quantities + delete_range(element_quantities); + // Delete relationship nodes for (auto& rel : relationships) { diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index b62126841a..0606f23860 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -68,10 +68,18 @@ class IFC_PARSE_API file_open_status { /// and provide access to the entities in an IFC file class IFC_PARSE_API IfcFile { public: - typedef std::map entities_by_type_t; - typedef boost::unordered_map entity_by_id_t; - typedef boost::unordered_map entity_by_iden_t; - typedef std::map entity_by_guid_t; +#ifndef NO_SHARED_POINTER_STORAGE + typedef std::shared_ptr instance_storage_type; + typedef std::weak_ptr instance_reference_type; +#else + typedef IfcUtil::IfcBaseClass* instance_storage_type; + typedef IfcUtil::IfcBaseClass* instance_reference_type; +#endif + + typedef std::multimap entities_by_type_t; + typedef boost::unordered_map entity_by_id_t; + typedef boost::unordered_map entity_by_iden_t; + typedef std::map entity_by_guid_t; typedef std::tuple inverse_attr_record; enum INVERSE_ATTR { INSTANCE_ID, @@ -80,9 +88,9 @@ class IFC_PARSE_API IfcFile { }; typedef std::map> entities_by_ref_t; typedef std::map> entities_by_ref_excl_t; - typedef std::map ref_map_t; typedef entity_by_id_t::const_iterator const_iterator; + template class type_iterator : private entities_by_type_t::const_iterator { public: type_iterator() : entities_by_type_t::const_iterator(){}; @@ -90,16 +98,35 @@ class IFC_PARSE_API IfcFile { type_iterator(const entities_by_type_t::const_iterator& iter) : entities_by_type_t::const_iterator(iter){}; - entities_by_type_t::key_type const* operator->() const { - return &entities_by_type_t::const_iterator::operator->()->first; + T const* operator->() const { + if constexpr (std::is_same_v) { + return &entities_by_type_t::const_iterator::operator->()->first; + } else { + return &entities_by_type_t::const_iterator::operator->()->second; + } } - entities_by_type_t::key_type const& operator*() const { - return entities_by_type_t::const_iterator::operator*().first; + T const& operator*() const { + if constexpr (std::is_same_v) { + return entities_by_type_t::const_iterator::operator*().first; + } else { + return entities_by_type_t::const_iterator::operator*().second; + } } type_iterator& operator++() { - entities_by_type_t::const_iterator::operator++(); + auto k = **this; + // @todo we changed from map(aggregate) to multimap(instance) + // (why again?) so now we need to keep iterating in our type + // iterator. Given the distribution of entity types, this + // likely has performance impliciations, but this function is + // probably hardly ever used. + do { + entities_by_type_t::const_iterator::operator++(); + if constexpr (std::is_same_v) { + break; + } + } while (k == **this); return *this; } @@ -116,6 +143,8 @@ class IFC_PARSE_API IfcFile { } }; + typedef std::pair, type_iterator> type_iterator_range_t; + static bool lazy_load_; static bool lazy_load() { return lazy_load_; } static void lazy_load(bool b) { lazy_load_ = b; } @@ -125,7 +154,7 @@ class IFC_PARSE_API IfcFile { static void guid_map(bool b) { guid_map_ = b; } private: - typedef std::map entity_entity_map_t; + typedef std::map entity_entity_map_t; bool parsing_complete_; file_open_status good_ = file_open_status::SUCCESS; @@ -192,48 +221,50 @@ class IFC_PARSE_API IfcFile { /// with the highest id (EXPRESS ENTITY_INSTANCE_NAME) const_iterator end() const; - type_iterator types_begin() const; - type_iterator types_end() const; + type_iterator types_begin() const; + type_iterator types_end() const; - type_iterator types_incl_super_begin() const; - type_iterator types_incl_super_end() const; + type_iterator types_incl_super_begin() const; + type_iterator types_incl_super_end() const; /// Returns all entities in the file that match the template argument. /// NOTE: This also returns subtypes of the requested type, for example: /// IfcWall will also return IfcWallStandardCase entities template typename T::list::ptr instances_by_type() { - aggregate_of_instance::ptr untyped_list = instances_by_type(&T::Class()); - if (untyped_list) { - return untyped_list->as(); + auto range = instances_by_type(&T::Class()); + typename T::list::ptr vec(new typename T::list); + for (auto it = range.first; it != range.second; ++it) { + vec->push((*it)->template as()); } - return typename T::list::ptr(new typename T::list); + return vec; } template typename T::list::ptr instances_by_type_excl_subtypes() { - aggregate_of_instance::ptr untyped_list = instances_by_type_excl_subtypes(&T::Class()); - if (untyped_list) { - return untyped_list->as(); + auto range = instances_by_type_excl_subtypes(&T::Class()); + typename T::list::ptr vec(new typename T::list); + for (auto it = range.first; it != range.second; ++it) { + vec->push((*it)->template as()); } - return typename T::list::ptr(new typename T::list); + return vec; } /// Returns all entities in the file that match the positional argument. /// NOTE: This also returns subtypes of the requested type, for example: /// IfcWall will also return IfcWallStandardCase entities - aggregate_of_instance::ptr instances_by_type(const IfcParse::declaration*); + type_iterator_range_t instances_by_type(const IfcParse::declaration*); /// Returns all entities in the file that match the positional argument. - aggregate_of_instance::ptr instances_by_type_excl_subtypes(const IfcParse::declaration*); + type_iterator_range_t instances_by_type_excl_subtypes(const IfcParse::declaration*); /// Returns all entities in the file that match the positional argument. /// NOTE: This also returns subtypes of the requested type, for example: /// IfcWall will also return IfcWallStandardCase entities - aggregate_of_instance::ptr instances_by_type(const std::string& type); + type_iterator_range_t instances_by_type(const std::string& type); /// Returns all entities in the file that match the positional argument. - aggregate_of_instance::ptr instances_by_type_excl_subtypes(const std::string& type); + type_iterator_range_t instances_by_type_excl_subtypes(const std::string& type); /// Returns all entities in the file that reference the id aggregate_of_instance::ptr instances_by_reference(int id); @@ -241,8 +272,10 @@ class IFC_PARSE_API IfcFile { /// Returns the entity with the specified id IfcUtil::IfcBaseClass* instance_by_id(int id); + IfcFile::instance_storage_type instance_by_id_2(int id); + /// Returns the entity with the specified GlobalId - IfcUtil::IfcBaseClass* instance_by_guid(const std::string& guid); + IfcFile::instance_storage_type instance_by_guid(const std::string& guid); /// Performs a depth-first traversal, returning all entity instance /// attributes as a flat list. NB: includes the root instance specified @@ -274,7 +307,7 @@ class IFC_PARSE_API IfcFile { void recalculate_id_counter(); - IfcUtil::IfcBaseClass* addEntity(IfcUtil::IfcBaseClass* entity, int id = -1); + IfcFile::instance_storage_type addEntity(IfcUtil::IfcBaseClass* entity, int id = -1); void addEntities(aggregate_of_instance::ptr entities); void batch() { batch_mode_ = true; } @@ -327,13 +360,22 @@ IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename); namespace std { template <> -struct iterator_traits { +struct iterator_traits> { typedef ptrdiff_t difference_type; typedef const IfcParse::declaration* value_type; typedef const IfcParse::declaration*& reference; typedef const IfcParse::declaration** pointer; typedef std::forward_iterator_tag iterator_category; }; + +template <> +struct iterator_traits> { + typedef ptrdiff_t difference_type; + typedef IfcParse::IfcFile::entities_by_type_t::value_type value_type; + typedef IfcParse::IfcFile::entities_by_type_t::value_type reference; + typedef IfcParse::IfcFile::entities_by_type_t::value_type* pointer; + typedef std::forward_iterator_tag iterator_category; +}; } // namespace std #endif diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 125d2e2fd2..338f308b30 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1555,7 +1555,7 @@ void IfcEntityInstanceData::setArgument(size_t i, Argument* a, IfcUtil::Argument if (it != this->file->internal_guid_map().end()) { Logger::Warning("Duplicate guid " + guid); } - this->file->internal_guid_map()[guid] = this->file->instance_by_id(this->id()); + this->file->internal_guid_map()[guid] = this->file->instance_by_id_2(this->id()); } catch (IfcParse::IfcException& e) { Logger::Error(e); } @@ -1656,7 +1656,7 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { boost::circular_buffer token_stream(3, Token()); IfcEntityInstanceData* data; - IfcUtil::IfcBaseClass* instance = 0; + instance_storage_type instance = 0; unsigned current_id = 0; int progress = 0; @@ -1682,7 +1682,7 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { } data = new IfcEntityInstanceData(entity_type, this, current_id, token_stream[2].startPos); - instance = schema()->instantiate(data); + instance = instance_storage_type(schema()->instantiate(data)); /// @todo Printing to stdout in a library class feels weird. Maybe move the progress prints to the client code? // Update the status after every 1000 instances parsed @@ -1715,22 +1715,10 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { const IfcParse::declaration* ty = &instance->declaration(); - { - aggregate_of_instance::ptr insts = instances_by_type_excl_subtypes(ty); - if (!insts) { - insts = aggregate_of_instance::ptr(new aggregate_of_instance()); - bytype_excl_[ty] = insts; - } - insts->push(instance); - } + bytype_excl_.insert({ ty, instance }); for (;;) { - aggregate_of_instance::ptr insts = instances_by_type(ty); - if (!insts) { - insts = aggregate_of_instance::ptr(new aggregate_of_instance()); - bytype_[ty] = insts; - } - insts->push(instance); + bytype_.insert({ ty, instance }); const IfcParse::declaration* pt = ty->as_entity()->supertype(); if (pt != nullptr) { ty = pt; @@ -1898,7 +1886,7 @@ void IfcFile::addEntities(aggregate_of_instance::ptr entities) { } } -IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) { +IfcFile::instance_storage_type IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) { if (id != -1 && byid_.find((unsigned)id) != byid_.end()) { throw IfcParse::IfcException("An instance with id " + boost::lexical_cast(id) + " is already part of this file"); } @@ -1914,7 +1902,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) return mit->second; } - IfcUtil::IfcBaseClass* new_entity = entity; + instance_storage_type new_entity = instance_storage_type(entity); // Obtain all forward references by a depth-first // traversal and add them to the file. @@ -1944,7 +1932,16 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) // If it is part of this file // nothing else needs to be done. - return entity; + if constexpr (std::is_same_v) { + return entity; + } else { + for (auto& x : byid_) { + if (&*x.second == entity) { + return x.second; + } + } + throw std::runtime_error("Internal error"); + } } // An instance is being added from another file. A copy of the @@ -1952,7 +1949,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) // need to be updated to point to instances in this file. IfcFile* other_file = entity->data().file; IfcEntityInstanceData* we = new IfcEntityInstanceData(entity->data()); - new_entity = schema()->instantiate(we); + new_entity = instance_storage_type(schema()->instantiate(we)); // In case an entity is added that contains geometry, the unit // information needs to be accounted for for IfcLengthMeasures. @@ -1981,7 +1978,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) } IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument(); - copy->set(eit->second); + copy->set(&*eit->second); we->setArgument(i, copy); } else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) { aggregate_of_instance::ptr instances = *attr; @@ -1991,7 +1988,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) if (eit == entity_file_map_.end()) { throw IfcParse::IfcException("Unable to map instance to file"); } - new_instances->push(eit->second); + new_instances->push(&*eit->second); } IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument(); @@ -2007,7 +2004,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) if (eit == entity_file_map_.end()) { throw IfcParse::IfcException("Unable to map instance to file"); } - list.push_back(eit->second); + list.push_back(&*eit->second); } new_instances->push(list); } @@ -2076,7 +2073,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) } } - entity_file_map_.insert(entity_entity_map_t::value_type(entity->identity(), new_entity)); + entity_file_map_.insert(entity_entity_map_t::value_type(entity->identity(), &*new_entity)); } // For subtypes of IfcRoot, the GUID mapping needs to be updated. @@ -2098,21 +2095,11 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) const IfcParse::declaration* ty = &new_entity->declaration(); if (ty->as_entity() != nullptr) { - aggregate_of_instance::ptr insts = instances_by_type_excl_subtypes(ty); - if (!insts) { - insts = aggregate_of_instance::ptr(new aggregate_of_instance()); - bytype_excl_[ty] = insts; - } - insts->push(new_entity); + bytype_excl_.insert({ ty, new_entity }); } for (; ty->as_entity() != nullptr;) { - aggregate_of_instance::ptr insts = instances_by_type(ty); - if (!insts) { - insts = aggregate_of_instance::ptr(new aggregate_of_instance()); - bytype_[ty] = insts; - } - insts->push(new_entity); + bytype_.insert({ ty, new_entity }); const IfcParse::declaration* pt = ty->as_entity()->supertype(); if (pt != nullptr) { @@ -2158,7 +2145,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) } if (parsing_complete_ && (ty->as_entity() != nullptr)) { - build_inverses_(new_entity); + build_inverses_(&*new_entity); } return new_entity; @@ -2323,20 +2310,24 @@ void IfcFile::process_deletion_() { const IfcParse::declaration* ty = &entity->declaration(); { - aggregate_of_instance::ptr instances_of_same_type = instances_by_type_excl_subtypes(ty); - instances_of_same_type->remove(entity); - if (instances_of_same_type->size() == 0) { - bytype_excl_.erase(ty); + auto instances_of_same_type = bytype_excl_.equal_range(ty); + for (auto it = instances_of_same_type.first; it != instances_of_same_type.second;) { + if (&*it->second == entity) { + it = bytype_excl_.erase(it); + } else { + ++it; + } } } for (;;) { - aggregate_of_instance::ptr instances_of_same_type = instances_by_type(ty); - if (instances_of_same_type) { - instances_of_same_type->remove(entity); - } - if (instances_of_same_type->size() == 0) { - bytype_.erase(ty); + auto instances_of_same_type = bytype_.equal_range(ty); + for (auto it = instances_of_same_type.first; it != instances_of_same_type.second;) { + if (&*it->second == entity) { + it = bytype_.erase(it); + } else { + ++it; + } } const IfcParse::declaration* pt = ty->as_entity()->supertype(); @@ -2350,14 +2341,16 @@ void IfcFile::process_deletion_() { // entity_file_map is in place to prevent duplicate definitions with usage of add(). // Upon deletion the pairs need to be erased. for (auto it = entity_file_map_.begin(); it != entity_file_map_.end();) { - if (it->second == entity) { + if (&*it->second == entity) { it = entity_file_map_.erase(it); } else { ++it; } } - delete entity; + if constexpr (std::is_same_v) { + delete entity; + } } if (batch_mode_) { @@ -2397,21 +2390,19 @@ void IfcFile::process_deletion_() { batch_deletion_ids_.clear(); } -aggregate_of_instance::ptr IfcFile::instances_by_type(const IfcParse::declaration* t) { - entities_by_type_t::const_iterator it = bytype_.find(t); - return (it == bytype_.end()) ? aggregate_of_instance::ptr() : it->second; +IfcFile::type_iterator_range_t IfcFile::instances_by_type(const IfcParse::declaration* t) { + return bytype_.equal_range(t); } -aggregate_of_instance::ptr IfcFile::instances_by_type_excl_subtypes(const IfcParse::declaration* t) { - entities_by_type_t::const_iterator it = bytype_excl_.find(t); - return (it == bytype_excl_.end()) ? aggregate_of_instance::ptr() : it->second; +IfcFile::type_iterator_range_t IfcFile::instances_by_type_excl_subtypes(const IfcParse::declaration* t) { + return bytype_.equal_range(t); } -aggregate_of_instance::ptr IfcFile::instances_by_type(const std::string& t) { +IfcFile::type_iterator_range_t IfcFile::instances_by_type(const std::string& t) { return instances_by_type(schema()->declaration_by_name(t)); } -aggregate_of_instance::ptr IfcFile::instances_by_type_excl_subtypes(const std::string& t) { +IfcFile::type_iterator_range_t IfcFile::instances_by_type_excl_subtypes(const std::string& t) { return instances_by_type_excl_subtypes(schema()->declaration_by_name(t)); } @@ -2424,6 +2415,14 @@ aggregate_of_instance::ptr IfcFile::instances_by_reference(int t) { } IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) { + entity_by_id_t::const_iterator it = byid_.find(id); + if (it == byid_.end()) { + throw IfcException("Instance #" + boost::lexical_cast(id) + " not found"); + } + return &*it->second; +} + +IfcFile::instance_storage_type IfcFile::instance_by_id_2(int id) { entity_by_id_t::const_iterator it = byid_.find(id); if (it == byid_.end()) { throw IfcException("Instance #" + boost::lexical_cast(id) + " not found"); @@ -2431,7 +2430,7 @@ IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) { return it->second; } -IfcUtil::IfcBaseClass* IfcFile::instance_by_guid(const std::string& guid) { +IfcFile::instance_storage_type IfcFile::instance_by_guid(const std::string& guid) { entity_by_guid_t::const_iterator it = byguid_.find(guid); if (it == byguid_.end()) { throw IfcException("Instance with GlobalId '" + guid + "' not found"); @@ -2441,15 +2440,17 @@ IfcUtil::IfcBaseClass* IfcFile::instance_by_guid(const std::string& guid) { // FIXME: Test destructor to delete entity and arg allocations IfcFile::~IfcFile() { - std::set entities_to_delete; - for (const auto& pair : byid_) { - entities_to_delete.insert(pair.second); - } - for (const auto& pair : byidentity_) { - entities_to_delete.insert(pair.second); - } - for (auto* entity : entities_to_delete) { - delete entity; + if constexpr (std::is_same_v) { + std::set entities_to_delete; + for (const auto& pair : byid_) { + entities_to_delete.insert(pair.second); + } + for (const auto& pair : byidentity_) { + entities_to_delete.insert(pair.second); + } + for (auto entity : entities_to_delete) { + delete &*entity; + } } delete stream; delete tokens; @@ -2463,19 +2464,19 @@ IfcFile::entity_by_id_t::const_iterator IfcFile::end() const { return byid_.end(); } -IfcFile::type_iterator IfcFile::types_begin() const { +IfcFile::type_iterator IfcFile::types_begin() const { return bytype_excl_.begin(); } -IfcFile::type_iterator IfcFile::types_end() const { +IfcFile::type_iterator IfcFile::types_end() const { return bytype_excl_.end(); } -IfcFile::type_iterator IfcFile::types_incl_super_begin() const { +IfcFile::type_iterator IfcFile::types_incl_super_begin() const { return bytype_.begin(); } -IfcFile::type_iterator IfcFile::types_incl_super_end() const { +IfcFile::type_iterator IfcFile::types_incl_super_end() const { return bytype_.end(); } @@ -2490,12 +2491,12 @@ struct id_instance_pair_sorter { std::ostream& operator<<(std::ostream& out, const IfcParse::IfcFile& file) { file.header().write(out); - typedef std::vector> vector_t; + typedef std::vector> vector_t; vector_t sorted(file.begin(), file.end()); std::sort(sorted.begin(), sorted.end(), id_instance_pair_sorter()); for (vector_t::const_iterator it = sorted.begin(); it != sorted.end(); ++it) { - const IfcUtil::IfcBaseClass* e = it->second; + auto& e = it->second; if (e->declaration().as_entity() != nullptr) { out << e->data().toString(true) << ";" << std::endl; } @@ -2622,16 +2623,16 @@ void IfcFile::setDefaultHeaderValues() { std::pair IfcFile::getUnit(const std::string& unit_type) { std::pair return_value(0, 1.); - aggregate_of_instance::ptr projects = instances_by_type(schema()->declaration_by_name("IfcProject")); - if (!projects || projects->size() == 0) { + auto projects = instances_by_type(schema()->declaration_by_name("IfcProject")); + if (std::distance(projects.first, projects.second) == 0) { try { projects = instances_by_type(schema()->declaration_by_name("IfcContext")); } catch (IfcException& e) { } } - if (projects && projects->size() == 1) { - IfcUtil::IfcBaseClass* project = *projects->begin(); + if (std::distance(projects.first, projects.second) == 1) { + auto project = *projects.first; IfcUtil::IfcBaseClass* unit_assignment = *project->data().getArgument( project->declaration().as_entity()->attribute_index("UnitsInContext")); @@ -2704,7 +2705,7 @@ void IfcParse::IfcFile::build_inverses_(IfcUtil::IfcBaseClass* inst) { void IfcParse::IfcFile::build_inverses() { for (const auto& pair : *this) { - build_inverses_(pair.second); + build_inverses_(&*pair.second); } } diff --git a/src/ifcparse/parse_ifcxml.cpp b/src/ifcparse/parse_ifcxml.cpp index 922fb94091..2e1a8dfac4 100644 --- a/src/ifcparse/parse_ifcxml.cpp +++ b/src/ifcparse/parse_ifcxml.cpp @@ -480,7 +480,7 @@ static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) if (state->dialect == ifcxml_dialect_ifc4) { // In IFC2X3 not added directly because attrs such as GlobalId are in // subsequent child nodes - newinst = state->file->addEntity(newinst); + newinst = &*state->file->addEntity(newinst); if (id) { state->idmap[*id] = newinst->data().id(); } diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index 3f2800b62e..7f7540a386 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -49,10 +49,10 @@ private: %rename("by_id") instance_by_id; %rename("by_type") instances_by_type; %rename("by_type_excl_subtypes") instances_by_type_excl_subtypes; -%rename("entity_instance") IfcBaseClass; + %rename("file") IfcFile; -%rename("add") addEntity; -%rename("remove") removeEntity; +%ignore IfcParse::IfcFile::addEntity; +%ignore IfcParse::IfcFile::removeEntity; class attribute_value_derived {}; %{ @@ -88,12 +88,71 @@ PyObject* get_feature(const std::string& x) { %} %{ +class entity_instance; +const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl); +IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsigned i); +%} -static const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) { + +%inline %{ +class entity_instance { + boost::variant< + IfcParse::IfcFile::instance_storage_type, + std::weak_ptr + > data_; + + struct visitor { + IfcParse::IfcFile::instance_storage_type operator()(const IfcParse::IfcFile::instance_storage_type& t) { + return t; + } + IfcParse::IfcFile::instance_storage_type operator()(const std::weak_ptr& t) { + auto u = t.lock(); + if (u) { + return u; + } else { + throw std::runtime_error("No longer availabe"); + } + } + }; +public: + entity_instance(const IfcParse::IfcFile::instance_storage_type& shared) + : data_(std::weak_ptr(shared)) + {} + + entity_instance(IfcUtil::IfcBaseClass* naked) + : data_(IfcParse::IfcFile::instance_storage_type(naked)) + {} + + operator IfcUtil::IfcBaseClass*() const { + return &*boost::apply_visitor(visitor{}, data_); + } + + const IfcParse::declaration& declaration() const { + return boost::apply_visitor(visitor{}, data_)->declaration(); + } + + const IfcEntityInstanceData& data() const { + return boost::apply_visitor(visitor{}, data_)->data(); + } + + IfcEntityInstanceData& data() { + return boost::apply_visitor(visitor{}, data_)->data(); + } + + uint32_t identity() const { + return boost::apply_visitor(visitor{}, data_)->identity(); + } +}; + +%} + +%{ + +const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) { return decl->name(); } -static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClass* inst, unsigned i) { +IfcUtil::ArgumentType helper_fn_attribute_type(const entity_instance* inst, unsigned i) { const IfcParse::parameter_type* pt = 0; if (inst->declaration().as_entity()) { pt = inst->declaration().as_entity()->attribute_by_index(i)->type_of_attribute(); @@ -122,20 +181,28 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas return reinterpret_cast($self); } - IfcUtil::IfcBaseClass* by_guid(const std::string& guid) { + entity_instance by_guid(const std::string& guid) { return $self->instance_by_guid(guid); } + + entity_instance add(entity_instance& e, int i) { + return $self->addEntity(e, i); + } + + void remove(entity_instance& e) { + return $self->removeEntity(e); + } - aggregate_of_instance::ptr get_inverse(IfcUtil::IfcBaseClass* e) { - return $self->getInverse(e->data().id(), 0, -1); + aggregate_of_instance::ptr get_inverse(entity_instance& e) { + return $self->getInverse(e.data().id(), 0, -1); } - std::vector get_inverse_indices(IfcUtil::IfcBaseClass* e) { - return $self->get_inverse_indices(e->data().id()); + std::vector get_inverse_indices(entity_instance& e) { + return $self->get_inverse_indices(e.data().id()); } - int get_total_inverses(IfcUtil::IfcBaseClass* e) { - return $self->getTotalInverses(e->data().id()); + int get_total_inverses(entity_instance& e) { + return $self->getTotalInverses(e.data().id()); } void write(const std::string& fn) { @@ -186,7 +253,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas %} } -%extend IfcUtil::IfcBaseClass { +%extend entity_instance { int get_attribute_category(const std::string& name) const { if (!$self->declaration().as_entity()) { @@ -288,7 +355,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas return std::pair($self->data().getArgument(i)->type(), $self->data().getArgument(i)); } - bool __eq__(IfcUtil::IfcBaseClass* other) const { + bool __eq__(entity_instance* other) const { return $self->identity() == other->identity(); } @@ -477,7 +544,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas } } - void setArgumentAsEntityInstance(unsigned int i, IfcUtil::IfcBaseClass* v) { + void setArgumentAsEntityInstance(unsigned int i, entity_instance* v) { IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); if (arg_type == IfcUtil::Argument_ENTITY_INSTANCE) { IfcWrite::IfcWriteArgument* arg = new IfcWrite::IfcWriteArgument(); @@ -603,7 +670,7 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas return IFCOPENSHELL_VERSION; } - IfcUtil::IfcBaseClass* new_IfcBaseClass(const std::string& schema_identifier, const std::string& name) { + entity_instance new_IfcBaseClass(const std::string& schema_identifier, const std::string& name) { const IfcParse::schema_definition* schema = IfcParse::schema_by_name(schema_identifier); const IfcParse::declaration* decl = schema->declaration_by_name(name); IfcEntityInstanceData* data = new IfcEntityInstanceData(decl); diff --git a/src/serializers/GltfSerializer.cpp b/src/serializers/GltfSerializer.cpp index fac2ed51b0..1930fd8d09 100644 --- a/src/serializers/GltfSerializer.cpp +++ b/src/serializers/GltfSerializer.cpp @@ -443,14 +443,14 @@ void GltfSerializer::setFile(IfcParse::IfcFile* f) { boost::optional> crs_x_axis; boost::optional> eastings_northings_elevation; - aggregate_of_instance::ptr coordops; + IfcParse::IfcFile::type_iterator_range_t coordops; try { coordops = f->instances_by_type("IfcCoordinateOperation"); } catch (IfcParse::IfcException&) { // Ignored. Schema likely doesn't support IfcCoordinateOperation. } - if (coordops) { - for (auto& coordop : *coordops) { + if (std::distance(coordops.first, coordops.second)) { + for (auto& coordop : boost::make_iterator_range(coordops)) { IfcUtil::IfcBaseClass* source_crs = *coordop->as()->get("SourceCRS"); if (source_crs->declaration().is("IfcGeometricRepresentationContext")) { IfcUtil::IfcBaseClass* target_crs = *coordop->as()->get("TargetCRS"); @@ -486,9 +486,9 @@ void GltfSerializer::setFile(IfcParse::IfcFile* f) { if (!crs_epsg) { auto sites = f->instances_by_type("IfcSite"); - if (sites && sites->size() == 1) { - auto lat_attr = (*sites->begin())->as()->get("RefLatitude"); - auto lon_attr = (*sites->begin())->as()->get("RefLongitude"); + if (std::distance(sites.first, sites.second)) { + auto lat_attr = (*sites.first)->as()->get("RefLatitude"); + auto lon_attr = (*sites.first)->as()->get("RefLongitude"); if (!lat_attr->isNull() && !lon_attr->isNull()) { std::vector lat_dms = *lat_attr; @@ -521,8 +521,8 @@ void GltfSerializer::setFile(IfcParse::IfcFile* f) { auto contexts = f->instances_by_type_excl_subtypes("IfcGeometricRepresentationContext"); - if (contexts && contexts->size() > 0) { - auto context = (*contexts->begin())->as(); + if (std::distance(contexts.first, contexts.second)) { + auto context = (*contexts.first)->as(); auto north_attr = context->get("TrueNorth"); if (!north_attr->isNull()) { IfcUtil::IfcBaseClass* north = *north_attr; diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 1db8f772da..8a2f61f0c9 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -1830,13 +1830,13 @@ void SvgSerializer::addTextAnnotations(const drawing_key& k) { } } - aggregate_of_instance::ptr annotations; + boost::optional annotations; if (file) { annotations = file->instances_by_type("IfcAnnotation"); } if (annotations) { - for (auto& ann_ : *annotations) { - auto ann = (IfcUtil::IfcBaseEntity*) ann_; + for (auto& ann_ : boost::make_iterator_range(*annotations)) { + auto ann = ann_->as(); auto ot = ann->get("ObjectType"); auto nm = ann->get("Name"); @@ -2072,50 +2072,48 @@ void SvgSerializer::finalize() { if (file && storey_height_display_ != SH_NONE && pln && std::abs(pln->Position().Direction().Z()) < 1.e-5) { auto storeys = file->instances_by_type("IfcBuildingStorey"); - if (storeys) { - const double lu = file->getUnit("LENGTHUNIT").second; - for (auto& s : *storeys) { - auto storey = (IfcUtil::IfcBaseEntity*) s; - auto a = storey->get("Elevation"); - if (!a->isNull()) { - double elev = *a; - elev *= lu; - auto svg_name = nameElement(storey); + const double lu = file->getUnit("LENGTHUNIT").second; + for (auto& s : boost::make_iterator_range(storeys)) { + auto storey = s->as(); + auto a = storey->get("Elevation"); + if (!a->isNull()) { + double elev = *a; + elev *= lu; + auto svg_name = nameElement(storey); - gp_Pln elev_pln(gp_Ax3(gp_Pnt(0, 0, elev), gp::DZ(), gp::DX())); - //, pln->Position().XDirection())); - // auto ref_y = pln->Position().YDirection().XYZ().Dot(pln->Position().Location().XYZ()); + gp_Pln elev_pln(gp_Ax3(gp_Pnt(0, 0, elev), gp::DZ(), gp::DX())); + //, pln->Position().XDirection())); + // auto ref_y = pln->Position().YDirection().XYZ().Dot(pln->Position().Location().XYZ()); - double x0, y0, z0, x1, y1, z1; - bnd_.Get(x0, y0, z0, x1, y1, z1); + double x0, y0, z0, x1, y1, z1; + bnd_.Get(x0, y0, z0, x1, y1, z1); - // @todo this is a hack in order to get the auto elevations (which are 0.1 offset from - // the global bounding box) to include the storey height symbols. - x0 -= 0.2; - y0 -= 0.2; - z0 -= 0.2; + // @todo this is a hack in order to get the auto elevations (which are 0.1 offset from + // the global bounding box) to include the storey height symbols. + x0 -= 0.2; + y0 -= 0.2; + z0 -= 0.2; - x1 += 0.2; - y1 += 0.2; - z1 += 0.2; + x1 += 0.2; + y1 += 0.2; + z1 += 0.2; - const double shll = storey_height_line_length_.get_value_or(2.); + const double shll = storey_height_line_length_.get_value_or(2.); - BRepBuilderAPI_MakeFace mf(elev_pln, x0 - shll, x1 + shll, y0 - shll, y1 + shll); - gp_Trsf trsf; - TopoDS_Compound C; - BRep_Builder B; - B.MakeCompound(C); - B.Add(C, mf.Face()); - std::string name; - auto a2 = storey->get("Name"); - if (!a2->isNull()) { - name = (std::string) *a2; - } - write(geometry_data{ - C,{boost::none},trsf,storey,storey,elev,name,nameElement(storey) - }); + BRepBuilderAPI_MakeFace mf(elev_pln, x0 - shll, x1 + shll, y0 - shll, y1 + shll); + gp_Trsf trsf; + TopoDS_Compound C; + BRep_Builder B; + B.MakeCompound(C); + B.Add(C, mf.Face()); + std::string name; + auto a2 = storey->get("Name"); + if (!a2->isNull()) { + name = (std::string) *a2; } + write(geometry_data{ + C,{boost::none},trsf,storey,storey,elev,name,nameElement(storey) + }); } } } @@ -2303,30 +2301,28 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) { file = f; auto storeys = f->instances_by_type("IfcBuildingStorey"); - if (!storeys || storeys->size() == 0) { + if (std::distance(storeys.first, storeys.second)) { auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(file, geometry_settings_); std::vector to_derive_from; to_derive_from.push_back(f->schema()->declaration_by_name("IfcBuilding")); to_derive_from.push_back(f->schema()->declaration_by_name("IfcSite")); for (auto it = to_derive_from.begin(); it != to_derive_from.end(); ++it) { - aggregate_of_instance::ptr insts = f->instances_by_type(*it); - if (insts) { - for (auto jt = insts->begin(); jt != insts->end(); ++jt) { - IfcUtil::IfcBaseEntity* product = (IfcUtil::IfcBaseEntity*) *jt; - if (!product->get("ObjectPlacement")->isNull()) { - auto item = mapping->map(*product->get("ObjectPlacement")); - auto matrix = ifcopenshell::geometry::taxonomy::cast(item); - gp_Trsf trsf; - if (matrix) { - // @todo shouldn't this take into account configurable section height? - setSectionHeight(matrix->translation_part()(3) + 1.); + auto insts = f->instances_by_type(*it); + for (auto& product_ : boost::make_iterator_range(insts)) { + IfcUtil::IfcBaseEntity* product = product_->as(); + if (!product->get("ObjectPlacement")->isNull()) { + auto item = mapping->map(*product->get("ObjectPlacement")); + auto matrix = ifcopenshell::geometry::taxonomy::cast(item); + gp_Trsf trsf; + if (matrix) { + // @todo shouldn't this take into account configurable section height? + setSectionHeight(matrix->translation_part()(3) + 1.); #ifdef TAXONOMY_USE_NAKED_PTR - delete matrix; + delete matrix; #endif - Logger::Warning("No building storeys encountered, used for reference:", product); - return; - } + Logger::Warning("No building storeys encountered, used for reference:", product); + return; } } } @@ -2352,9 +2348,9 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) { section_data_.emplace(); auto storeys = file->instances_by_type("IfcBuildingStorey"); const double lu = file->getUnit("LENGTHUNIT").second; - if (storeys && storeys->size() > 0) { - for (auto& s : *storeys) { - auto attr_value = ((IfcUtil::IfcBaseEntity*)s)->get("Elevation"); + if (std::distance(storeys.first, storeys.second)) { + for (auto& s : boost::make_iterator_range(storeys)) { + auto attr_value = s->as()->get("Elevation"); if (!attr_value->isNull()) { double elev; try { @@ -2366,7 +2362,7 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) { if (!section_data_->empty()) { boost::get(section_data_->back()).next_elevation = elev * lu; } - section_data_->push_back(horizontal_plan{ (IfcUtil::IfcBaseEntity*)s, elev * lu, offset, std::numeric_limits::infinity() }); + section_data_->push_back(horizontal_plan{ s->as(), elev * lu, offset, std::numeric_limits::infinity() }); } } } else {