From 87d185fef16c894210e3aebd8f07e33da20b08da Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 28 Oct 2024 18:21:25 +0500 Subject: [PATCH] Lock scale for aggregate parts too if aggregate has openings --- src/bonsai/bonsai/bim/import_ifc.py | 18 ++++++++++++++++-- src/bonsai/bonsai/bim/module/void/operator.py | 4 +--- src/bonsai/bonsai/tool/aggregate.py | 10 ++++++++++ src/bonsai/bonsai/tool/geometry.py | 13 +++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py index 4a0dcc4dfc..1708c95919 100644 --- a/src/bonsai/bonsai/bim/import_ifc.py +++ b/src/bonsai/bonsai/bim/import_ifc.py @@ -254,6 +254,8 @@ class IfcImporter: self.profile_code("Place objects in collections") self.setup_arrays() self.profile_code("Setup arrays") + self.lock_scales() + self.profile_code("Lock objects scales") self.add_project_to_scene() self.profile_code("Add project to scene") if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 1000: @@ -811,8 +813,6 @@ class IfcImporter: obj = bpy.data.objects.new(tool.Loader.get_name(element), mesh) self.link_element(element, obj) - if getattr(element, "HasOpenings", None): - tool.Geometry.lock_scale(obj) if shape: # We use numpy here because Blender mathutils.Matrix is not accurate enough @@ -1157,6 +1157,20 @@ class IfcImporter: tool.Blender.Modifier.Array.set_children_lock_state(element, i, True) tool.Blender.Modifier.Array.constrain_children_to_parent(element) + def lock_scales(self) -> None: + elements = set(self.file.by_type("IfcProduct")) + while elements: + element = elements.pop() + if not getattr(element, "HasOpenings", False): + continue + voided_elements = tool.Aggregate.get_parts_recursively(element) + voided_elements.add(element) + elements.difference_update(voided_elements) + for element in voided_elements: + if not (obj := tool.Ifc.get_object(element)): + continue + tool.Geometry.lock_scale(obj) + class IfcImportSettings: def __init__(self): diff --git a/src/bonsai/bonsai/bim/module/void/operator.py b/src/bonsai/bonsai/bim/module/void/operator.py index ca3c37ec3b..6d243329a4 100644 --- a/src/bonsai/bonsai/bim/module/void/operator.py +++ b/src/bonsai/bonsai/bim/module/void/operator.py @@ -197,9 +197,7 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator): is_global=True, should_sync_changes_first=False, ) - if building_obj and not getattr(element, "HasOpenings", None): - tool.Geometry.unlock_scale(building_obj) - + tool.Geometry.unlock_scale_object_with_openings(obj) tool.Geometry.clear_cache(element) return {"FINISHED"} diff --git a/src/bonsai/bonsai/tool/aggregate.py b/src/bonsai/bonsai/tool/aggregate.py index 18d099eb6e..9066b1ec63 100644 --- a/src/bonsai/bonsai/tool/aggregate.py +++ b/src/bonsai/bonsai/tool/aggregate.py @@ -73,3 +73,13 @@ class Aggregate(bonsai.core.tool.Aggregate): for rel in related_element.Decomposes: if rel.is_a("IfcRelAggregates"): return rel.RelatingObject + + @classmethod + def get_parts_recursively(cls, element: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]: + parts = set() + queue = {element} + while queue: + element = queue.pop() + queue.update(new_parts := set(ifcopenshell.util.element.get_parts(element))) + parts.update(new_parts) + return parts diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 58baa58fe9..d580a0dff9 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -148,6 +148,19 @@ class Geometry(bonsai.core.tool.Geometry): def unlock_scale(cls, obj: bpy.types.Object) -> None: obj.lock_scale = (False, False, False) + @classmethod + def unlock_scale_object_with_openings(cls, obj: bpy.types.Object) -> None: + element = tool.Ifc.get_entity(obj) + queue = {element} + while queue: + element = queue.pop() + if getattr(element, "HasOpenings", None): + # Part still has openings, keep it locked. + continue + obj = tool.Ifc.get_object(element) + cls.unlock_scale(obj) + queue.update(new_parts := set(ifcopenshell.util.element.get_parts(element))) + @classmethod def delete_ifc_item(cls, obj: bpy.types.Object) -> None: props = bpy.context.scene.BIMGeometryProperties