From 2816d7f5224d7fbc04d86d0dce616e0de77022fc Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 19 Mar 2023 18:15:09 +1100 Subject: [PATCH] Fix bug where deleting the drawing object could lead to orphaned relationships --- .../bim/module/geometry/operator.py | 9 ++++++- src/blenderbim/blenderbim/core/drawing.py | 6 ++--- .../test/bim/feature/drawing.feature | 26 +++++++++++++++++++ .../test/bim/feature/geometry.feature | 4 +-- src/blenderbim/test/bim/test_feature.py | 18 ++++++++++++- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index c7ed57d303..8a9a715322 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -28,6 +28,7 @@ import ifcopenshell.api import blenderbim.core.geometry as core import blenderbim.core.style import blenderbim.core.root +import blenderbim.core.drawing import blenderbim.tool as tool import blenderbim.bim.handler from mathutils import Vector @@ -357,6 +358,8 @@ class OverrideDeleteTrait: element = tool.Ifc.get_entity(obj) if not element: return + if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": + return blenderbim.core.drawing.remove_drawing(tool.Ifc, tool.Drawing, drawing=element) IfcStore.delete_element(element) if obj.users_collection and obj.users_collection[0].name == obj.name: parent = ifcopenshell.util.element.get_aggregate(element) @@ -425,7 +428,11 @@ class OverrideDelete(bpy.types.Operator, OverrideDeleteTrait): def _execute(self, context): for obj in context.selected_objects: self.delete_ifc_object(obj) - bpy.data.objects.remove(obj) + try: + obj.name + bpy.data.objects.remove(obj) + except: + pass # Required otherwise gizmos are still visible context.view_layer.objects.active = None return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index ed8850e961..1932132124 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -191,13 +191,13 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati def remove_drawing(ifc, drawing_tool, drawing=None): + collection = drawing_tool.get_drawing_collection(drawing) + if collection: + drawing_tool.delete_collection(collection) group = drawing_tool.get_drawing_group(drawing) if group: drawing_tool.delete_drawing_elements(drawing_tool.get_group_elements(group)) ifc.run("group.remove_group", group=group) - collection = drawing_tool.get_drawing_collection(drawing) - if collection: - drawing_tool.delete_collection(collection) for reference in drawing_tool.get_drawing_references(drawing): reference_obj = ifc.get_object(reference) if reference_obj: diff --git a/src/blenderbim/test/bim/feature/drawing.feature b/src/blenderbim/test/bim/feature/drawing.feature index 28b5c61626..09fde31b26 100644 --- a/src/blenderbim/test/bim/feature/drawing.feature +++ b/src/blenderbim/test/bim/feature/drawing.feature @@ -47,3 +47,29 @@ Scenario: Create drawing after deleting a duplicated object And I press "object.delete(use_global=False)" When I press "bim.create_drawing" Then nothing happens + +Scenario: Remove drawing + Given an empty IFC project + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()" + And I press "bim.add_drawing" + And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" + And the collection "IfcGroup/PLAN_VIEW" exists + When I press "bim.remove_drawing(drawing={drawing})" + Then the collection "IfcGroup/PLAN_VIEW" does not exist + +Scenario: Remove drawing - via object deletion + Given an empty IFC project + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And the variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()" + And I press "bim.add_drawing" + And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" + And the object "IfcAnnotation/PLAN_VIEW" is selected + When I press "bim.override_object_delete" + Then the collection "IfcGroup/PLAN_VIEW" does not exist diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index 8f20dcc22d..75c856e3f2 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -262,7 +262,7 @@ Scenario: Override delete - without active IFC data Given an empty Blender session And I add a cube And the object "Cube" is selected - When I press "object.delete" + When I press "bim.override_object_delete" Then the object "Cube" does not exist Scenario: Override delete - with active IFC data @@ -272,7 +272,7 @@ Scenario: Override delete - with active IFC data And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" And I press "bim.assign_class" And the object "IfcWall/Cube" is selected - When I press "object.delete" + When I press "bim.override_object_delete" Then the object "IfcWall/Cube" does not exist Scenario: Override outliner delete diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 78a3c098ad..4409801795 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -301,6 +301,16 @@ def the_object_name_exists(name) -> bpy.types.Object: return obj +@given(parsers.parse('the collection "{name}" exists')) +@when(parsers.parse('the collection "{name}" exists')) +@then(parsers.parse('the collection "{name}" exists')) +def the_collection_name_exists(name) -> bpy.types.Collection: + obj = bpy.data.collections.get(name) + if not obj: + assert False, f'The collection "{name}" does not exist' + return obj + + @then(parsers.parse('the object "{name1}" and "{name2}" are different elements')) def the_object_name1_and_name2_are_different_elements(name1, name2): ifc = an_ifc_file_exists() @@ -585,7 +595,13 @@ def the_collection_name1_is_in_the_collection_name2(name1, name2): @then(parsers.parse('the object "{name}" does not exist')) def the_object_name_does_not_exist(name): obj = bpy.data.objects.get(name) - assert obj is None or len(obj.users_collection) == 0, "Object exists" + assert obj is None or len(obj.users_collection) == 0, f"Object {name} exists" + + +@then(parsers.parse('the collection "{name}" does not exist')) +def the_collection_name_does_not_exist(name): + obj = bpy.data.collections.get(name) + assert obj is None, f"Collection {name} exists" @then(parsers.parse('objects starting with "{name}" do not exist'))