diff --git a/src/ifcblenderexport/blenderbim/bim/export_ifc.py b/src/ifcblenderexport/blenderbim/bim/export_ifc.py index 3e8f1edf23..f43d4cfef6 100644 --- a/src/ifcblenderexport/blenderbim/bim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/export_ifc.py @@ -6,12 +6,13 @@ import datetime import os import zipfile import tempfile +import ifcopenshell +import ifcopenshell.util.pset from pathlib import Path from mathutils import Vector, Matrix from .helper import SIUnitHelper, get_representation_elements from . import schema from . import ifc -import ifcopenshell import addon_utils @@ -466,10 +467,10 @@ class IfcParser: return qto_names = self.get_applicable_qtos(ifc_class) for name in qto_names: - if name not in schema.ifc.qtos: + if name not in ifcopenshell.util.pset.qtos: continue has_automatic_value = False - props = schema.ifc.qtos[name]["HasPropertyTemplates"].keys() + props = ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys() guessed_values = {} for prop_name in props: value = self.qto_calculator.guess_quantity(prop_name, props, obj) @@ -1666,12 +1667,12 @@ class IfcExporter: ) def create_qto_properties(self, qto): - if qto["attributes"]["Name"] in schema.ifc.qtos: + if qto["attributes"]["Name"] in ifcopenshell.util.pset.qtos: return self.create_templated_qto_properties(qto) return self.create_custom_qto_properties(qto) def create_pset_properties(self, pset): - if pset["attributes"]["Name"] in schema.ifc.psets: + if pset["attributes"]["Name"] in ifcopenshell.util.pset.psets: return self.create_templated_pset_properties(pset) return self.create_custom_pset_properties(pset) @@ -1704,7 +1705,7 @@ class IfcExporter: def create_templated_pset_properties(self, pset): properties = [] - templates = schema.ifc.psets[pset["attributes"]["Name"]]["HasPropertyTemplates"] + templates = ifcopenshell.util.pset.psets[pset["attributes"]["Name"]]["HasPropertyTemplates"] for name, data in templates.items(): if name not in pset["raw"]: continue @@ -1731,7 +1732,7 @@ class IfcExporter: def create_templated_qto_properties(self, qto): properties = [] - templates = schema.ifc.qtos[qto["attributes"]["Name"]]["HasPropertyTemplates"] + templates = ifcopenshell.util.pset.qtos[qto["attributes"]["Name"]]["HasPropertyTemplates"] for name, data in templates.items(): if name not in qto["raw"]: continue @@ -3157,10 +3158,7 @@ class IfcExporter: ) def relate_spaces_to_boundary_elements(self): - for ( - relating_space_index, - relationships, - ) in self.ifc_parser.rel_space_boundaries.items(): + for (relating_space_index, relationships,) in self.ifc_parser.rel_space_boundaries.items(): for relationship in relationships: relationship["attributes"]["GlobalId"] = ifcopenshell.guid.new() relationship["attributes"]["RelatedBuildingElement"] = self.ifc_parser.products[ diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index 4056ac18f3..0fe458f506 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -3,6 +3,7 @@ import ifcopenshell.geom import ifcopenshell.util.geolocation import ifcopenshell.util.selector import ifcopenshell.util.element +import ifcopenshell.util.pset import bpy import bmesh import os @@ -206,8 +207,8 @@ class MaterialCreator: def add_pset(self, pset, obj): new_pset = obj.BIMMaterialProperties.psets.add() new_pset.name = pset.Name - if new_pset.name in schema.ifc.psets: - for prop_name in schema.ifc.psets[new_pset.name]["HasPropertyTemplates"].keys(): + if new_pset.name in ifcopenshell.util.pset.psets: + for prop_name in ifcopenshell.util.pset.psets[new_pset.name]["HasPropertyTemplates"].keys(): prop = new_pset.properties.add() prop.name = prop_name for prop in pset.Properties: @@ -1338,8 +1339,8 @@ class IfcImporter: def add_pset(self, pset, obj): new_pset = obj.BIMObjectProperties.psets.add() new_pset.name = pset.Name - if new_pset.name in schema.ifc.psets: - for prop_name in schema.ifc.psets[new_pset.name]["HasPropertyTemplates"].keys(): + if new_pset.name in ifcopenshell.util.pset.psets: + for prop_name in ifcopenshell.util.pset.psets[new_pset.name]["HasPropertyTemplates"].keys(): prop = new_pset.properties.add() prop.name = prop_name # Invalid IFC, but some vendors like Solidworks do this so we accomodate it @@ -1358,8 +1359,8 @@ class IfcImporter: def add_qto(self, qto, obj): new_qto = obj.BIMObjectProperties.qtos.add() new_qto.name = str(qto.Name) - if new_qto.name in schema.ifc.qtos: - for prop_name in schema.ifc.qtos[new_qto.name]["HasPropertyTemplates"].keys(): + if new_qto.name in ifcopenshell.util.pset.qtos: + for prop_name in ifcopenshell.util.pset.qtos[new_qto.name]["HasPropertyTemplates"].keys(): prop = new_qto.properties.add() prop.name = prop_name for prop in qto.Quantities: diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index ffb73219e1..4ddd4229dc 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -9,6 +9,7 @@ import subprocess import ifcopenshell import ifcopenshell.util.selector import ifcopenshell.util.geolocation +import ifcopenshell.util.pset import tempfile from . import export_ifc from . import import_ifc @@ -836,11 +837,11 @@ class AddQto(bpy.types.Operator): def execute(self, context): name = bpy.context.active_object.BIMObjectProperties.qto_name - if name not in schema.ifc.qtos: + if name not in ifcopenshell.util.pset.qtos: return {"FINISHED"} qto = bpy.context.active_object.BIMObjectProperties.qtos.add() qto.name = name - for prop_name in schema.ifc.qtos[name]["HasPropertyTemplates"].keys(): + for prop_name in ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys(): prop = qto.properties.add() prop.name = prop_name return {"FINISHED"} @@ -852,11 +853,11 @@ class AddPset(bpy.types.Operator): def execute(self, context): pset_name = bpy.context.active_object.BIMObjectProperties.pset_name - if pset_name not in schema.ifc.psets: + if pset_name not in ifcopenshell.util.pset.psets: return {"FINISHED"} pset = bpy.context.active_object.BIMObjectProperties.psets.add() pset.name = pset_name - for prop_name in schema.ifc.psets[pset_name]["HasPropertyTemplates"].keys(): + for prop_name in ifcopenshell.util.pset.psets[pset_name]["HasPropertyTemplates"].keys(): prop = pset.properties.add() prop.name = prop_name return {"FINISHED"} @@ -2729,7 +2730,6 @@ class CopyPropertyToSelection(bpy.types.Operator): def execute(self, context): self.applicable_psets_cache = {} - self.empty = ifcopenshell.file() for obj in bpy.context.selected_objects: if "/" not in obj.name: continue @@ -2740,7 +2740,7 @@ class CopyPropertyToSelection(bpy.types.Operator): continue pset = obj.BIMObjectProperties.psets.add() pset.name = self.pset_name - for template_prop_name in schema.ifc.psets[self.pset_name]["HasPropertyTemplates"].keys(): + for template_prop_name in ifcopenshell.util.pset.psets[self.pset_name]["HasPropertyTemplates"].keys(): prop = pset.properties.add() prop.name = template_prop_name prop = pset.properties.get(self.prop_name) @@ -2748,15 +2748,11 @@ class CopyPropertyToSelection(bpy.types.Operator): prop.string_value = self.prop_value return {"FINISHED"} - # TODO: move into util module. See bug #971 def get_applicable_psets(self, element_class): if element_class not in self.applicable_psets_cache: - element = self.empty.create_entity(element_class) - applicable_psets = [] - for ifc_class, pset_names in schema.ifc.applicable_psets.items(): - if element.is_a(ifc_class): - applicable_psets.extend(pset_names) - self.applicable_psets_cache[element_class] = applicable_psets + self.applicable_psets_cache[element_class] = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, element_class, is_pset=True + ) return self.applicable_psets_cache[element_class] @@ -3604,11 +3600,11 @@ class GuessQuantity(bpy.types.Operator): prop.string_value = str(round(quantity, 3)) def add_qto(self, obj, name): - if name not in schema.ifc.qtos: + if name not in ifcopenshell.util.pset.qtos: return qto = obj.BIMObjectProperties.qtos.add() qto.name = name - for prop_name in schema.ifc.qtos[name]["HasPropertyTemplates"].keys(): + for prop_name in ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys(): prop = qto.properties.add() prop.name = prop_name return qto diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index bdd76e8ff2..99d12d58d5 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -2,6 +2,7 @@ import json import os import csv import ifcopenshell +import ifcopenshell.util.pset from pathlib import Path from . import export_ifc from . import schema @@ -429,29 +430,25 @@ def refreshReferences(self, context): context.scene.BIMProperties.classification_references.root = "" -# TODO: move into util module. See bug #971 def getPsetNames(self, context): global psetnames_enum psetnames_enum.clear() if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements: - empty = ifcopenshell.file() - element = empty.create_entity(context.active_object.name.split("/")[0]) - for ifc_class, pset_names in schema.ifc.applicable_psets.items(): - if element.is_a(ifc_class): - psetnames_enum.extend([(p, p, "") for p in pset_names]) + pset_names = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_pset=True + ) + psetnames_enum.extend([(p, p, "") for p in pset_names]) return psetnames_enum -# TODO: move into util module. See bug #971 def getQtoNames(self, context): global qtonames_enum qtonames_enum.clear() if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements: - empty = ifcopenshell.file() - element = empty.create_entity(context.active_object.name.split("/")[0]) - for ifc_class, qto_names in schema.ifc.applicable_qtos.items(): - if element.is_a(ifc_class): - qtonames_enum.extend([(q, q, "") for q in qto_names]) + qto_names = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, context.active_object.name.split("/")[0], is_qto=True + ) + qtonames_enum.extend([(q, q, "") for q in qto_names]) return qtonames_enum diff --git a/src/ifcblenderexport/blenderbim/bim/schema.py b/src/ifcblenderexport/blenderbim/bim/schema.py index 4a34a042d3..958567ac09 100644 --- a/src/ifcblenderexport/blenderbim/bim/schema.py +++ b/src/ifcblenderexport/blenderbim/bim/schema.py @@ -1,6 +1,7 @@ import os import json import ifcopenshell +import ifcopenshell.util.pset import bpy from pathlib import Path @@ -30,14 +31,10 @@ class IfcSchema: self.property_files = [] property_paths = Path(os.path.join(self.data_dir, "pset")).glob("*.ifc") for path in property_paths: - self.property_files.append(ifcopenshell.open(path)) - self.property_files.append(ifcopenshell.open(os.path.join(self.schema_dir, "Pset_IFC4_ADD2.ifc"))) + ifcopenshell.util.pset.load_property_set_template(path) + ifcopenshell.util.pset.load_property_set_template(os.path.join(self.schema_dir, "Pset_IFC4_ADD2.ifc")) self.classification_files = {} - self.psets = {} - self.qtos = {} - self.applicable_psets = {} - self.applicable_qtos = {} self.classifications = {} self.load() @@ -50,17 +47,6 @@ class IfcSchema: with open(os.path.join(self.schema_dir, "ifc_types_IFC4.json")) as f: self.type_map = json.load(f) - for property_file in self.property_files: - for prop in property_file.by_type("IfcPropertySetTemplate"): - if prop.Name[0:4] == "Qto_": - self.qtos[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}} - entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot" - self.applicable_qtos.setdefault(entity, []).append(prop.Name) - else: - self.psets[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}} - entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot" - self.applicable_psets.setdefault(entity, []).append(prop.Name) - def load_classification(self, name, classification_index=None): if name not in self.classifications: if classification_index is not None: diff --git a/src/ifcopenshell-python/ifcopenshell/util/pset.py b/src/ifcopenshell-python/ifcopenshell/util/pset.py new file mode 100644 index 0000000000..5e5a541528 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/util/pset.py @@ -0,0 +1,39 @@ +import ifcopenshell + +property_set_template_files = [] +psets = {} +qtos = {} +applicable_psets = {} +applicable_qtos = {} + + +def load_property_set_template(path): + property_set_template_files.append(ifcopenshell.open(path)) + for prop in property_set_template_files[-1].by_type("IfcPropertySetTemplate"): + if prop.Name[0:4] == "Qto_": + qtos[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}} + entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot" + applicable_qtos.setdefault(entity, []).append(prop.Name) + else: + psets[prop.Name] = {"HasPropertyTemplates": {p.Name: p for p in prop.HasPropertyTemplates}} + entity = prop.ApplicableEntity if prop.ApplicableEntity else "IfcRoot" + applicable_psets.setdefault(entity, []).append(prop.Name) + + +def get_applicable_psetqtos(schema_version, ifc_class, is_pset=False, is_qto=False): + def is_a(entity, ifc_class): + if entity.name() == ifc_class: + return True + return is_a(entity.supertype(), ifc_class) if entity.supertype() else False + + results = [] + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_version) + entity = schema.declaration_by_name(ifc_class) + if is_pset: + search_items = applicable_psets.items() + elif is_qto: + search_items = applicable_qtos.items() + for ifc_class, pset_names in search_items: + if is_a(entity, ifc_class): + results.extend(pset_names) + return results