See #5972. You can now load a hierarchy to visualise the boolean result tree.

This commit is contained in:
Dion Moult
2025-01-16 22:58:17 +11:00
parent bd739f8bfd
commit f50ae2faa5
5 changed files with 98 additions and 13 deletions
@@ -22,19 +22,26 @@ from . import ui, prop, operator
classes = ( classes = (
operator.AddFilling, operator.AddFilling,
operator.AddOpening, operator.AddOpening,
operator.BooleansMarkAsManual,
operator.DisableEditingBooleans,
operator.EnableEditingBooleans,
operator.RemoveFilling, operator.RemoveFilling,
operator.RemoveOpening, operator.RemoveOpening,
operator.SelectDecomposition, operator.SelectDecomposition,
operator.BooleansMarkAsManual, prop.Boolean,
prop.VoidProperties, prop.VoidProperties,
prop.BIMBooleanProperties,
ui.BIM_PT_voids, ui.BIM_PT_voids,
ui.BIM_PT_booleans, ui.BIM_PT_booleans,
ui.BIM_UL_booleans,
) )
def register(): def register():
bpy.types.Scene.VoidProperties = bpy.props.PointerProperty(type=prop.VoidProperties) bpy.types.Scene.VoidProperties = bpy.props.PointerProperty(type=prop.VoidProperties)
bpy.types.Scene.BIMBooleanProperties = bpy.props.PointerProperty(type=prop.BIMBooleanProperties)
def unregister(): def unregister():
del bpy.types.Scene.VoidProperties del bpy.types.Scene.VoidProperties
del bpy.types.Scene.BIMBooleanProperties
+2 -2
View File
@@ -80,7 +80,7 @@ class BooleansData:
@classmethod @classmethod
def booleans(cls): def booleans(cls):
obj = bpy.context.active_object obj = bpy.context.scene.BIMGeometryProperties.representation_obj or bpy.context.active_object
if ( if (
not obj.data not obj.data
or not hasattr(obj.data, "BIMMeshProperties") or not hasattr(obj.data, "BIMMeshProperties")
@@ -93,7 +93,7 @@ class BooleansData:
@classmethod @classmethod
def manual_booleans(cls): def manual_booleans(cls):
obj = bpy.context.active_object obj = bpy.context.scene.BIMGeometryProperties.representation_obj or bpy.context.active_object
if ( if (
not obj.data not obj.data
or not hasattr(obj.data, "BIMMeshProperties") or not hasattr(obj.data, "BIMMeshProperties")
@@ -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'}" {"INFO"}, f"{len(booleans)} booleans were marked as {'manual' if self.mark_as_manual else 'automatic'}"
) )
bonsai.bim.handler.refresh_ui_data() 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"} return {"FINISHED"}
+14 -1
View File
@@ -18,8 +18,21 @@
import bpy import bpy
from bpy.types import PropertyGroup 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): class VoidProperties(PropertyGroup):
desired_opening: PointerProperty(name="Desired Opening To Fill", type=bpy.types.Object) 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")
+28 -9
View File
@@ -18,7 +18,7 @@
import bpy import bpy
import bonsai.tool as tool 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 from bonsai.bim.module.void.data import BooleansData, VoidsData
@@ -110,19 +110,18 @@ class BIM_PT_booleans(Panel):
if not context.active_object.data: if not context.active_object.data:
return return
layout = self.layout layout = self.layout
props = context.active_object.data.BIMMeshProperties props = context.scene.BIMBooleanProperties
if context.active_object.data.BIMMeshProperties.ifc_definition_id: if context.active_object.data.BIMMeshProperties.ifc_definition_id:
row = layout.row(align=True) row = layout.row(align=True)
total_booleans = BooleansData.data["total_booleans"] total_booleans = BooleansData.data["total_booleans"]
manual_booleans = BooleansData.data["manual_booleans"] manual_booleans = BooleansData.data["manual_booleans"]
row.label(text=f"{len(total_booleans)} Booleans Found ({len(manual_booleans)} Manual)") 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") 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) booleans_are_manual = len(manual_booleans) == len(total_booleans)
op = row.operator( op = row.operator(
"bim.booleans_mark_as_manual", text="", icon="PINNED" if booleans_are_manual else "UNPINNED" "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.label(text=upsteam_obj.name)
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = upstream_obj_ifc_id 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 = layout.row(align=True)
row.operator("bim.hide_booleans", text="Hide Boolean") for i in range(0, item.level):
row.operator("bim.remove_booleans", text="Remove Boolean", icon="X") row.label(text="", icon="BLANK1")
row.label(text=item.name, icon=icon)