From 7ff98d2918cbdda744b70db662cdffd01e6ca35a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 17 Oct 2023 18:18:58 +0500 Subject: [PATCH] Some UI to mark booleans as manual #3709 Currently it allows only to either set all object's booleans to be marked as manual or all of them marked as automatic, can't change some specific boolean. Short demo - https://imgur.com/a/bczv35N --- .../blenderbim/bim/module/void/__init__.py | 1 + .../blenderbim/bim/module/void/data.py | 33 +++++++++------- .../blenderbim/bim/module/void/operator.py | 39 +++++++++++++++++++ .../blenderbim/bim/module/void/ui.py | 14 +++++-- src/blenderbim/blenderbim/tool/model.py | 20 +++++----- 5 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/void/__init__.py b/src/blenderbim/blenderbim/bim/module/void/__init__.py index 0d1bdfcad2..30bdd5c9b9 100644 --- a/src/blenderbim/blenderbim/bim/module/void/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/void/__init__.py @@ -25,6 +25,7 @@ classes = ( operator.RemoveFilling, operator.RemoveOpening, operator.SelectDecomposition, + operator.BooleansMarkAsManual, prop.VoidProperties, ui.BIM_PT_voids, ui.BIM_PT_booleans, diff --git a/src/blenderbim/blenderbim/bim/module/void/data.py b/src/blenderbim/blenderbim/bim/module/void/data.py index 8fa4b3f1fc..24b74b8a9a 100644 --- a/src/blenderbim/blenderbim/bim/module/void/data.py +++ b/src/blenderbim/blenderbim/bim/module/void/data.py @@ -67,32 +67,39 @@ class VoidsData: return results - class BooleansData: data = {} is_loaded = False @classmethod def load(cls): - cls.data = {"total_booleans": cls.total_booleans()} + cls.data = {} + cls.data["total_booleans"] = cls.booleans() + cls.data["manual_booleans"] = cls.manual_booleans() cls.is_loaded = True @classmethod - def total_booleans(cls): + def booleans(cls): obj = bpy.context.active_object if ( not obj.data or not hasattr(obj.data, "BIMMeshProperties") or not obj.data.BIMMeshProperties.ifc_definition_id ): - return 0 + return [] + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - total_booleans = 0 - for item in representation.Items: - while True: - if item.is_a("IfcBooleanResult"): - total_booleans += 1 - item = item.FirstOperand - else: - break - return total_booleans + return tool.Model.get_booleans(representation=representation) + + @classmethod + def manual_booleans(cls): + obj = bpy.context.active_object + if ( + not obj.data + or not hasattr(obj.data, "BIMMeshProperties") + or not obj.data.BIMMeshProperties.ifc_definition_id + ): + return [] + + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) + return tool.Model.get_manual_booleans(tool.Ifc.get_entity(obj), representation=representation) diff --git a/src/blenderbim/blenderbim/bim/module/void/operator.py b/src/blenderbim/blenderbim/bim/module/void/operator.py index 3d4106aa1c..d7b2e37504 100644 --- a/src/blenderbim/blenderbim/bim/module/void/operator.py +++ b/src/blenderbim/blenderbim/bim/module/void/operator.py @@ -218,3 +218,42 @@ class SelectDecomposition(bpy.types.Operator): if subobj: subobj.select_set(True) return {"FINISHED"} + + +class BooleansMarkAsManual(bpy.types.Operator): + bl_idname = "bim.booleans_mark_as_manual" + bl_label = "Mark Booleans as Manual" + bl_options = {"REGISTER", "UNDO"} + bl_description = ( + "\nMark all booleans in object's current representations as manual.\n" + "Manual booleans will be preserved until they are removed explicitly" + ) + mark_as_manual: bpy.props.BoolProperty(name="Mark as Manual", default=True) + + @classmethod + def poll(cls, context): + obj = context.active_object + if ( + obj + and tool.Ifc.get_entity(obj) + and hasattr(obj.data, "BIMMeshProperties") + and obj.data.BIMMeshProperties.ifc_definition_id + ): + return True + cls.poll_message_set("Need to select IFC element with representation") + return False + + def execute(self, context): + obj = context.active_object + element = tool.Ifc.get_entity(obj) + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) + booleans = tool.Model.get_booleans(representation=representation) + + if self.mark_as_manual: + tool.Model.mark_manual_booleans(element, booleans) + else: + tool.Model.unmark_manual_booleans(element, booleans) + + self.report({"INFO"}, f"{len(booleans)} were marked as {'manual' if self.mark_as_manual else 'automatic'}") + blenderbim.bim.handler.refresh_ui_data() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/void/ui.py b/src/blenderbim/blenderbim/bim/module/void/ui.py index dd199ef5d0..805dd31e24 100644 --- a/src/blenderbim/blenderbim/bim/module/void/ui.py +++ b/src/blenderbim/blenderbim/bim/module/void/ui.py @@ -111,13 +111,21 @@ class BIM_PT_booleans(Panel): if context.active_object.data.BIMMeshProperties.ifc_definition_id: row = layout.row(align=True) - row.label(text=f"{BooleansData.data['total_booleans']} Booleans Found") - row.operator("bim.add_boolean", text="Apply Boolean", icon="ADD") + total_booleans = BooleansData.data["total_booleans"] + manual_booleans = BooleansData.data["manual_booleans"] + row.label(text=f"{len(total_booleans)} Booleans Found ({len(manual_booleans)} Manual)") + row.operator("bim.add_boolean", text="", icon="ADD") show_boolean_button = row.row(align=True) show_boolean_button.operator("bim.show_booleans", text="", icon="HIDE_OFF") - show_boolean_button.enabled = BooleansData.data["total_booleans"] > 0 + show_boolean_button.enabled = len(total_booleans) > 0 row.operator("bim.hide_booleans", text="", icon="HIDE_ON") + booleans_are_manual = len(manual_booleans) == len(total_booleans) + op = row.operator( + "bim.booleans_mark_as_manual", text="", icon="PINNED" if booleans_are_manual else "UNPINNED" + ) + op.mark_as_manual = not booleans_are_manual + elif context.active_object.data.BIMMeshProperties.ifc_boolean_id: upsteam_obj = context.active_object.data.BIMMeshProperties.obj upstream_obj_ifc_id = upsteam_obj.BIMObjectProperties.ifc_definition_id diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 67dd73d058..e58a13eaed 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -474,12 +474,13 @@ class Model(blenderbim.core.tool.Model): return {"thickness": thickness, "offset": offset, "direction_sense": direction_sense} @classmethod - def get_booleans(cls, element): - body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") - if not body: - return [] + def get_booleans(cls, element=None, representation=None): + if representation is None: + representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + if not representation: + return [] booleans = [] - items = list(body.Items) + items = list(representation.Items) while items: item = items.pop() if item.is_a("IfcBooleanResult"): @@ -488,14 +489,15 @@ class Model(blenderbim.core.tool.Model): return booleans @classmethod - def get_manual_booleans(cls, element): + def get_manual_booleans(cls, element, representation=None): pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") if not pset: return [] boolean_ids = json.loads(pset["Data"]) - body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") - if not body: - return [] + if representation is None: + representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + if not representation: + return [] booleans = [b for b in cls.get_booleans(element) if b.id() in boolean_ids] return booleans