mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +00:00
#1376 remove_deep_batched()
This commit is contained in:
@@ -179,6 +179,14 @@ class file(object):
|
||||
:rtype: None
|
||||
"""
|
||||
return self.wrapped_data.remove(inst.wrapped_data)
|
||||
|
||||
def batch(self):
|
||||
"""Low-level mechanism to speed up deletion of large subgraphs"""
|
||||
return self.wrapped_data.batch()
|
||||
|
||||
def unbatch(self):
|
||||
"""Low-level mechanism to speed up deletion of large subgraphs"""
|
||||
return self.wrapped_data.unbatch()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self[id] for id in self.wrapped_data.entity_names())
|
||||
|
||||
@@ -126,6 +126,13 @@ def remove_deep(ifc_file, element):
|
||||
ifc_file.remove(ref)
|
||||
|
||||
|
||||
def remove_deep_batched(ifc_file, element):
|
||||
# @todo maybe some sort of try-finally mechanism.
|
||||
ifc_file.batch()
|
||||
remove_deep(ifc_file, element)
|
||||
ifc_file.unbatch()
|
||||
|
||||
|
||||
def get_representation(element, context, subcontext=None, target_view=None):
|
||||
if element.is_a("IfcProduct") and element.Representation:
|
||||
for r in element.Representation.Representations:
|
||||
|
||||
@@ -131,6 +131,11 @@ private:
|
||||
void initialize_(IfcParse::IfcSpfStream* f);
|
||||
|
||||
void build_inverses_(IfcUtil::IfcBaseClass*);
|
||||
|
||||
std::set<int> batch_deletion_ids_;
|
||||
bool batch_mode_ = false;
|
||||
void process_deletion_();
|
||||
|
||||
public:
|
||||
IfcParse::IfcSpfLexer* tokens;
|
||||
IfcParse::IfcSpfStream* stream;
|
||||
@@ -226,6 +231,9 @@ public:
|
||||
IfcUtil::IfcBaseClass* addEntity(IfcUtil::IfcBaseClass* entity);
|
||||
void addEntities(IfcEntityList::ptr es);
|
||||
|
||||
void batch() { batch_mode_ = true; }
|
||||
void unbatch() { process_deletion_(); batch_mode_ = false; }
|
||||
|
||||
/// Removes entity instance from file and unsets references.
|
||||
///
|
||||
/// Attention when running removeEntity inside a loop over a list of entities to be removed.
|
||||
|
||||
+203
-153
@@ -1710,73 +1710,76 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) {
|
||||
}
|
||||
}
|
||||
|
||||
// The mapping by entity type is updated.
|
||||
const IfcParse::declaration* ty = &new_entity->declaration();
|
||||
// The mapping by entity type is updated.
|
||||
const IfcParse::declaration* ty = &new_entity->declaration();
|
||||
|
||||
if (ty->as_entity()) {
|
||||
IfcEntityList::ptr insts = instances_by_type_excl_subtypes(ty);
|
||||
if (!insts) {
|
||||
insts = IfcEntityList::ptr(new IfcEntityList());
|
||||
bytype_excl[ty] = insts;
|
||||
}
|
||||
insts->push(new_entity);
|
||||
if (ty->as_entity()) {
|
||||
IfcEntityList::ptr insts = instances_by_type_excl_subtypes(ty);
|
||||
if (!insts) {
|
||||
insts = IfcEntityList::ptr(new IfcEntityList());
|
||||
bytype_excl[ty] = insts;
|
||||
}
|
||||
insts->push(new_entity);
|
||||
}
|
||||
|
||||
for (; ty->as_entity();) {
|
||||
IfcEntityList::ptr insts = instances_by_type(ty);
|
||||
if (!insts) {
|
||||
insts = IfcEntityList::ptr(new IfcEntityList());
|
||||
bytype[ty] = insts;
|
||||
}
|
||||
insts->push(new_entity);
|
||||
|
||||
const IfcParse::declaration* pt = ty->as_entity()->supertype();
|
||||
if (pt) {
|
||||
ty = pt;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ty->as_entity()) {
|
||||
int new_id = -1;
|
||||
if (!new_entity->data().file) {
|
||||
// For newly created entities ensure a valid ENTITY_INSTANCE_NAME is set
|
||||
new_entity->data().file = this;
|
||||
new_id = new_entity->data().set_id();
|
||||
}
|
||||
else {
|
||||
new_id = new_entity->data().id();
|
||||
}
|
||||
|
||||
for (; ty->as_entity();) {
|
||||
IfcEntityList::ptr insts = instances_by_type(ty);
|
||||
if (!insts) {
|
||||
insts = IfcEntityList::ptr(new IfcEntityList());
|
||||
bytype[ty] = insts;
|
||||
}
|
||||
insts->push(new_entity);
|
||||
|
||||
const IfcParse::declaration* pt = ty->as_entity()->supertype();
|
||||
if (pt) {
|
||||
ty = pt;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (byid.find(new_id) != byid.end()) {
|
||||
// This should not happen
|
||||
std::stringstream ss;
|
||||
ss << "Overwriting entity with id " << new_id;
|
||||
Logger::Message(Logger::LOG_WARNING, ss.str());
|
||||
}
|
||||
|
||||
if (ty->as_entity()) {
|
||||
int new_id = -1;
|
||||
if (!new_entity->data().file) {
|
||||
// For newly created entities ensure a valid ENTITY_INSTANCE_NAME is set
|
||||
new_entity->data().file = this;
|
||||
new_id = new_entity->data().set_id();
|
||||
} else {
|
||||
new_id = new_entity->data().id();
|
||||
}
|
||||
// The mapping by entity instance name is updated.
|
||||
byid[new_id] = new_entity;
|
||||
}
|
||||
|
||||
if (byid.find(new_id) != byid.end()) {
|
||||
// This should not happen
|
||||
std::stringstream ss;
|
||||
ss << "Overwriting entity with id " << new_id;
|
||||
Logger::Message(Logger::LOG_WARNING, ss.str());
|
||||
}
|
||||
if (parsing_complete_ && ty->as_entity()) {
|
||||
build_inverses_(new_entity);
|
||||
}
|
||||
|
||||
// The mapping by entity instance name is updated.
|
||||
byid[new_id] = new_entity;
|
||||
}
|
||||
|
||||
if (parsing_complete_ && ty->as_entity()) {
|
||||
build_inverses_(new_entity);
|
||||
}
|
||||
|
||||
return new_entity;
|
||||
return new_entity;
|
||||
}
|
||||
|
||||
void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
|
||||
const unsigned id = entity->data().id();
|
||||
|
||||
IfcUtil::IfcBaseClass* file_entity = instance_by_id(id);
|
||||
|
||||
|
||||
// Attention when running removeEntity inside a loop over a list of entities to be removed.
|
||||
// This invalidates the iterator. A workaround is to reverse the loop:
|
||||
// boost::shared_ptr<IfcEntityList> entities = ...;
|
||||
// for (auto it = entities->end() - 1; it >= entities->begin(); --it) {
|
||||
// IfcUtil::IfcBaseClass *const inst = *it;
|
||||
// model->removeEntity(inst);
|
||||
// }
|
||||
// IfcUtil::IfcBaseClass *const inst = *it;
|
||||
// model->removeEntity(inst);
|
||||
// }
|
||||
|
||||
// TODO: Create a set of weak relations. Inverse relations that do not dictate an
|
||||
// instance to be retained. For example: when deleting an IfcRepresentation, the
|
||||
@@ -1789,133 +1792,180 @@ void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
|
||||
throw IfcParse::IfcException("Instance not part of this file");
|
||||
}
|
||||
|
||||
IfcEntityList::ptr references = instances_by_reference(id);
|
||||
batch_deletion_ids_.insert(id);
|
||||
|
||||
// Alter entity instances with INVERSE relations to the entity being
|
||||
// deleted. This is necessary to maintain a valid IFC file, because
|
||||
// dangling references to it's entities name should be removed. At this
|
||||
// moment, inversely related instances affected by the removal of the
|
||||
// entity being deleted are not deleted themselves.
|
||||
if (references) {
|
||||
for (IfcEntityList::it iit = references->begin(); iit != references->end(); ++iit) {
|
||||
IfcUtil::IfcBaseEntity* related_instance = (IfcUtil::IfcBaseEntity*) *iit;
|
||||
if (!batch_mode_) {
|
||||
process_deletion_();
|
||||
}
|
||||
}
|
||||
|
||||
void IfcFile::process_deletion_() {
|
||||
|
||||
for (unsigned i = 0; i < related_instance->data().getArgumentCount(); ++i) {
|
||||
Argument* attr = related_instance->data().getArgument(i);
|
||||
if (attr->isNull()) continue;
|
||||
for (auto& id : batch_deletion_ids_) {
|
||||
auto entity = instance_by_id(id);
|
||||
|
||||
IfcUtil::ArgumentType attr_type = attr->type();
|
||||
switch(attr_type) {
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* instance_attribute = *attr;
|
||||
if (instance_attribute == entity) {
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(boost::blank());
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
|
||||
IfcEntityList::ptr instance_list = *attr;
|
||||
if (instance_list->contains(entity)) {
|
||||
instance_list->remove(entity);
|
||||
IfcEntityList::ptr references = instances_by_reference(id);
|
||||
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(instance_list);
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: {
|
||||
IfcEntityListList::ptr instance_list_list = *attr;
|
||||
if (instance_list_list->contains(entity)) {
|
||||
IfcEntityListList::ptr new_list(new IfcEntityListList);
|
||||
for (IfcEntityListList::outer_it it = instance_list_list->begin(); it != instance_list_list->end(); ++it) {
|
||||
std::vector<IfcUtil::IfcBaseClass*> instances = *it;
|
||||
std::vector<IfcUtil::IfcBaseClass*>::iterator jt;
|
||||
while ((jt = std::find(instances.begin(), instances.end(), entity)) != instances.end()) {
|
||||
instances.erase(jt);
|
||||
// Alter entity instances with INVERSE relations to the entity being
|
||||
// deleted. This is necessary to maintain a valid IFC file, because
|
||||
// dangling references to it's entities name should be removed. At this
|
||||
// moment, inversely related instances affected by the removal of the
|
||||
// entity being deleted are not deleted themselves.
|
||||
if (references) {
|
||||
for (IfcEntityList::it iit = references->begin(); iit != references->end(); ++iit) {
|
||||
IfcUtil::IfcBaseEntity* related_instance = (IfcUtil::IfcBaseEntity*) *iit;
|
||||
|
||||
if (std::find(batch_deletion_ids_.begin(), batch_deletion_ids_.end(), related_instance->data().id()) != batch_deletion_ids_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < related_instance->data().getArgumentCount(); ++i) {
|
||||
Argument* attr = related_instance->data().getArgument(i);
|
||||
if (attr->isNull()) continue;
|
||||
|
||||
IfcUtil::ArgumentType attr_type = attr->type();
|
||||
switch (attr_type) {
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* instance_attribute = *attr;
|
||||
if (instance_attribute == entity) {
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(boost::blank());
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
|
||||
IfcEntityList::ptr instance_list = *attr;
|
||||
if (instance_list->contains(entity)) {
|
||||
instance_list->remove(entity);
|
||||
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(instance_list);
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: {
|
||||
IfcEntityListList::ptr instance_list_list = *attr;
|
||||
if (instance_list_list->contains(entity)) {
|
||||
IfcEntityListList::ptr new_list(new IfcEntityListList);
|
||||
for (IfcEntityListList::outer_it it = instance_list_list->begin(); it != instance_list_list->end(); ++it) {
|
||||
std::vector<IfcUtil::IfcBaseClass*> instances = *it;
|
||||
std::vector<IfcUtil::IfcBaseClass*>::iterator jt;
|
||||
while ((jt = std::find(instances.begin(), instances.end(), entity)) != instances.end()) {
|
||||
instances.erase(jt);
|
||||
}
|
||||
new_list->push(instances);
|
||||
}
|
||||
new_list->push(instances);
|
||||
}
|
||||
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(new_list);
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
default: break;
|
||||
IfcWrite::IfcWriteArgument* copy = new IfcWrite::IfcWriteArgument();
|
||||
copy->set(new_list);
|
||||
related_instance->data().setArgument(i, copy);
|
||||
} }
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
byref.erase(byref.find(id));
|
||||
}
|
||||
|
||||
IfcEntityList::ptr entity_attributes = traverse(entity, 1);
|
||||
for (IfcEntityList::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) {
|
||||
IfcUtil::IfcBaseClass* entity_attribute = *it;
|
||||
if (entity_attribute == entity) continue;
|
||||
const unsigned int name = entity_attribute->data().id();
|
||||
// Do not update inverses for simple types (which have id()==0 in IfcOpenShell).
|
||||
if (name != 0) {
|
||||
entities_by_ref_t::iterator byref_it = byref.find(name);
|
||||
if (byref_it != byref.end()) {
|
||||
std::vector<unsigned>& ids = byref_it->second;
|
||||
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
|
||||
if (!batch_mode_) {
|
||||
byref.erase(id);
|
||||
|
||||
// This is based on traversal which needs instances to still be contained in the map.
|
||||
// another option would be to keep byid intact for the remainder of this loop
|
||||
IfcEntityList::ptr entity_attributes = traverse(entity, 1);
|
||||
for (IfcEntityList::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) {
|
||||
IfcUtil::IfcBaseClass* entity_attribute = *it;
|
||||
if (entity_attribute == entity) continue;
|
||||
const unsigned int name = entity_attribute->data().id();
|
||||
// Do not update inverses for simple types (which have id()==0 in IfcOpenShell).
|
||||
if (name != 0) {
|
||||
entities_by_ref_t::iterator byref_it = byref.find(name);
|
||||
if (byref_it != byref.end()) {
|
||||
std::vector<unsigned>& ids = byref_it->second;
|
||||
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
|
||||
}
|
||||
by_ref_cached_.erase(name);
|
||||
}
|
||||
}
|
||||
by_ref_cached_.erase(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->declaration().is(*ifcroot_type_)) {
|
||||
const std::string global_id = *entity->data().getArgument(0);
|
||||
auto it = byguid.find(global_id);
|
||||
if (it != byguid.end()) {
|
||||
byguid.erase(it);
|
||||
} else {
|
||||
Logger::Warning("GlobalId on rooted instance not encountered in map");
|
||||
if (entity->declaration().is(*ifcroot_type_)) {
|
||||
const std::string global_id = *entity->data().getArgument(0);
|
||||
auto it = byguid.find(global_id);
|
||||
if (it != byguid.end()) {
|
||||
byguid.erase(it);
|
||||
}
|
||||
else {
|
||||
Logger::Warning("GlobalId on rooted instance not encountered in map");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byid.erase(byid.find(id));
|
||||
|
||||
const IfcParse::declaration* ty = &entity->declaration();
|
||||
byid.erase(byid.find(id));
|
||||
|
||||
{
|
||||
IfcEntityList::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);
|
||||
}
|
||||
}
|
||||
const IfcParse::declaration* ty = &entity->declaration();
|
||||
|
||||
for (;;) {
|
||||
IfcEntityList::ptr instances_of_same_type = instances_by_type(ty);
|
||||
if (instances_of_same_type) {
|
||||
{
|
||||
IfcEntityList::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.erase(ty);
|
||||
if (instances_of_same_type->size() == 0) {
|
||||
bytype_excl.erase(ty);
|
||||
}
|
||||
}
|
||||
|
||||
const IfcParse::declaration* pt = ty->as_entity()->supertype();
|
||||
if (pt) {
|
||||
ty = pt;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
IfcEntityList::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);
|
||||
}
|
||||
|
||||
// This entity_file_map remains obviously flawed, but until we have proper lookup by value, or another mechanism,
|
||||
// to prevent duplicate definitions with usage of add() we have to keep it. This might be a good moment to clear it.
|
||||
for (auto it = entity_file_map.begin(); it != entity_file_map.end();) {
|
||||
if (it->second == entity) {
|
||||
it = entity_file_map.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
const IfcParse::declaration* pt = ty->as_entity()->supertype();
|
||||
if (pt) {
|
||||
ty = pt;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This entity_file_map remains obviously flawed, but until we have proper lookup by value, or another mechanism,
|
||||
// to prevent duplicate definitions with usage of add() we have to keep it. This might be a good moment to clear it.
|
||||
for (auto it = entity_file_map.begin(); it != entity_file_map.end();) {
|
||||
if (it->second == entity) {
|
||||
it = entity_file_map.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
delete entity;
|
||||
|
||||
}
|
||||
|
||||
delete entity;
|
||||
if (batch_mode_) {
|
||||
for (auto it = byref.begin(); it != byref.end();) {
|
||||
bool do_delete = batch_deletion_ids_.find(it->first) != batch_deletion_ids_.end();
|
||||
if (!do_delete) {
|
||||
it->second.erase(std::remove_if(it->second.begin(), it->second.end(), [this](int x) {
|
||||
return batch_deletion_ids_.find(x) != batch_deletion_ids_.end();
|
||||
}), it->second.end());
|
||||
do_delete = it->second.empty();
|
||||
}
|
||||
if (do_delete) {
|
||||
it = byref.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
by_ref_cached_.clear();
|
||||
|
||||
batch_deletion_ids_.clear();
|
||||
}
|
||||
|
||||
IfcEntityList::ptr IfcFile::instances_by_type(const IfcParse::declaration* t) {
|
||||
|
||||
Reference in New Issue
Block a user