From ba4a94dfead0a23588b94604a21bf5a729a1ed5d Mon Sep 17 00:00:00 2001 From: Robin Quint Date: Fri, 8 Aug 2025 14:54:18 +0200 Subject: [PATCH] Added tool.Blender.Modifier.has_mirrored_type and set_mirrored_type. --- src/bonsai/bonsai/bim/module/model/opening.py | 1 + src/bonsai/bonsai/bim/module/model/product.py | 130 ------------------ .../bonsai/bim/module/model/workspace.py | 9 +- 3 files changed, 2 insertions(+), 138 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/opening.py b/src/bonsai/bonsai/bim/module/model/opening.py index 702f666e47..8a1155e5d4 100644 --- a/src/bonsai/bonsai/bim/module/model/opening.py +++ b/src/bonsai/bonsai/bim/module/model/opening.py @@ -783,6 +783,7 @@ class FlipFill(bpy.types.Operator, tool.Ifc.Operator): tool.Geometry.reload_representation(filled_object) + return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 5007d78165..3d1e155e6a 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -682,136 +682,6 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator): obj.matrix_world = newmat -class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): - bl_idname = "bim.mirror_geometry" - bl_label = "Mirror Element Geometry" - bl_options = {"REGISTER", "UNDO"} - bl_description = "Mirrors the selected objects by mirroring their representation (or their types representation)" - - @classmethod - def poll(cls, context): - return context.selected_objects - - def _execute(self, context): - for obj in context.selected_objects: - self.change_obj_swing_direction(obj) - return { "FINISHED" } - - def change_obj_swing_direction(self, obj): - element = tool.Ifc.get_entity(obj) - if not element: - return - type_element = ifcopenshell.util.element.get_type(element) - - active_context = tool.Geometry.get_active_representation_context(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) - else: - # invert representation of entity directly - self.invert_representation(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. - bonsai.core.geometry.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=ifcopenshell.util.representation.get_representation(element, active_context), - should_reload=False, - is_global=False, - should_sync_changes_first=False, - ) - - def invert_general_object(self, element): - if element.is_a("IfcProduct"): - if not element.Representation: - return - - for representation in element.Representation.Representations: - for item in representation.Items: - builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) - builder.mirror(item, (1, 0), create_copy=False) - elif element.is_a("IfcTypeProduct"): - for representation_map in element.RepresentationMaps: - for item in representation_map.MappedRepresentation.Items: - builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) - builder.mirror(item, (1, 0), create_copy=False) - - tool.Geometry.reload_representation(tool.Ifc.get_object(element)) - - def invert_door_swing(self, element): - obj = tool.Ifc.get_object(element) - - pset_data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Door", "Data")) - - if "LEFT" in pset_data["door_type"]: - pset_data["door_type"] = pset_data["door_type"].replace("LEFT", "RIGHT") - elif "RIGHT" in pset_data["door_type"]: - pset_data["door_type"] = pset_data["door_type"].replace("RIGHT", "LEFT") - - pset = tool.Pset.get_element_pset(element, "BBIM_Door") - pset_data_str = tool.Ifc.get().createIfcText(json.dumps(pset_data, default=list)) - ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=pset, properties={"Data": pset_data_str}) - - pset_data.update(pset_data.pop("lining_properties")) - pset_data.update(pset_data.pop("panel_properties")) - pset_data.update(tool.Model.get_constituents_props_data(element)) - - # we need this workaround because set_props_kwargs_from_ifc_data will - # "update" the mesh of the active object, which will switch its representation - prev_active = bpy.context.view_layer.objects.active - bpy.context.view_layer.objects.active = obj - - props = tool.Model.get_door_props(obj) - props.set_props_kwargs_from_ifc_data(pset_data) - - bpy.context.view_layer.objects.active = prev_active - - # regenerate door geometry - update_door_modifier_representation(obj) - - tool.Model.mark_thumbnail_for_update(element) - - def invert_representation(self, element): - if ifcopenshell.util.element.get_pset(element, "BBIM_Door", "Data"): - self.invert_door_swing(element) - else: - self.invert_general_object(element) - - def get_inverted_type(self, type_element): - inverted_pset = ifcopenshell.util.element.get_pset(type_element, "BBIM_InvertedSwingType", "Data") - if not inverted_pset: - return None - - data = json.loads(inverted_pset) - if "inverted_swing_type" in data: - return tool.Ifc.get_entity_by_id(int(data["inverted_swing_type"])) - return None - - def set_inverted_type(self, type_element, inverted_type_element): - inverted_pset = tool.Pset.get_element_pset(type_element, "BBIM_InvertedSwingType") - if not inverted_pset: - inverted_pset = ifcopenshell.api.pset.add_pset(tool.Ifc.get(), type_element, "BBIM_InvertedSwingType") - - ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), inverted_pset, "BBIM_InvertedSwingType", { "Data": json.dumps({ "inverted_swing_type": inverted_type_element.id() }) }) - - def assign_inverted_type(self, element): - type_element = ifcopenshell.util.element.get_type(element) - - inverted_type = self.get_inverted_type(type_element) - if not inverted_type: - old_to_new, _ = tool.Geometry.duplicate_ifc_objects([ tool.Ifc.get_object(type_element) ]) - inverted_type = old_to_new[type_element][0] - self.invert_representation(inverted_type) - self.set_inverted_type(inverted_type, type_element) - self.set_inverted_type(type_element, inverted_type) - inverted_type.Name = f"{inverted_type.Name}.Mirror" - - bonsai.core.type.assign_type(tool.Ifc, tool.Type, element, inverted_type) - class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.mirror_geometry" diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index 26e0ab08bd..01941e05bf 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -1093,13 +1093,6 @@ class EditObjectUI: def draw_flip(cls, ui_context, layout) -> None: row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else layout add_layout_hotkey_operator(row, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__, ui_context) - row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else layout - add_layout_hotkey_operator(row, "Mirror Geometry", "C_F", bpy.ops.bim.mirror_geometry.__doc__, ui_context) - - @classmethod - def draw_mirror_geometry(cls, ui_context, layout) -> None: - row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else layout - add_layout_hotkey_operator(row, "Mirror Geometry", "C_F", bpy.ops.bim.mirror_geometry.__doc__, ui_context) @classmethod def draw_mirror_geometry(cls, ui_context, layout) -> None: @@ -1308,7 +1301,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): def hotkey_C_F(self): if not bpy.context.selected_objects: return - bpy.ops.bim.mirror_geometry() + bpy.ops.bim.mirror_geometry(keep_original=getattr(self, "shift", False)) def hotkey_S_G(self): obj = bpy.context.active_object