diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..fab942429d --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,8 @@ +c14f5eeca042232025797990396232e4feab53e7 +d6881e833da0b6278313870d37d42ca6ece8686a +892be5444c5bd01a601c99243ac8d3e7b2f0e779 +d169a964dd7e9ab5c98b35a82bcbc76dc6229a7a +4a6ec11f6f84a3d1fed2ee9b2f85d783ac3daa9d +2c9d6a47f4d24a923069775206bf734feeb14830 +8f1743ed64a2a52c201a1ef5e312b4d3a7a922cf +ad7f030344c405e10cfa7d5cf06d82c121eeb825 diff --git a/src/ifcblenderexport/Makefile b/src/ifcblenderexport/Makefile index f3af203803..197ddb8e4d 100644 --- a/src/ifcblenderexport/Makefile +++ b/src/ifcblenderexport/Makefile @@ -38,10 +38,12 @@ endif cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f geolocation.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f selector.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f unit.py + cd dist/blenderbim/libs/site/packages/ifcopenshell/util && rm -f pset.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/element.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/geolocation.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/selector.py cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/unit.py + cd dist/blenderbim/libs/site/packages/ifcopenshell/util && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/pset.py # Provides Python OCC functionality for cutting IFC geometry for construction documentation mkdir dist/working diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index e9dc8800fb..0c05eb6154 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -21,6 +21,7 @@ if bpy is not None: from .module.model import opening as model_opening classes = ( + operator.ReassignClass, operator.AssignClass, operator.UnassignClass, operator.SelectClass, 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 5a21e09955..0355a5fddf 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 @@ -213,6 +214,20 @@ class SelectPset(bpy.types.Operator): return {"FINISHED"} +class ReassignClass(bpy.types.Operator): + bl_idname = "bim.reassign_class" + bl_label = "Reassign IFC Class" + + def execute(self, context): + obj = bpy.context.active_object + bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True + bpy.context.scene.BIMProperties.ifc_class = obj.name.split("/")[0] + predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType") + if predefined_type: + bpy.context.scene.BIMProperties.ifc_predefined_type = predefined_type.string_value + return {"FINISHED"} + + class AssignClass(bpy.types.Operator): bl_idname = "bim.assign_class" bl_label = "Assign IFC Class" @@ -221,6 +236,7 @@ class AssignClass(bpy.types.Operator): def execute(self, context): if self.object_name: objects = [bpy.data.objects.get(self.object_name)] + objects[0].BIMObjectProperties.is_reassigning_class = False else: objects = bpy.context.selected_objects for obj in objects: @@ -835,32 +851,60 @@ class AddQto(bpy.types.Operator): bl_label = "Add Qto" def execute(self, context): + self.applicable_qtos_cache = {} 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(): - prop = qto.properties.add() - prop.name = prop_name + for obj in bpy.context.selected_objects: + if "/" not in obj.name or obj.BIMObjectProperties.qtos.find(name) != -1: + continue + applicable_qtos = self.get_applicable_qtos(obj.name.split("/")[0]) + if name not in applicable_qtos: + continue + qto = obj.BIMObjectProperties.qtos.add() + qto.name = name + for prop_name in ifcopenshell.util.pset.qtos[name]["HasPropertyTemplates"].keys(): + prop = qto.properties.add() + prop.name = prop_name return {"FINISHED"} + def get_applicable_qtos(self, ifc_class): + if ifc_class not in self.applicable_qtos_cache: + self.applicable_qtos_cache[ifc_class] = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, ifc_class, is_qto=True + ) + return self.applicable_qtos_cache[ifc_class] + class AddPset(bpy.types.Operator): bl_idname = "bim.add_pset" bl_label = "Add Pset" def execute(self, context): - pset_name = bpy.context.active_object.BIMObjectProperties.pset_name - if pset_name not in schema.ifc.psets: + self.applicable_psets_cache = {} + name = bpy.context.active_object.BIMObjectProperties.pset_name + if 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(): - prop = pset.properties.add() - prop.name = prop_name + for obj in bpy.context.selected_objects: + if "/" not in obj.name or obj.BIMObjectProperties.psets.find(name) != -1: + continue + applicable_psets = self.get_applicable_psets(obj.name.split("/")[0]) + if name not in applicable_psets: + continue + pset = obj.BIMObjectProperties.psets.add() + pset.name = name + for prop_name in ifcopenshell.util.pset.psets[name]["HasPropertyTemplates"].keys(): + prop = pset.properties.add() + prop.name = prop_name return {"FINISHED"} + def get_applicable_psets(self, ifc_class): + if ifc_class not in self.applicable_psets_cache: + self.applicable_psets_cache[ifc_class] = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, ifc_class, is_pset=True + ) + return self.applicable_psets_cache[ifc_class] + class RemovePset(bpy.types.Operator): bl_idname = "bim.remove_pset" @@ -868,7 +912,13 @@ class RemovePset(bpy.types.Operator): pset_index: bpy.props.IntProperty() def execute(self, context): - bpy.context.active_object.BIMObjectProperties.psets.remove(self.pset_index) + name = bpy.context.active_object.BIMObjectProperties.psets[self.pset_index].name + for obj in bpy.context.selected_objects: + if "/" not in obj.name: + continue + index = obj.BIMObjectProperties.psets.find(name) + if index != -1: + obj.BIMObjectProperties.psets.remove(index) return {"FINISHED"} @@ -878,7 +928,13 @@ class RemoveQto(bpy.types.Operator): index: bpy.props.IntProperty() def execute(self, context): - bpy.context.active_object.BIMObjectProperties.qtos.remove(self.index) + name = bpy.context.active_object.BIMObjectProperties.qtos[self.index].name + for obj in bpy.context.selected_objects: + if "/" not in obj.name: + continue + index = obj.BIMObjectProperties.qtos.find(name) + if index != -1: + obj.BIMObjectProperties.qtos.remove(index) return {"FINISHED"} @@ -1221,9 +1277,18 @@ class AddAttribute(bpy.types.Operator): bl_label = "Add Attribute" def execute(self, context): - if bpy.context.active_object.BIMObjectProperties.applicable_attributes: - attribute = bpy.context.active_object.BIMObjectProperties.attributes.add() - attribute.name = bpy.context.active_object.BIMObjectProperties.applicable_attributes + if not bpy.context.active_object.BIMObjectProperties.applicable_attributes: + return {"FINISHED"} + name = bpy.context.active_object.BIMObjectProperties.applicable_attributes + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema) + for obj in bpy.context.selected_objects: + if "/" not in obj.name or obj.BIMObjectProperties.attributes.find(name) != -1: + continue + entity = schema.declaration_by_name(obj.name.split("/")[0]) + if name not in [a.name() for a in entity.all_attributes()]: + continue + attribute = obj.BIMObjectProperties.attributes.add() + attribute.name = name if attribute.name == "GlobalId": attribute.string_value = ifcopenshell.guid.new() return {"FINISHED"} @@ -1246,7 +1311,13 @@ class RemoveAttribute(bpy.types.Operator): attribute_index: bpy.props.IntProperty() def execute(self, context): - bpy.context.active_object.BIMObjectProperties.attributes.remove(self.attribute_index) + name = bpy.context.active_object.BIMObjectProperties.attributes[self.attribute_index].name + for obj in bpy.context.selected_objects: + if "/" not in obj.name: + continue + index = obj.BIMObjectProperties.attributes.find(name) + if index != -1: + obj.BIMObjectProperties.attributes.remove(index) return {"FINISHED"} @@ -2714,7 +2785,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 @@ -2725,7 +2795,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) @@ -2733,16 +2803,12 @@ 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 - return self.applicable_psets_cache[element_class] + def get_applicable_psets(self, ifc_class): + if ifc_class not in self.applicable_psets_cache: + self.applicable_psets_cache[ifc_class] = ifcopenshell.util.pset.get_applicable_psetqtos( + bpy.context.scene.BIMProperties.export_schema, ifc_class, is_pset=True + ) + return self.applicable_psets_cache[ifc_class] class BIM_OT_ChangeClassificationLevel(bpy.types.Operator): @@ -3589,11 +3655,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 4298a7abea..8d30d61d18 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 @@ -449,29 +450,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 @@ -1625,6 +1622,7 @@ class BoundaryCondition(PropertyGroup): class BIMObjectProperties(PropertyGroup): + is_reassigning_class: BoolProperty(name="Is Reassigning Class") global_ids: CollectionProperty(name="GlobalIds", type=GlobalId) attributes: CollectionProperty(name="Attributes", type=Attribute) relating_type: PointerProperty(name="Type Product", type=bpy.types.Object) 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/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index ccd4dd2be9..57f834eaac 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -21,24 +21,28 @@ class BIM_PT_object(Panel): props = context.active_object.BIMObjectProperties bim_properties = context.scene.BIMProperties - row = layout.row() - row.prop(bim_properties, "ifc_product") - row = layout.row() - row.prop(bim_properties, "ifc_class") - if bim_properties.ifc_predefined_type: + if props.is_reassigning_class or "/" not in context.active_object.name: row = layout.row() - row.prop(bim_properties, "ifc_predefined_type") - if bim_properties.ifc_predefined_type == "USERDEFINED": + row.prop(bim_properties, "ifc_product") row = layout.row() - row.prop(bim_properties, "ifc_userdefined_type") - row = layout.row(align=True) - if "Ifc" not in context.active_object.name: - op = row.operator("bim.assign_class") + row.prop(bim_properties, "ifc_class") + if bim_properties.ifc_predefined_type: + row = layout.row() + row.prop(bim_properties, "ifc_predefined_type") + if bim_properties.ifc_predefined_type == "USERDEFINED": + row = layout.row() + row.prop(bim_properties, "ifc_userdefined_type") + row = layout.row(align=True) + if "Ifc" not in context.active_object.name: + op = row.operator("bim.assign_class") + else: + op = row.operator("bim.assign_class") + op.object_name = context.active_object.name + op = row.operator("bim.unassign_class", icon="X", text="") + op.object_name = context.active_object.name else: - op = row.operator("bim.assign_class", text="Reassign IFC Class") - op.object_name = context.active_object.name - op = row.operator("bim.unassign_class", icon="X", text="") - op.object_name = context.active_object.name + row = layout.row() + row.operator("bim.reassign_class", text="Reassign IFC Class") if "Ifc" not in context.active_object.name: return @@ -1894,10 +1898,13 @@ class BIM_UL_classifications(bpy.types.UIList): class BIM_ADDON_preferences(bpy.types.AddonPreferences): bl_idname = "blenderbim" - svg2pdf_command: StringProperty(name="SVG to PDF Command") - svg2dxf_command: StringProperty(name="SVG to DXF Command") - svg_command: StringProperty(name="SVG Command") - pdf_command: StringProperty(name="PDF Command") + svg2pdf_command: StringProperty(name="SVG to PDF Command", description="E.g. [['inkscape', svg, '-o', pdf]]") + svg2dxf_command: StringProperty( + name="SVG to DXF Command", + description="E.g. [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]", + ) + svg_command: StringProperty(name="SVG Command", description="E.g. [['firefox-bin', path]]") + pdf_command: StringProperty(name="PDF Command", description="E.g. [['firefox-bin', path]]") def draw(self, context): layout = self.layout 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 diff --git a/src/ifcpatch/recipes/ExtractElements.py b/src/ifcpatch/recipes/ExtractElements.py new file mode 100644 index 0000000000..c42ee0ddef --- /dev/null +++ b/src/ifcpatch/recipes/ExtractElements.py @@ -0,0 +1,58 @@ +import ifcopenshell +import ifcopenshell.util.selector + + +class Patcher: + def __init__(self, src, file, logger, args=None): + self.src = src + self.file = file + self.logger = logger + self.args = args + + def patch(self): + self.contained_ins = {} + self.aggregates = {} + self.new = ifcopenshell.file(schema=self.file.wrapped_data.schema) + self.owner_history = None + for owner_history in self.file.by_type("IfcOwnerHistory"): + self.owner_history = self.new.add(owner_history) + break + selector = ifcopenshell.util.selector.Selector() + for element in selector.parse(self.file, self.args[0]): + self.add_element(element) + self.create_spatial_tree() + self.file = self.new + + def add_element(self, element): + new_element = self.new.add(element) + for rel in element.ContainedInStructure: + spatial_element = rel.RelatingStructure + new_spatial_element = self.new.add(spatial_element) + self.contained_ins.setdefault(spatial_element.GlobalId, set()).add(new_element) + self.add_spatial_tree(spatial_element, new_spatial_element) + + def add_spatial_tree(self, spatial_element, new_spatial_element): + for rel in spatial_element.Decomposes: + new = self.new.add(rel.RelatingObject) + self.aggregates.setdefault(rel.RelatingObject.GlobalId, set()).add(new_spatial_element) + self.add_spatial_tree(rel.RelatingObject, new) + + def create_spatial_tree(self): + for relating_structure, related_elements in self.contained_ins.items(): + self.new.createIfcRelContainedInSpatialStructure( + ifcopenshell.guid.new(), + self.owner_history, + None, + None, + list(related_elements), + self.new.by_guid(relating_structure), + ) + for relating_object, related_objects in self.aggregates.items(): + self.new.createIfcRelAggregates( + ifcopenshell.guid.new(), + self.owner_history, + None, + None, + self.new.by_guid(relating_object), + list(related_objects), + )