From d74b344a95d9e4926dad57bc753e573f9e052553 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 5 Jan 2021 16:29:14 +1100 Subject: [PATCH] WIP create aggregation module to manipulate aggregations. See #1222. --- .../blenderbim/bim/__init__.py | 4 ++ .../bim/module/aggregate/__init__.py | 17 +++++++ .../bim/module/aggregate/assign_object.py | 49 +++++++++++++++++++ .../blenderbim/bim/module/aggregate/data.py | 17 +++++++ .../bim/module/aggregate/operator.py | 47 ++++++++++++++++++ .../blenderbim/bim/module/aggregate/ui.py | 43 ++++++++++++++++ .../bim/module/attribute/operator.py | 4 +- .../blenderbim/bim/module/root/ui.py | 2 +- .../blenderbim/bim/module/spatial/ui.py | 2 + .../blenderbim/bim/operator.py | 10 ++-- src/ifcblenderexport/blenderbim/bim/prop.py | 2 + 11 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/aggregate/__init__.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/aggregate/assign_object.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/aggregate/data.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/aggregate/operator.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/aggregate/ui.py diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 8dfe823fee..475cbdb5a3 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -6,6 +6,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.aggregate as module_aggregate import blenderbim.bim.module.attribute as module_attribute import blenderbim.bim.module.bcf as module_bcf import blenderbim.bim.module.context as module_context @@ -311,6 +312,7 @@ if bpy is not None: ] classes.extend(module_root.classes) + classes.extend(module_aggregate.classes) classes.extend(module_attribute.classes) classes.extend(module_bcf.classes) classes.extend(module_context.classes) @@ -350,6 +352,7 @@ if bpy is not None: bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties) bpy.types.SCENE_PT_unit.append(ui.ifc_units) module_root.register() + module_aggregate.register() module_attribute.register() module_bcf.register() module_context.register() @@ -384,5 +387,6 @@ if bpy is not None: module_context.unregister() module_bcf.unregister() module_attribute.register() + module_aggregate.register() module_root.unregister() bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler) diff --git a/src/ifcblenderexport/blenderbim/bim/module/aggregate/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/aggregate/__init__.py new file mode 100644 index 0000000000..253c4f35c5 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/aggregate/__init__.py @@ -0,0 +1,17 @@ +import bpy +from . import ui, operator + +classes = ( + operator.AssignObject, + operator.EnableEditingAggregate, + operator.DisableEditingAggregate, + ui.BIM_PT_aggregate, +) + + +def register(): + pass + + +def unregister(): + pass diff --git a/src/ifcblenderexport/blenderbim/bim/module/aggregate/assign_object.py b/src/ifcblenderexport/blenderbim/bim/module/aggregate/assign_object.py new file mode 100644 index 0000000000..57e3c3b7c2 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/aggregate/assign_object.py @@ -0,0 +1,49 @@ +import ifcopenshell + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "product": None, + "relating_object": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + decomposes = None + if self.settings["product"].Decomposes: + decomposes = self.settings["product"].Decomposes[0] + + is_decomposed_by = None + for rel in self.settings["relating_object"].IsDecomposedBy: + if rel.is_a("IfcRelAggregates"): + is_decomposed_by = rel + break + + if decomposes and decomposes == is_decomposed_by: + return + + if decomposes: + related_objects = list(decomposes.RelatedObjects) + related_objects.remove(self.settings["product"]) + if related_objects: + decomposes.RelatedObjects = related_objects + else: + self.file.remove(decomposes) + + if is_decomposed_by: + related_objects = list(is_decomposed_by.RelatedObjects) + related_objects.append(self.settings["product"]) + is_decomposed_by.RelatedObjects = related_objects + else: + is_decomposed_by = self.file.create_entity( + "IfcRelAggregates", + **{ + "GlobalId": ifcopenshell.guid.new(), + # TODO "OwnerHistory": None + "RelatedObjects": [self.settings["product"]], + "RelatingObject": self.settings["relating_object"], + } + ) diff --git a/src/ifcblenderexport/blenderbim/bim/module/aggregate/data.py b/src/ifcblenderexport/blenderbim/bim/module/aggregate/data.py new file mode 100644 index 0000000000..883e6bd209 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/aggregate/data.py @@ -0,0 +1,17 @@ +from blenderbim.bim.ifc import IfcStore + + +class Data: + products = {} + + @classmethod + def load(cls, product_id): + file = IfcStore.get_file() + if not file: + return + product = file.by_id(product_id) + if product.Decomposes and product.Decomposes[0].is_a("IfcRelAggregates"): + obj = product.Decomposes[0].RelatingObject + cls.products[product_id] = {"type": obj.is_a(), "Name": obj.Name, "id": int(obj.id())} + else: + cls.products[product_id] = {"type": None, "Name": None, "id": None} diff --git a/src/ifcblenderexport/blenderbim/bim/module/aggregate/operator.py b/src/ifcblenderexport/blenderbim/bim/module/aggregate/operator.py new file mode 100644 index 0000000000..a00efeb095 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/aggregate/operator.py @@ -0,0 +1,47 @@ +import bpy +import blenderbim.bim.module.aggregate.assign_object as assign_object +from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.aggregate.data import Data + + +class AssignObject(bpy.types.Operator): + bl_idname = "bim.assign_object" + bl_label = "Assign Object" + + def execute(self, context): + self.file = IfcStore.get_file() + obj = bpy.context.active_object + props = obj.BIMObjectProperties + relating_object = props.relating_object + if not relating_object or not relating_object.BIMObjectProperties.ifc_definition_id: + return {"FINISHED"} + usecase = assign_object.Usecase( + self.file, + { + "product": self.file.by_id(props.ifc_definition_id), + "relating_object": self.file.by_id(relating_object.BIMObjectProperties.ifc_definition_id), + }, + ) + usecase.execute() + Data.load(props.ifc_definition_id) + bpy.ops.bim.disable_editing_aggregate() + return {"FINISHED"} + + +class EnableEditingAggregate(bpy.types.Operator): + bl_idname = "bim.enable_editing_aggregate" + bl_label = "Enable Editing Aggregate" + + def execute(self, context): + bpy.context.active_object.BIMObjectProperties.relating_object = None + bpy.context.active_object.BIMObjectProperties.is_editing_aggregate = True + return {"FINISHED"} + + +class DisableEditingAggregate(bpy.types.Operator): + bl_idname = "bim.disable_editing_aggregate" + bl_label = "Disable Editing Aggregate" + + def execute(self, context): + bpy.context.active_object.BIMObjectProperties.is_editing_aggregate = False + return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/aggregate/ui.py b/src/ifcblenderexport/blenderbim/bim/module/aggregate/ui.py new file mode 100644 index 0000000000..57beab793c --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/aggregate/ui.py @@ -0,0 +1,43 @@ +from bpy.types import Panel +from blenderbim.bim.module.aggregate.data import Data + + +class BIM_PT_aggregate(Panel): + bl_label = "IFC Aggregation" + bl_idname = "BIM_PT_aggregate" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + props = context.active_object.BIMObjectProperties + if not props.ifc_definition_id: + return False + if props.ifc_definition_id not in Data.products: + Data.load(props.ifc_definition_id) + if not Data.products[props.ifc_definition_id]: + return False + return True + + def draw(self, context): + props = context.active_object.BIMObjectProperties + if not props.ifc_definition_id: + return + if props.ifc_definition_id not in Data.products: + Data.load(props.ifc_definition_id) + + if props.is_editing_aggregate: + row = self.layout.row(align=True) + row.prop(props, "relating_object", text="") + row.operator("bim.assign_object", icon="CHECKMARK", text="") + row.operator("bim.disable_editing_aggregate", icon="X", text="") + else: + row = self.layout.row(align=True) + name = "{}/{}".format( + Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"] + ) + if name == "None/None": + name = "This object is not part of an aggregation" + row.label(text=name) + row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="") diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py index a9540e7903..4a3df216dd 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py @@ -59,14 +59,12 @@ class EditAttributes(bpy.types.Operator): blender_attribute = props.attributes.get(attribute["name"]) if not blender_attribute: continue - if blender_attribute.is_null: + if attribute["is_optional"] and blender_attribute.is_null: attributes[attribute["name"]] = None elif attribute["type"] == "string": attributes[attribute["name"]] = blender_attribute.string_value elif attribute["type"] == "list": values = blender_attribute.string_value[1:-1].split(", ") - print(attribute["name"]) - print(attribute["list_type"]) if attribute["list_type"] == "float": values = [float(v) for v in values] elif attribute["list_type"] == "integer": diff --git a/src/ifcblenderexport/blenderbim/bim/module/root/ui.py b/src/ifcblenderexport/blenderbim/bim/module/root/ui.py index 46e0721b52..c8a1ae56e8 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/root/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/root/ui.py @@ -15,10 +15,10 @@ class BIM_PT_class(Panel): if props.ifc_definition_id not in Data.products: Data.load(props.ifc_definition_id) if props.is_reassigning_class: - self.draw_class_dropdowns() row = self.layout.row(align=True) row.operator("bim.reassign_class", icon="CHECKMARK") row.operator("bim.disable_reassign_class", icon="X", text="") + self.draw_class_dropdowns() else: data = Data.products[props.ifc_definition_id] name = data["type"] diff --git a/src/ifcblenderexport/blenderbim/bim/module/spatial/ui.py b/src/ifcblenderexport/blenderbim/bim/module/spatial/ui.py index 8780fe47de..2a817d686b 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/spatial/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/spatial/ui.py @@ -31,5 +31,7 @@ class BIM_PT_spatial(Panel): name = "{}/{}".format( Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"] ) + if name == "None/None": + name = "This object is not spatially contained" row.label(text=name) row.operator("bim.assign_container", icon="FILE_REFRESH", text="") diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index 621e094eb6..cc7361b3a0 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -3957,14 +3957,10 @@ class InspectFromObject(bpy.types.Operator): bl_label = "Inspect From Object" def execute(self, context): - global_id = bpy.context.active_object.BIMObjectProperties.attributes.get("GlobalId") - if not global_id: + ifc_definition_id = bpy.context.active_object.BIMObjectProperties.ifc_definition_id + if not ifc_definition_id: return {"FINISHED"} - global_id = global_id.string_value - self.file = ifc.IfcStore.get_file() - element = self.file.by_guid(global_id) - if element: - bpy.ops.bim.inspect_from_step_id(step_id=element.id()) + bpy.ops.bim.inspect_from_step_id(step_id=ifc_definition_id) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index 5c791203c4..86f70fcc14 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -1461,6 +1461,8 @@ class BIMObjectProperties(PropertyGroup): global_ids: CollectionProperty(name="GlobalIds", type=GlobalId) attributes: CollectionProperty(name="Attributes", type=Attribute) is_editing_attributes: BoolProperty(name="Is Editing Attributes") + relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object) + is_editing_aggregate: BoolProperty(name="Is Editing Aggregate") relating_type: PointerProperty(name="Type Product", type=bpy.types.Object) relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object) psets: CollectionProperty(name="Psets", type=PsetQto)