From 21d1d014e3094de88e601d59e898a1e7b58ce996 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. --- .../bonsai/bim/module/geometry/operator.py | 32 +++++++++---------- src/bonsai/bonsai/bim/module/model/door.py | 8 ++--- src/bonsai/bonsai/bim/module/model/product.py | 27 +++------------- src/bonsai/bonsai/tool/blender.py | 15 +++++++++ 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index afa68b42e8..732bc61a9f 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -2719,24 +2719,22 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator): # apply changes to mirrored object, if it exists if props.representation_obj and (representation_elem := tool.Ifc.get_entity(props.representation_obj)): type_elem = ifcopenshell.util.element.get_type(representation_elem) - if type_elem and (mirrored_pset := ifcopenshell.util.element.get_pset(type_elem, "BBIM_InvertedSwingType", "Data")): - mirrored_data = json.loads(mirrored_pset) - if "inverted_swing_type" in mirrored_data and (inverted_type := tool.Ifc.get_entity_by_id(int(mirrored_data["inverted_swing_type"]))): - # object has mirror, update it as well - for map_index, repr_map in enumerate(type_elem.RepresentationMaps): - new_items = [] - for item in repr_map.MappedRepresentation.Items: - new_mirrored_repr = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), item) - builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) - builder.mirror(new_mirrored_repr, (1, 0), create_copy=False) - new_items.append(new_mirrored_repr) - - old_items = inverted_type.RepresentationMaps[map_index].MappedRepresentation.Items - inverted_type.RepresentationMaps[map_index].MappedRepresentation.Items = new_items - for old_item in old_items: - ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_item) + if type_elem and (mirrored_type := tool.Blender.Modifier.has_mirrored_type(type_elem)): + # object has mirror, update it as well + for map_index, repr_map in enumerate(type_elem.RepresentationMaps): + new_items = [] + for item in repr_map.MappedRepresentation.Items: + new_mirrored_repr = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), item) + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + builder.mirror(new_mirrored_repr, (1, 0), create_copy=False) + new_items.append(new_mirrored_repr) - tool.Geometry.reload_representation(tool.Ifc.get_object(inverted_type)) + old_items = mirrored_type.RepresentationMaps[map_index].MappedRepresentation.Items + mirrored_type.RepresentationMaps[map_index].MappedRepresentation.Items = new_items + for old_item in old_items: + ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_item) + + tool.Geometry.reload_representation(tool.Ifc.get_object(mirrored_type)) def enable_edit_mode(self, context): diff --git a/src/bonsai/bonsai/bim/module/model/door.py b/src/bonsai/bonsai/bim/module/model/door.py index 22ea4afeff..a280b1fa30 100644 --- a/src/bonsai/bonsai/bim/module/model/door.py +++ b/src/bonsai/bonsai/bim/module/model/door.py @@ -638,11 +638,9 @@ class FinishEditingDoor(_DoorEditMixin, bpy.types.Operator, tool.Ifc.Operator): door_data_str = tool.Ifc.get().createIfcText(json.dumps(door_data, default=list)) ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=pset, properties={"Data": door_data_str}) - if inverted_pset := ifcopenshell.util.element.get_pset(element, "BBIM_InvertedSwingType", "Data"): - inverted_data = json.loads(inverted_pset) - if "inverted_swing_type" in inverted_data and (inverted_type := tool.Ifc.get_entity_by_id(int(inverted_data["inverted_swing_type"]))): - # object has mirrored repr, update it as well - self.copy_door_params(door_data, element, inverted_type) + if mirrored_type := tool.Blender.Modifier.has_mirrored_type(element): + # object has mirrored repr, update it as well + self.copy_door_params(door_data, element, mirrored_type) def copy_door_params(self, from_data, from_elem, to_elem): if "RIGHT" in from_data["door_type"]: diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 35374f28fb..5e31edbede 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.change_obj_swing_direction(obj) + self.mirror_obj(obj) return { "FINISHED" } - def change_obj_swing_direction(self, obj): + def mirror_obj(self, obj): element = tool.Ifc.get_entity(obj) if not element: return @@ -778,33 +778,16 @@ class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator): 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) + inverted_type = tool.Blender.Modifier.has_mirrored_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) + tool.Blender.Modifier.set_mirrored_type(inverted_type, type_element) + tool.Blender.Modifier.set_mirrored_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) diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index 0594958613..fbb98b9ae1 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -61,6 +61,7 @@ import numpy as np import numpy.typing as npt from gpu_extras.batch import batch_for_shader from ifcopenshell import entity_instance +import ifcopenshell.api.pset from mathutils import Matrix, Vector import bonsai.bim @@ -1618,6 +1619,20 @@ class Blender(bonsai.core.tool.Blender): _any_selected_array_child_memo: tuple[frozenset[int], int, bool] | None = None + @classmethod + def has_mirrored_type(cls, element: entity_instance, inherit: bool = True) -> entity_instance | None: + pset = ifcopenshell.util.element.get_pset(element, "BBIM_MirroredType", "Data", should_inherit=inherit) + if pset and (parsed := json.loads(pset)) and "mirrored_type" in parsed: + return tool.Ifc.get_entity_by_id(int(parsed["mirrored_type"])) + return None + + @classmethod + def set_mirrored_type(cls, element: entity_instance, mirrored_type: entity_instance): + pset = tool.Pset.get_element_pset(element, "BBIM_MirroredType") + if not pset: + pset = ifcopenshell.api.pset.add_pset(tool.Ifc.get(), element, "BBIM_MirroredType") + ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset, properties={"Data": json.dumps({"mirrored_type": mirrored_type.id()})}) + @classmethod def is_slab(cls, element: entity_instance) -> bool: """A slab is host-eligible for the parametric add-opening gizmo if