diff --git a/src/bonsai/bonsai/bim/module/model/opening.py b/src/bonsai/bonsai/bim/module/model/opening.py index 56936c950d..8a8a7c0904 100644 --- a/src/bonsai/bonsai/bim/module/model/opening.py +++ b/src/bonsai/bonsai/bim/module/model/opening.py @@ -36,6 +36,7 @@ import ifcopenshell.util.unit import bonsai.tool as tool import bonsai.core.geometry import bonsai.bim.import_ifc as import_ifc +from collections import defaultdict from bonsai.bim.ifc import IfcStore from math import pi, radians from mathutils import Vector, Matrix @@ -737,27 +738,43 @@ class HideBooleans(Operator, tool.Ifc.Operator): set_active_obj, set_selected_objs = None, None boolean_objs: list[bpy.types.Object] selected_objects = tool.Blender.get_selected_objects() - selected_booleans_objs = [obj for obj in selected_objects if tool.Model.is_boolean_obj(obj)] + selected_booleans_objs = [ + obj for obj in selected_objects if tool.Model.get_tracked_opening_type(obj) == "BOOLEAN" + ] + + # Hide currently selected booleans, otherwise hide all booleans. if selected_booleans_objs: boolean_objs = selected_booleans_objs if (active_object := context.active_object) in selected_booleans_objs: active_obj_source = active_object else: active_obj_source = selected_booleans_objs[0] - set_active_obj = active_obj_source.data.BIMMeshProperties.obj - set_selected_objs = [tool.Model.get_booleaned_obj(obj) for obj in boolean_objs] + set_active_obj = tool.Model.get_booleaned_obj(active_obj_source) + assert set_active_obj + set_selected_objs = [ + bool_obj for obj in selected_booleans_objs if (bool_obj := tool.Model.get_booleaned_obj(obj)) + ] else: props = bpy.context.scene.BIMModelProperties - boolean_objs = [obj for o in props.openings if (obj := o.obj)] + boolean_objs = [obj for o in props.openings if (obj := o.obj) and o.name == "BOOLEAN"] + + objects_to_remove: set[bpy.types.Object] = set() + booleans_to_add: dict[bpy.types.Object, list[bpy.types.Object]] = defaultdict(list) - objects_to_remove = set() for obj in boolean_objs: - ifc_boolean_id = obj.data.BIMMeshProperties.ifc_boolean_id - boolean = tool.Ifc.get_entity_by_id(ifc_boolean_id) + main_obj = tool.Model.get_booleaned_obj(obj) + if not main_obj: + continue # Update boolean transform. - if boolean: - main_obj = cast(bpy.types.Object, obj.data.BIMMeshProperties.obj) + if main_obj: + ifc_boolean_id = obj.data.BIMMeshProperties.ifc_boolean_id + boolean = tool.Ifc.get_entity_by_id(ifc_boolean_id) + + if boolean is None: + booleans_to_add[main_obj].append(obj) + continue + if boolean.is_a("IfcHalfSpaceSolid"): surface = boolean.BaseSurface @@ -781,6 +798,11 @@ class HideBooleans(Operator, tool.Ifc.Operator): tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True) tool.Model.purge_scene_openings() + if booleans_to_add: + for obj, boolean_objs in booleans_to_add.items(): + with context.temp_override(selected_objects=boolean_objs + [obj]): + bpy.ops.bim.add_boolean() + if set_active_obj and set_selected_objs is not None: tool.Blender.set_objects_selection(context, set_active_obj, set_selected_objs) return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/void/ui.py b/src/bonsai/bonsai/bim/module/void/ui.py index 05e2eb4f7f..67590316f0 100644 --- a/src/bonsai/bonsai/bim/module/void/ui.py +++ b/src/bonsai/bonsai/bim/module/void/ui.py @@ -104,6 +104,9 @@ class BIM_PT_booleans(Panel): if not BooleansData.is_loaded: BooleansData.load() + obj = context.active_object + assert obj + if not context.active_object.data: return layout = self.layout @@ -126,8 +129,7 @@ class BIM_PT_booleans(Panel): ) 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 + elif upsteam_obj := obj.data.BIMMeshProperties.obj: upstream_obj_ifc_id = upsteam_obj.BIMObjectProperties.ifc_definition_id row = layout.row(align=True) diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 4573f72e0e..a5048f057d 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -2007,12 +2007,12 @@ class Model(bonsai.core.tool.Model): return {"ifc_file": tmp, "curves": curves} @classmethod - def is_boolean_obj(cls, obj: bpy.types.Object) -> bool: - return obj.type == "MESH" and obj.data.BIMMeshProperties.ifc_boolean_id - - @classmethod - def get_booleaned_obj(cls, boolean_obj: bpy.types.Object) -> bpy.types.Object: - return boolean_obj.data.BIMMeshProperties.obj + def get_booleaned_obj(cls, obj: bpy.types.Object) -> Union[bpy.types.Object, None]: + """Get boolean obj, return `None` if either it's not a tracked boolean + or it's not referring to an object (e.g. potential boolean object).""" + if obj.type != "MESH": + return + return obj.data.BIMMeshProperties.obj @classmethod def get_tracked_opening_type(cls, obj: bpy.types.Object) -> Union[Literal["OPENING", "BOOLEAN"], None]: