From 0b7f798c037dd36da8b1527296ea3577012d9cda Mon Sep 17 00:00:00 2001 From: Robin Quint Date: Tue, 12 Aug 2025 21:02:51 +0200 Subject: [PATCH] Mirrored objects now stay in place correctly. Editing doors no longer results in switching representations. --- src/bonsai/bonsai/bim/module/model/product.py | 17 +++++++-- src/bonsai/bonsai/tool/model.py | 35 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 5e31edbede..73f048fe82 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -691,10 +691,10 @@ class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): def _execute(self, context): for obj in context.selected_objects: - self.mirror_obj(obj) + self.mirror_obj(context, obj) return { "FINISHED" } - def mirror_obj(self, obj): + def mirror_obj(self, context: bpy.types.Context, obj: bpy.types.Object): element = tool.Ifc.get_entity(obj) if not element: return @@ -702,6 +702,8 @@ class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): active_context = tool.Geometry.get_active_representation_context(obj) + bb_data = tool.Blender.get_object_bounding_box(obj) + if type_element and element.id() != type_element.id(): # obj has a type, use / create inverted type and assign it self.assign_inverted_type(element) @@ -709,6 +711,17 @@ class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): # invert representation of entity directly self.invert_representation(element) + context.view_layer.update() + + mirrored_bb_data = tool.Blender.get_object_bounding_box(obj) + + x_correction_factor = mirrored_bb_data["min_x"] - bb_data["min_x"] + x_correction_vec = (obj.matrix_world @ Vector((x_correction_factor, 0, 0, 0))).xyz + obj.location -= x_correction_vec + + if element.is_a("IfcElement") and element.FillsVoids: + tool.Model.update_simple_openings(element) + # bonsai does not automatically switch to the representation that should be active in the given context # when switching to a type that was previously viewed in another context (e.g. plan view), # the wrong representation will be used. diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 137bed3371..0b1b59addb 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -1695,6 +1695,38 @@ class Model(bonsai.core.tool.Model): representation=body, ) + @classmethod + def reload_active_representation(cls, obj_or_objects: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None: + """Update active representation including all decomposed objects""" + if isinstance(obj_or_objects, collections.abc.Iterable): + objects = set(obj_or_objects) + else: + objects = {obj_or_objects} + + # decompose objects + decomposed_objs = objects.copy() + for obj in objects: + for subelement in ifcopenshell.util.element.get_decomposition(tool.Ifc.get_entity(obj)): + subobj = tool.Ifc.get_object(subelement) + if subobj: + decomposed_objs.add(subobj) + + # update representation + for obj in decomposed_objs: + if not obj.data: + continue + element = tool.Ifc.get_entity(obj) + active_repr = tool.Geometry.get_active_representation(obj) + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=active_repr, + should_reload=True, + is_global=False, + should_sync_changes_first=False, + ) + @classmethod def is_parametric_roof_active(cls) -> bool: from bonsai.bim.module.model.data import RoofData @@ -2097,7 +2129,8 @@ class Model(bonsai.core.tool.Model): has_replaced_opening_representation = True - tool.Model.reload_body_representation(voided_objs) + tool.Geometry.reload_representation(voided_objs) + tool.Model.reload_active_representation(voided_objs) if fillings: with bpy.context.temp_override(selected_objects=list(fillings.values())): bpy.ops.bim.recalculate_fill()