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 = (
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
+2 -2
View File
@@ -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")
@@ -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"}
+14 -1
View File
@@ -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")
+28 -9
View File
@@ -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)