diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 38a0b61078..745d7ce9f2 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -612,10 +612,11 @@ class OverrideDelete(bpy.types.Operator): return self.execute(context) def draw(self, context): - row = self.layout.row() - row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR") row = self.layout.row() row.prop(self, "is_batch", text="Enable Faster Deletion") + if self.is_batch: + row = self.layout.row() + row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR") def _execute(self, context): start_time = time() @@ -627,7 +628,7 @@ class OverrideDelete(bpy.types.Operator): for obj in context.selected_objects: element = tool.Ifc.get_entity(obj) if element: - if tool.Geometry.is_lockable(element): + if tool.Geometry.is_locked(element): continue if ifcopenshell.util.element.get_pset(element, "BBIM_Array"): self.report({"INFO"}, "Elements that are part of an array cannot be deleted.") @@ -736,10 +737,11 @@ class OverrideOutlinerDelete(bpy.types.Operator): return self.execute(context) def draw(self, context): - row = self.layout.row() - row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR") row = self.layout.row() row.prop(self, "is_batch", text="Enable Faster Deletion") + if self.is_batch: + row = self.layout.row() + row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR") def _execute(self, context): if self.is_batch: @@ -757,7 +759,7 @@ class OverrideOutlinerDelete(bpy.types.Operator): objects_to_delete.add(bpy.data.objects.get(item.name)) for obj in objects_to_delete: if element := tool.Ifc.get_entity(obj): - if tool.Geometry.is_lockable(element): + if tool.Geometry.is_locked(element): if collection := obj.BIMObjectProperties.collection: collections_to_delete.discard(collection) continue diff --git a/src/bonsai/bonsai/bim/module/spatial/prop.py b/src/bonsai/bonsai/bim/module/spatial/prop.py index 9674db84bb..dc48c27773 100644 --- a/src/bonsai/bonsai/bim/module/spatial/prop.py +++ b/src/bonsai/bonsai/bim/module/spatial/prop.py @@ -86,6 +86,35 @@ def update_container_obj(self, context): self.container_obj = None +def update_grid_is_locked(self, context): + if not tool.Ifc.get(): + return + if tool.Ifc.get().schema in ("IFC2X3", "IFC4"): + elements = tool.Ifc.get().by_type("IfcGrid") + tool.Ifc.get().by_type("IfcGridAxis") + else: + elements = tool.Ifc.get().by_type("IfcPositioningElement") + for element in elements: + if obj := tool.Ifc.get_object(element): + if self.is_locked: + tool.Geometry.lock_object(obj) + else: + tool.Geometry.unlock_object(obj) + + +def update_spatial_is_locked(self, context): + if not tool.Ifc.get(): + return + if tool.Ifc.get().schema == "IFC2X3": + elements = tool.Ifc.get().by_type("IfcSpatialStructureElement") + else: + elements = tool.Ifc.get().by_type("IfcSpatialElement") + for element in elements: + if obj := tool.Ifc.get_object(element): + if self.is_locked: + tool.Geometry.lock_object(obj) + else: + tool.Geometry.unlock_object(obj) + def poll_container_obj(self, obj): return obj is None or tool.Ifc.get_entity(obj) @@ -133,6 +162,7 @@ class Element(PropertyGroup): class BIMSpatialDecompositionProperties(PropertyGroup): + is_locked: BoolProperty(name="Is Locked", default=True, update=update_spatial_is_locked) container_filter: StringProperty(name="Container Filter", default="", options={"TEXTEDIT_UPDATE"}) containers: CollectionProperty(name="Containers", type=BIMContainer) contracted_containers: StringProperty(name="Contracted containers", default="[]") @@ -169,4 +199,5 @@ class BIMSpatialDecompositionProperties(PropertyGroup): class BIMGridProperties(PropertyGroup): + is_locked: BoolProperty(name="Is Locked", default=True, update=update_grid_is_locked) grid_axes: CollectionProperty(name="Grid Axes", type=ObjProperty) diff --git a/src/bonsai/bonsai/bim/module/spatial/ui.py b/src/bonsai/bonsai/bim/module/spatial/ui.py index dff3f0fef9..0ae92c1b67 100644 --- a/src/bonsai/bonsai/bim/module/spatial/ui.py +++ b/src/bonsai/bonsai/bim/module/spatial/ui.py @@ -96,11 +96,19 @@ class BIM_PT_spatial_decomposition(Panel): bl_region_type = "WINDOW" bl_context = "scene" bl_parent_id = "BIM_PT_tab_spatial" + bl_options = {"HEADER_LAYOUT_EXPAND"} @classmethod def poll(cls, context): return tool.Ifc.get() + def draw_header(self, context): + row = self.layout.row(align=True) + row.label(text="") # empty text occupies the left of the row + icon = "VIEW_LOCKED" if context.scene.BIMSpatialDecompositionProperties.is_locked else "VIEW_UNLOCKED" + row.prop(context.scene.BIMSpatialDecompositionProperties, "is_locked", text="", icon=icon) + + def draw(self, context): if not SpatialDecompositionData.is_loaded: SpatialDecompositionData.load() @@ -203,10 +211,17 @@ class BIM_PT_grids(Panel): bl_region_type = "WINDOW" bl_context = "scene" bl_parent_id = "BIM_PT_tab_spatial" + bl_options = {"HEADER_LAYOUT_EXPAND"} def draw(self, context): self.layout.row().operator("mesh.add_grid", icon="ADD", text="Add Grids") + def draw_header(self, context): + row = self.layout.row(align=True) + row.label(text="") # empty text occupies the left of the row + icon = "VIEW_LOCKED" if context.scene.BIMGridProperties.is_locked else "VIEW_UNLOCKED" + row.prop(context.scene.BIMGridProperties, "is_locked", text="", icon=icon) + class BIM_UL_containers_manager(UIList): def __init__(self): diff --git a/src/bonsai/bonsai/tool/collector.py b/src/bonsai/bonsai/tool/collector.py index 19549eb89f..38130358fd 100644 --- a/src/bonsai/bonsai/tool/collector.py +++ b/src/bonsai/bonsai/tool/collector.py @@ -41,7 +41,7 @@ class Collector(bonsai.core.tool.Collector): element = tool.Ifc.get_entity(obj) assert element - if tool.Geometry.is_lockable(element): + if tool.Geometry.is_locked(element): tool.Geometry.lock_object(obj) if element.is_a("IfcGridAxis"): diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index bdb79bb1f7..116887281c 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -92,14 +92,14 @@ class Geometry(bonsai.core.tool.Geometry): bpy.data.meshes.remove(data) @classmethod - def is_lockable(cls, element: ifcopenshell.entity_instance) -> bool: - if ( - element.is_a("IfcProject") - or tool.Root.is_spatial_element(element) - or element.is_a("IfcPositioningElement") - or element.is_a("IfcGrid") - or element.is_a("IfcGridAxis") - ): + def is_locked(cls, element: ifcopenshell.entity_instance) -> bool: + if element.is_a("IfcProject"): + return True + elif tool.Root.is_spatial_element(element) and bpy.context.scene.BIMSpatialDecompositionProperties.is_locked: + return True + elif ( + element.is_a("IfcPositioningElement") or element.is_a("IfcGrid") or element.is_a("IfcGridAxis") + ) and bpy.context.scene.BIMGridProperties.is_locked: return True return False @@ -108,6 +108,11 @@ class Geometry(bonsai.core.tool.Geometry): obj.lock_location = (True, True, True) obj.lock_rotation = (True, True, True) + @classmethod + def unlock_object(cls, obj: bpy.types.Object) -> None: + obj.lock_location = (False, False, False) + obj.lock_rotation = (False, False, False) + @classmethod def delete_ifc_object(cls, obj: bpy.types.Object) -> None: element = tool.Ifc.get_entity(obj)