From a37e124d8f1b99075c5ec08c942a2113d489b538 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 27 Jan 2021 19:00:35 +1100 Subject: [PATCH] You can now duplicate or linked duplicate objects in IFC. Neat. See #1222. --- .../bim/module/geometry/map_representation.py | 5 ++- .../bim/module/geometry/operator.py | 18 +++++---- .../blenderbim/bim/module/root/__init__.py | 1 + .../blenderbim/bim/module/root/copy_class.py | 37 +++++++++++++++++++ .../blenderbim/bim/module/root/operator.py | 30 +++++++++++++++ .../blenderbim/bim/module/root/ui.py | 1 + 6 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/root/copy_class.py diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/map_representation.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/map_representation.py index 479986e290..73dace0ebf 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/map_representation.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/map_representation.py @@ -10,12 +10,13 @@ class Usecase: self.settings[key] = value def execute(self): - self.get_mapping_source() - self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)) self.x_axis = self.file.createIfcDirection((1.0, 0.0, 0.0)) self.y_axis = self.file.createIfcDirection((0.0, 1.0, 0.0)) self.z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0)) + + self.get_mapping_source() + mapping_target = self.file.createIfcCartesianTransformationOperator3D( self.x_axis, self.y_axis, self.zero, 1, self.z_axis ) diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py index 9257e1626c..acba9268f9 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/operator.py @@ -179,20 +179,23 @@ class MapRepresentation(bpy.types.Operator): bl_idname = "bim.map_representation" bl_label = "Map Representation" obj: bpy.props.StringProperty() - target_obj: bpy.props.StringProperty() + obj_data: bpy.props.StringProperty() def execute(self, context): objs = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects - target_obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object - objs = [o for o in objs if o != target_obj] + obj_data = bpy.data.meshes.get(self.obj_data) if self.obj_data else bpy.context.active_object.data + objs = [o for o in objs if o.data != obj_data] self.file = IfcStore.get_file() for obj in objs: bpy.ops.bim.edit_object_placement(obj=obj.name) product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) - old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id) - target_representation = self.file.by_id(target_obj.data.BIMMeshProperties.ifc_definition_id) - obj.data = target_obj.data + if obj.data.BIMMeshProperties.ifc_definition_id: + old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id) + else: + old_representation = None + target_representation = self.file.by_id(obj_data.BIMMeshProperties.ifc_definition_id) + obj.data = obj_data result = map_representation.Usecase( self.file, { @@ -201,7 +204,8 @@ class MapRepresentation(bpy.types.Operator): }, ).execute() assign_representation.Usecase(self.file, {"product": product, "representation": result}).execute() - bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id()) + if old_representation: + bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id()) Data.load(obj.BIMObjectProperties.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 index ccad13bad2..0d41a5eba9 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/root/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/root/__init__.py @@ -8,6 +8,7 @@ classes = ( operator.AssignClass, operator.UnassignClass, operator.UnlinkObject, + operator.CopyClass, prop.BIMRootProperties, ui.BIM_PT_class, ) diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/copy_class.py b/src/ifcblenderexport/blenderbim/bim/module/root/copy_class.py new file mode 100644 index 0000000000..04ba418578 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/root/copy_class.py @@ -0,0 +1,37 @@ +import ifcopenshell + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = {"product": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + result = self.file.create_entity(self.settings["product"].is_a()) + self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema) + self.copy_attributes(self.settings["product"], result) + for inverse in self.file.get_inverse(self.settings["product"]): + for i, value in enumerate(inverse): + if value == self.settings["product"]: + new_inverse = self.file.create_entity(inverse.is_a()) + self.copy_attributes(inverse, new_inverse) + new_inverse[i] = result + elif isinstance(value, (tuple, list)) and self.settings["product"] in value: + new_value = list(value) + new_value.append(result) + inverse[i] = new_value + if result.is_a("IfcProduct"): + result.Representation = None + elif result.is_a("IfcTypeProduct"): + result.RepresentationMaps = None + return result + + def copy_attributes(self, from_element, to_element): + declaration = self.schema.declaration_by_name(from_element.is_a()) + for attribute in declaration.all_attributes(): + if attribute.name() == "GlobalId": + setattr(to_element, attribute.name(), ifcopenshell.guid.new()) + else: + setattr(to_element, attribute.name(), getattr(from_element, attribute.name())) diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/operator.py b/src/ifcblenderexport/blenderbim/bim/module/root/operator.py index de6dbc66c9..16e6f0bf05 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/root/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/root/operator.py @@ -5,6 +5,7 @@ import ifcopenshell.util.schema import blenderbim.bim.module.root.create_product as create_product import blenderbim.bim.module.root.remove_product as remove_product import blenderbim.bim.module.root.reassign_class as reassign_class +import blenderbim.bim.module.root.copy_class as copy_class from blenderbim.bim.module.owner.api import create_owner_history from blenderbim.bim.ifc import IfcStore @@ -203,3 +204,32 @@ class UnlinkObject(bpy.types.Operator): if obj.BIMObjectProperties.ifc_definition_id: obj.BIMObjectProperties.ifc_definition_id = 0 return {"FINISHED"} + + +class CopyClass(bpy.types.Operator): + bl_idname = "bim.copy_class" + bl_label = "Copy Class" + obj: bpy.props.StringProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + if self.obj: + objects = [bpy.data.objects.get(self.obj)] + else: + objects = bpy.context.selected_objects + for obj in objects: + if not obj.BIMObjectProperties.ifc_definition_id: + continue + result = copy_class.Usecase(self.file, { + "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) + }).execute() + obj.BIMObjectProperties.ifc_definition_id = result.id() + if obj.data.users == 1: + bpy.ops.bim.add_representation(obj=obj.name) + else: + obj_data = obj.data.name + temporary_mesh = bpy.data.meshes.new("Temporary Mesh") + obj.data = temporary_mesh + bpy.ops.bim.map_representation(obj=obj.name, obj_data=obj_data) + bpy.data.meshes.remove(temporary_mesh) + return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/ui.py b/src/ifcblenderexport/blenderbim/bim/module/root/ui.py index 968aacc96b..def15004cb 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/root/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/root/ui.py @@ -34,6 +34,7 @@ class BIM_PT_class(Panel): name += "[{}]".format(data["PredefinedType"]) row = self.layout.row(align=True) row.label(text=name) + row.operator("bim.copy_class", icon="DUPLICATE", text="").obj = context.active_object.name row.operator("bim.unlink_object", icon="UNLINKED", text="").obj = context.active_object.name row.operator("bim.enable_reassign_class", icon="GREASEPENCIL", text="") row.operator("bim.unassign_class", icon="X", text="").obj = context.active_object.name