From 0c0ffce908edbde471ab546ccad6bba08c7828c5 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 4 Jan 2021 11:08:15 +1100 Subject: [PATCH] WIP assigning a class now ported over to partial editing and creates an IFC element. See #1222. --- .../blenderbim/bim/__init__.py | 7 +- .../module/geometry/assign_representation.py | 3 +- .../bim/module/geometry/assign_styles.py | 2 + .../bim/module/geometry/operator.py | 74 +++++++--- .../blenderbim/bim/module/root/__init__.py | 16 +++ .../bim/module/root/assign_class.py | 24 ++++ .../blenderbim/bim/module/root/operator.py | 135 ++++++++++++++++++ .../blenderbim/bim/operator.py | 95 ------------ 8 files changed, 234 insertions(+), 122 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/root/__init__.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/root/assign_class.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/root/operator.py diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 88266caaf4..e055545c12 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -5,6 +5,7 @@ bpy = sys.modules.get("bpy") if bpy is not None: import bpy + import blenderbim.bim.module.root as module_root import blenderbim.bim.module.bcf as module_bcf import blenderbim.bim.module.context as module_context import blenderbim.bim.module.covetool as module_covetool @@ -13,9 +14,6 @@ if bpy is not None: from . import ui, prop, operator classes = [ - operator.ReassignClass, - operator.AssignClass, - operator.UnassignClass, operator.SelectClass, operator.SelectType, operator.OpenUri, @@ -312,6 +310,7 @@ if bpy is not None: ui.BIM_ADDON_preferences, ] + classes.extend(module_root.classes) classes.extend(module_bcf.classes) classes.extend(module_context.classes) classes.extend(module_covetool.classes) @@ -348,6 +347,7 @@ if bpy is not None: bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties) bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties) bpy.types.SCENE_PT_unit.append(ui.ifc_units) + module_root.register() module_bcf.register() module_context.register() module_covetool.register() @@ -378,4 +378,5 @@ if bpy is not None: module_covetool.unregister() module_context.unregister() module_bcf.unregister() + module_root.unregister() bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler) diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_representation.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_representation.py index a6f724fe32..b01084c8ae 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_representation.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_representation.py @@ -12,6 +12,7 @@ class Usecase: definition = self.settings["product"].Representation if not definition: definition = self.file.createIfcProductDefinitionShape() - representations = list(definition.Representations) + self.settings["product"].Representation = definition + representations = list(definition.Representations) if definition.Representations else [] representations.append(self.settings["representation"]) definition.Representations = representations diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_styles.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_styles.py index 00abefd2c5..1728b44672 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_styles.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/assign_styles.py @@ -6,6 +6,8 @@ class Usecase: self.settings[key] = value def execute(self): + if not self.settings["styles"]: + return [] self.results = [] for element in self.file.traverse(self.settings["shape_representation"]): if not element.is_a("IfcShapeRepresentation"): diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py index 92af92695d..5c7f3eeafe 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py @@ -22,20 +22,35 @@ class AddRepresentation(bpy.types.Operator): self.context_id = bpy.context.scene.BIMProperties.contexts element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id) - usecase = add_representation.Usecase(self.file, { - "context": self.file.by_id(int(self.context_id)), - "geometry": obj.data, - "total_items": max(1, len(obj.material_slots)), - }) + usecase = add_representation.Usecase( + self.file, + { + "context": self.file.by_id(int(self.context_id)), + "geometry": obj.data, + "total_items": max(1, len(obj.material_slots)), + }, + ) result = usecase.execute() if not result: print("Failed to write shape representation") return {"FINISHED"} - usecase = assign_representation.Usecase(self.file, { - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "representation": result - }) + usecase = assign_styles.Usecase( + self.file, + { + "shape_representation": result, + "styles": [ + self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) + for s in obj.material_slots + if s.material + ], + }, + ) + usecase.execute() + + usecase = assign_representation.Usecase( + self.file, {"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), "representation": result} + ) usecase.execute() existing_mesh = obj.data @@ -116,27 +131,40 @@ class BakeParametricGeometry(bpy.types.Operator): obj = bpy.context.active_object self.file = IfcStore.get_file() - usecase = add_object_placement.Usecase(self.file, { - "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), - "matrix": np.array(obj.matrix_world) - }) + usecase = add_object_placement.Usecase( + self.file, + { + "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), + "matrix": np.array(obj.matrix_world), + }, + ) result = usecase.execute() element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id) - usecase = add_representation.Usecase(self.file, { - "context": element.ContextOfItems, - "geometry": obj.data, - "total_items": max(1, len(obj.material_slots)), - }) + usecase = add_representation.Usecase( + self.file, + { + "context": element.ContextOfItems, + "geometry": obj.data, + "total_items": max(1, len(obj.material_slots)), + }, + ) result = usecase.execute() if not result: print("Failed to write shape representation") return {"FINISHED"} - usecase = assign_styles.Usecase(self.file, { - "shape_representation": result, - "styles": [self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) for s in obj.material_slots if s.material] - }) + usecase = assign_styles.Usecase( + self.file, + { + "shape_representation": result, + "styles": [ + self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) + for s in obj.material_slots + if s.material + ], + }, + ) usecase.execute() for inverse in self.file.get_inverse(element): @@ -157,7 +185,7 @@ class UpdateIfcRepresentation(bpy.types.Operator): props = obj.data.BIMMeshProperties parameter = props.ifc_parameters[self.index] element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value - bpy.ops.bim.switch_representation(ifc_definition_id = props.ifc_definition_id) + bpy.ops.bim.switch_representation(ifc_definition_id=props.ifc_definition_id) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/root/__init__.py new file mode 100644 index 0000000000..82c2f2e4ce --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/root/__init__.py @@ -0,0 +1,16 @@ +import bpy +from . import operator + +classes = ( + operator.ReassignClass, + operator.AssignClass, + operator.UnassignClass, +) + + +def register(): + pass + + +def unregister(): + pass diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/assign_class.py b/src/ifcblenderexport/blenderbim/bim/module/root/assign_class.py new file mode 100644 index 0000000000..b42b7e1a59 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/root/assign_class.py @@ -0,0 +1,24 @@ +import ifcopenshell + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "ifc_class": "IfcBuildingElementProxy", + "predefined_type": None, + "name": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + element = self.file.create_entity(self.settings["ifc_class"], **{"GlobalId": ifcopenshell.guid.new()}) + element.Name = self.settings["name"] or None + if self.settings["predefined_type"] and hasattr(element, "PredefinedType"): + try: + element.PredefinedType = self.settings["predefined_type"] + except: + element.PredefinedType = "USERDEFINED" + element.ObjectType = self.settings["predefined_type"] + return element diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/operator.py b/src/ifcblenderexport/blenderbim/bim/module/root/operator.py new file mode 100644 index 0000000000..fa79cc317c --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/root/operator.py @@ -0,0 +1,135 @@ +import bpy +import numpy as np +import ifcopenshell +import blenderbim.bim.module.root.assign_class as assign_class +import blenderbim.bim.module.geometry.add_representation as add_representation +import blenderbim.bim.module.geometry.assign_styles as assign_styles +import blenderbim.bim.module.geometry.assign_representation as assign_representation +import blenderbim.bim.module.geometry.add_object_placement as add_object_placement +from blenderbim.bim.ifc import IfcStore + +# from blenderbim.bim.module.geometry.data import Data + + +class ReassignClass(bpy.types.Operator): + bl_idname = "bim.reassign_class" + bl_label = "Reassign IFC Class" + + def execute(self, context): + obj = bpy.context.active_object + ifc_class = obj.name.split("/")[0] + ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema) + bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True + ifc_products = [ + "IfcElement", + "IfcElementType", + "IfcSpatialElement", + "IfcGroup", + "IfcStructural", + "IfcPositioningElement", + "IfcContext", + "IfcAnnotation", + ] + for ifc_product in ifc_products: + if ifcopenshell.util.schema.is_a(ifc_schema.declaration_by_name(ifc_class), ifc_product): + bpy.context.scene.BIMProperties.ifc_product = ifc_product + 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" + object_name: bpy.props.StringProperty() + + def execute(self, context): + objects = [bpy.data.objects.get(self.object_name)] if self.object_name else bpy.context.selected_objects + self.file = IfcStore.get_file() + for obj in objects: + if obj.BIMObjectProperties.ifc_definition_id: + continue + usecase = assign_class.Usecase( + self.file, + { + "ifc_class": bpy.context.scene.BIMProperties.ifc_class, + "predefined_type": bpy.context.scene.BIMProperties.ifc_predefined_type, + "name": obj.name, + }, + ) + product = usecase.execute() + + usecase = add_object_placement.Usecase( + self.file, + { + "product": product, + "matrix": np.array(obj.matrix_world), + }, + ) + result = usecase.execute() + + if obj.data: + usecase = add_representation.Usecase( + self.file, + { + "context": self.file.by_id(int(bpy.context.scene.BIMProperties.contexts)), + "geometry": obj.data, + "total_items": max(1, len(obj.material_slots)), + }, + ) + representation = usecase.execute() + + usecase = assign_styles.Usecase( + self.file, + { + "shape_representation": representation, + "styles": [ + self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) + for s in obj.material_slots + if s.material + ], + }, + ) + usecase.execute() + + usecase = assign_representation.Usecase( + self.file, {"product": product, "representation": representation} + ) + usecase.execute() + + obj.name = "{}/{}".format(bpy.context.scene.BIMProperties.ifc_class, obj.name) + obj.BIMObjectProperties.ifc_definition_id = int(result.id()) + if bpy.context.scene.BIMProperties.ifc_product == "IfcElementType": + self.place_in_types_collection(obj) + return {"FINISHED"} + + def place_in_types_collection(self, obj): + for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]: + if not [c for c in project.children if "Types" in c.name]: + types = bpy.data.collections.new("Types") + project.collection.children.link(types) + for collection in [c for c in project.children if "Types" in c.name]: + for user_collection in obj.users_collection: + user_collection.objects.unlink(obj) + collection.collection.objects.link(obj) + break + break + + +class UnassignClass(bpy.types.Operator): + bl_idname = "bim.unassign_class" + bl_label = "Unassign IFC Class" + object_name: bpy.props.StringProperty() + + def execute(self, context): + if self.object_name: + objects = [bpy.data.objects.get(self.object_name)] + else: + objects = bpy.context.selected_objects + for obj in objects: + existing_class = None + if "/" in obj.name and obj.name[0:3] == "Ifc": + obj.name = "/".join(obj.name.split("/")[1:]) + return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index ad4cccdac5..b3b62e95d8 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -229,101 +229,6 @@ 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 - ifc_class = obj.name.split("/")[0] - ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema) - bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True - ifc_products = [ - "IfcElement", - "IfcElementType", - "IfcSpatialElement", - "IfcGroup", - "IfcStructural", - "IfcPositioningElement", - "IfcContext", - "IfcAnnotation", - ] - for ifc_product in ifc_products: - if ifcopenshell.util.schema.is_a(ifc_schema.declaration_by_name(ifc_class), ifc_product): - bpy.context.scene.BIMProperties.ifc_product = ifc_product - 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" - object_name: bpy.props.StringProperty() - - 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: - existing_class = None - if "/" in obj.name and obj.name[0:3] == "Ifc": - existing_class = obj.name.split("/")[0] - if existing_class: - obj.name = "{}/{}".format(bpy.context.scene.BIMProperties.ifc_class, obj.name.split("/")[1]) - else: - obj.name = "{}/{}".format(bpy.context.scene.BIMProperties.ifc_class, obj.name) - predefined_type_index = obj.BIMObjectProperties.attributes.find("PredefinedType") - if predefined_type_index >= 0: - obj.BIMObjectProperties.attributes.remove(predefined_type_index) - object_type_index = obj.BIMObjectProperties.attributes.find("ObjectType") - if object_type_index >= 0: - obj.BIMObjectProperties.attributes.remove(object_type_index) - if bpy.context.scene.BIMProperties.ifc_predefined_type: - predefined_type = obj.BIMObjectProperties.attributes.add() - predefined_type.name = "PredefinedType" - predefined_type.string_value = ( - bpy.context.scene.BIMProperties.ifc_predefined_type - ) # TODO: make it an enum - if bpy.context.scene.BIMProperties.ifc_predefined_type == "USERDEFINED": - object_type = obj.BIMObjectProperties.attributes.add() - object_type.name = "ObjectType" - object_type.string_value = bpy.context.scene.BIMProperties.ifc_userdefined_type - if bpy.context.scene.BIMProperties.ifc_product == "IfcElementType": - for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]: - if not [c for c in project.children if "Types" in c.name]: - types = bpy.data.collections.new("Types") - project.collection.children.link(types) - for collection in [c for c in project.children if "Types" in c.name]: - for user_collection in obj.users_collection: - user_collection.objects.unlink(obj) - collection.collection.objects.link(obj) - break - break - return {"FINISHED"} - - -class UnassignClass(bpy.types.Operator): - bl_idname = "bim.unassign_class" - bl_label = "Unassign IFC Class" - object_name: bpy.props.StringProperty() - - def execute(self, context): - if self.object_name: - objects = [bpy.data.objects.get(self.object_name)] - else: - objects = bpy.context.selected_objects - for obj in objects: - existing_class = None - if "/" in obj.name and obj.name[0:3] == "Ifc": - obj.name = "/".join(obj.name.split("/")[1:]) - return {"FINISHED"} - - class SelectClass(bpy.types.Operator): bl_idname = "bim.select_class" bl_label = "Select IFC Class"