From 87fee0b8503efdb9d53561acdbda6dd3ef8900cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Thu, 12 Dec 2024 11:08:04 -0300 Subject: [PATCH] Add features for handling newly created objects in aggregate mode - Highlight new objects in red when in aggregate mode - Introduce an operator to add new objects to the current aggregate, accessible via a button in the header menu --- .../bonsai/bim/module/aggregate/__init__.py | 1 + .../bonsai/bim/module/aggregate/decorator.py | 41 +++++++++++++++++-- .../bonsai/bim/module/aggregate/operator.py | 36 ++++++++++++++++ .../bonsai/bim/module/model/workspace.py | 1 + 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/aggregate/__init__.py b/src/bonsai/bonsai/bim/module/aggregate/__init__.py index daafdf201c..6a459541df 100644 --- a/src/bonsai/bonsai/bim/module/aggregate/__init__.py +++ b/src/bonsai/bonsai/bim/module/aggregate/__init__.py @@ -32,6 +32,7 @@ classes = ( operator.BIM_OT_select_linked_aggregates, operator.BIM_OT_disable_aggregate_mode, operator.BIM_OT_toggle_aggregate_mode_local_view, + operator.BIM_OT_aggregate_assing_new_objects_in_aggregate_mode, prop.BIMObjectAggregateProperties, prop.Objects, prop.BIMAggregateProperties, diff --git a/src/bonsai/bonsai/bim/module/aggregate/decorator.py b/src/bonsai/bonsai/bim/module/aggregate/decorator.py index 4b94989e9d..be7540eda3 100644 --- a/src/bonsai/bonsai/bim/module/aggregate/decorator.py +++ b/src/bonsai/bonsai/bim/module/aggregate/decorator.py @@ -25,6 +25,7 @@ from bpy.types import SpaceView3D from bpy_extras import view3d_utils from gpu_extras.batch import batch_for_shader from mathutils import Vector +from bonsai.bim.module.geometry.decorator import ItemDecorator def transparent_color(color, alpha=0.1): @@ -85,7 +86,6 @@ class AggregateDecorator: cls.uninstall() handler = cls() cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_aggregate, (context,), "WINDOW", "POST_VIEW")) - cls.handlers.append(SpaceView3D.draw_handler_add(handler.lock_objects, (context,), "WINDOW", "POST_VIEW")) cls.is_installed = True @classmethod @@ -227,6 +227,9 @@ class AggregateModeDecorator: cls.handlers.append( SpaceView3D.draw_handler_add(handler.draw_aggregate_empty, (context,), "WINDOW", "POST_VIEW") ) + cls.handlers.append( + SpaceView3D.draw_handler_add(handler.draw_new_objects, (context,), "WINDOW", "POST_VIEW") + ) cls.is_installed = True @classmethod @@ -248,7 +251,9 @@ class AggregateModeDecorator: region = context.region rv3d = region.data props = context.scene.BIMAggregateProperties - if aggregate_obj := props.editing_aggregate: + + aggregate_obj = props.editing_aggregate + if not aggregate_obj: return self.addon_prefs = tool.Blender.get_addon_preferences() self.font_id = 0 @@ -273,7 +278,8 @@ class AggregateModeDecorator: def draw_aggregate_empty(self, context): props = context.scene.BIMAggregateProperties - if aggregate_obj := props.editing_aggregate: + aggregate_obj = props.editing_aggregate + if not aggregate_obj: return self.addon_prefs = tool.Blender.get_addon_preferences() self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") @@ -294,8 +300,8 @@ class AggregateModeDecorator: parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(aggregate_obj)) if parts: for part in parts: + part_obj = tool.Ifc.get_object(part) if part.is_a("IfcElementAssembly"): - part_obj = tool.Ifc.get_object(part) self.line_shader.uniform_float("lineWidth", 1.0) size = part_obj.empty_display_size location = part_obj.location @@ -315,3 +321,30 @@ class AggregateModeDecorator: indices, edges = create_bounding_box(parts_objs) self.line_shader.uniform_float("lineWidth", 0.5) self.draw_batch("LINES", indices, color, edges) + + def draw_new_objects(self, context): + self.addon_prefs = tool.Blender.get_addon_preferences() + self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") + self.line_shader.bind() + self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height)) + self.line_shader.uniform_float("lineWidth", 2.0) + color = self.addon_prefs.decorator_color_unselected + props = context.scene.BIMAggregateProperties + editing_objects = set([o.obj for o in props.editing_objects]) + not_editing_objects = set([o.obj for o in props.not_editing_objects]) + objs = [] + visible_objects = tool.Raycast.get_visible_objects(context) + for obj in visible_objects: + if obj.visible_in_viewport_get(context.space_data): + objs.append(obj.original) + new_objs = set(objs) - not_editing_objects - editing_objects + new_objs = [o for o in new_objs if o.data] + for obj in new_objs: + element = tool.Ifc.get_entity(obj) + if element and element.is_a("IfcElement"): + data = ItemDecorator.get_obj_data(obj) + if data: + self.draw_batch( + "TRIS", data["verts"], transparent_color((1, 0, 0, 1)), data["tris"] + ) + diff --git a/src/bonsai/bonsai/bim/module/aggregate/operator.py b/src/bonsai/bonsai/bim/module/aggregate/operator.py index e5cd4a93ea..04c55d19d4 100644 --- a/src/bonsai/bonsai/bim/module/aggregate/operator.py +++ b/src/bonsai/bonsai/bim/module/aggregate/operator.py @@ -68,6 +68,10 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator): related_obj=obj, ) tool.Aggregate.constrain_parts_to_aggregate(relating_obj) + props = context.scene.BIMAggregateProperties + if relating_obj == props.editing_aggregate and props.in_aggregate_mode: + new_editing_obj = props.editing_objects.add() + new_editing_obj.obj = obj except core.IncompatibleAggregateError: self.report({"ERROR"}, f"Cannot aggregate {obj.name} to {relating_obj.name}") except core.AggregateRepresentationError: @@ -399,3 +403,35 @@ class BIM_OT_toggle_aggregate_mode_local_view(bpy.types.Operator): return {"FINISHED"} +class BIM_OT_aggregate_assing_new_objects_in_aggregate_mode(bpy.types.Operator): + bl_idname = "bim.aggregate_assing_new_objects_in_aggregate_mode" + bl_label = "Aggregate Assing New Objects In Aggregate Mode" + bl_options = {"REGISTER", "UNDO"} + relating_object: bpy.props.IntProperty() + related_object: bpy.props.IntProperty() + + def execute(self, context): + bpy.ops.object.select_all(action="DESELECT") + props = context.scene.BIMAggregateProperties + editing_objects = set([o.obj for o in props.editing_objects]) + not_editing_objects = set([o.obj for o in props.not_editing_objects]) + objs = [] + visible_objects = tool.Raycast.get_visible_objects(context) + for obj in visible_objects: + if obj.visible_in_viewport_get(context.space_data): + objs.append(obj.original) + new_objs = set(objs) - not_editing_objects - editing_objects + new_objs = [o for o in new_objs if o.data] + for obj in new_objs: + element = tool.Ifc.get_entity(obj) + if element and element.is_a("IfcElement"): + obj.select_set(True) + editing_obj = props.editing_objects.add() + editing_obj.obj = obj + props.editing_aggregate.select_set(True) + self.relating_object = tool.Ifc.get_entity(props.editing_aggregate).id() + self.related_object = tool.Ifc.get_entity(obj).id() + BIM_OT_aggregate_assign_object._execute(self, context) + + return {"FINISHED"} + diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index c4f59f400f..c1ebac4352 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -594,6 +594,7 @@ class EditObjectUI: row = cls.layout.row(align=True) op = row.operator("bim.disable_aggregate_mode", text="", icon="X") op = row.operator("bim.toggle_aggregate_mode_local_view", text="", icon="ZOOM_SELECTED") + op = row.operator("bim.aggregate_assing_new_objects_in_aggregate_mode", text="", icon="CUBE") text = format_ifc_camel_case(AuthoringData.data["active_class"]) layout.label(text=f"{text} Edit Tools:", icon="RESTRICT_SELECT_OFF")