From f50ae2faa5f216c50e33a0145f8d2fc2eb25e5df Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 16 Jan 2025 22:58:17 +1100 Subject: [PATCH] See #5972. You can now load a hierarchy to visualise the boolean result tree. --- src/bonsai/bonsai/bim/module/void/__init__.py | 9 +++- src/bonsai/bonsai/bim/module/void/data.py | 4 +- src/bonsai/bonsai/bim/module/void/operator.py | 46 +++++++++++++++++++ src/bonsai/bonsai/bim/module/void/prop.py | 15 +++++- src/bonsai/bonsai/bim/module/void/ui.py | 37 +++++++++++---- 5 files changed, 98 insertions(+), 13 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/void/__init__.py b/src/bonsai/bonsai/bim/module/void/__init__.py index 68995f1b2e..f89d514ad0 100644 --- a/src/bonsai/bonsai/bim/module/void/__init__.py +++ b/src/bonsai/bonsai/bim/module/void/__init__.py @@ -22,19 +22,26 @@ from . import ui, prop, operator classes = ( operator.AddFilling, operator.AddOpening, + operator.BooleansMarkAsManual, + operator.DisableEditingBooleans, + operator.EnableEditingBooleans, operator.RemoveFilling, operator.RemoveOpening, operator.SelectDecomposition, - operator.BooleansMarkAsManual, + prop.Boolean, prop.VoidProperties, + prop.BIMBooleanProperties, ui.BIM_PT_voids, ui.BIM_PT_booleans, + ui.BIM_UL_booleans, ) def register(): bpy.types.Scene.VoidProperties = bpy.props.PointerProperty(type=prop.VoidProperties) + bpy.types.Scene.BIMBooleanProperties = bpy.props.PointerProperty(type=prop.BIMBooleanProperties) def unregister(): del bpy.types.Scene.VoidProperties + del bpy.types.Scene.BIMBooleanProperties diff --git a/src/bonsai/bonsai/bim/module/void/data.py b/src/bonsai/bonsai/bim/module/void/data.py index 11f58f51c9..4bbb3da1d3 100644 --- a/src/bonsai/bonsai/bim/module/void/data.py +++ b/src/bonsai/bonsai/bim/module/void/data.py @@ -80,7 +80,7 @@ class BooleansData: @classmethod def booleans(cls): - obj = bpy.context.active_object + obj = bpy.context.scene.BIMGeometryProperties.representation_obj or bpy.context.active_object if ( not obj.data or not hasattr(obj.data, "BIMMeshProperties") @@ -93,7 +93,7 @@ class BooleansData: @classmethod def manual_booleans(cls): - obj = bpy.context.active_object + obj = bpy.context.scene.BIMGeometryProperties.representation_obj or bpy.context.active_object if ( not obj.data or not hasattr(obj.data, "BIMMeshProperties") diff --git a/src/bonsai/bonsai/bim/module/void/operator.py b/src/bonsai/bonsai/bim/module/void/operator.py index 5a1558a839..2631ea697e 100644 --- a/src/bonsai/bonsai/bim/module/void/operator.py +++ b/src/bonsai/bonsai/bim/module/void/operator.py @@ -295,4 +295,50 @@ class BooleansMarkAsManual(bpy.types.Operator, tool.Ifc.Operator): {"INFO"}, f"{len(booleans)} booleans were marked as {'manual' if self.mark_as_manual else 'automatic'}" ) bonsai.bim.handler.refresh_ui_data() + + +class EnableEditingBooleans(bpy.types.Operator): + bl_idname = "bim.enable_editing_booleans" + bl_label = "Enable Editing Booleans" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Manage a hierarchy of boolean operations" + + @classmethod + def poll(cls, context): + return bpy.context.scene.BIMGeometryProperties.representation_obj + + def execute(self, context): + props = context.scene.BIMBooleanProperties + rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj + representation = tool.Geometry.get_active_representation(rep_obj) + representation = ifcopenshell.util.representation.resolve_representation(representation) + props.booleans.clear() + + def load_boolean(item, level=0): + new = props.booleans.add() + new.name = f"{item.is_a()}/{item.id()}" + new.ifc_definition_id = item.id() + new.level = level + if item.is_a("IfcBooleanResult"): + new.name += f"/{item.Operator}" + new.operator = item.Operator + load_boolean(item.FirstOperand, level + 1) + load_boolean(item.SecondOperand, level + 1) + + for item in representation.Items: + if item.is_a("IfcBooleanResult"): + load_boolean(item) + + props.is_editing = True + return {"FINISHED"} + + +class DisableEditingBooleans(bpy.types.Operator): + bl_idname = "bim.disable_editing_booleans" + bl_label = "Disable Editing Booleans" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMBooleanProperties + props.is_editing = False return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/void/prop.py b/src/bonsai/bonsai/bim/module/void/prop.py index 94953be98b..8721febd5f 100644 --- a/src/bonsai/bonsai/bim/module/void/prop.py +++ b/src/bonsai/bonsai/bim/module/void/prop.py @@ -18,8 +18,21 @@ import bpy from bpy.types import PropertyGroup -from bpy.props import PointerProperty +from bpy.props import PointerProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty + + +class Boolean(PropertyGroup): + name: StringProperty(name="Name") + operator: StringProperty(name="Operator") + ifc_definition_id: IntProperty(name="IFC Definition ID") + level: IntProperty(name="Level") class VoidProperties(PropertyGroup): desired_opening: PointerProperty(name="Desired Opening To Fill", type=bpy.types.Object) + + +class BIMBooleanProperties(PropertyGroup): + is_editing: BoolProperty(name="Is Editing", default=False) + booleans: CollectionProperty(name="Booleans", type=Boolean) + active_boolean_index: IntProperty(name="Active Boolean Index") diff --git a/src/bonsai/bonsai/bim/module/void/ui.py b/src/bonsai/bonsai/bim/module/void/ui.py index 67590316f0..c5e5f45ada 100644 --- a/src/bonsai/bonsai/bim/module/void/ui.py +++ b/src/bonsai/bonsai/bim/module/void/ui.py @@ -18,7 +18,7 @@ import bpy import bonsai.tool as tool -from bpy.types import Panel +from bpy.types import Panel, UIList from bonsai.bim.module.void.data import BooleansData, VoidsData @@ -110,19 +110,18 @@ class BIM_PT_booleans(Panel): if not context.active_object.data: return layout = self.layout - props = context.active_object.data.BIMMeshProperties + props = context.scene.BIMBooleanProperties if context.active_object.data.BIMMeshProperties.ifc_definition_id: row = layout.row(align=True) 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)") + if props.is_editing: + row.operator("bim.disable_editing_booleans", icon="CANCEL", text="") + else: + row.operator("bim.enable_editing_booleans", icon="IMPORT", text="") 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 = 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" @@ -136,6 +135,26 @@ class BIM_PT_booleans(Panel): row.label(text=upsteam_obj.name) row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = upstream_obj_ifc_id + if not props.is_editing: + return + + self.layout.template_list("BIM_UL_booleans", "", props, "booleans", props, "active_boolean_index") + + +class BIM_UL_booleans(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + if item.operator == "DIFFERENCE": + icon = "SELECT_DIFFERENCE" + elif item.operator == "INTERSECTION": + icon = "SELECT_INTERSECT" + elif item.operator == "UNION": + icon = "SELECT_EXTEND" + elif "IfcHalfSpaceSolid" in item.name: + icon = "NORMALS_FACE" + else: + icon = "MESH_DATA" row = layout.row(align=True) - row.operator("bim.hide_booleans", text="Hide Boolean") - row.operator("bim.remove_booleans", text="Remove Boolean", icon="X") + for i in range(0, item.level): + row.label(text="", icon="BLANK1") + row.label(text=item.name, icon=icon)