diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 995c96725d..212cf0ff80 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -759,21 +759,22 @@ class EditProjectLibrary(bpy.types.Operator): attributes = bonsai.bim.helper.export_attributes(props.project_library_attributes) ifcopenshell.api.attribute.edit_attributes(library_file, project_library, attributes) - # Update parent library. + # Update parent library. Tear down the old IfcRelDeclares/IfcRelNests before + # creating the new one; a library must have exactly one of the two, never both. previous_parent_library = tool.Project.get_parent_library(project_library) new_parent_library = library_file.by_id(int(props.parent_library)) if previous_parent_library != new_parent_library: - if previous_parent_library is None: - # Edited library was a root in a library-only file; nest it under the new parent. + if previous_parent_library is not None: + if previous_parent_library.is_a("IfcProject"): + ifcopenshell.api.project.unassign_declaration( + library_file, [project_library], previous_parent_library + ) + else: + ifcopenshell.api.nest.unassign_object(library_file, [project_library]) + if new_parent_library.is_a("IfcProject"): + ifcopenshell.api.project.assign_declaration(library_file, [project_library], new_parent_library) + else: ifcopenshell.api.nest.assign_object(library_file, [project_library], new_parent_library) - elif previous_parent_library.is_a("IfcProject"): - # Then new one is IfcProjectLibrary. - ifcopenshell.api.nest.assign_object(library_file, [project_library], new_parent_library) - else: # Previous is IfcProjectLibrary. - ifcopenshell.api.nest.unassign_object(library_file, [project_library]) - # If new one is IfcProject, then it's already assigned by default. - if new_parent_library.is_a("IfcProjectLibrary"): - ifcopenshell.api.nest.assign_object(library_file, [project_library], new_parent_library) props.is_editing_project_library = False bpy.ops.bim.refresh_library() diff --git a/src/bonsai/test/bim/module/project/test_project_library_data.py b/src/bonsai/test/bim/module/project/test_project_library_data.py index 699e8a31b3..df9192aa6d 100644 --- a/src/bonsai/test/bim/module/project/test_project_library_data.py +++ b/src/bonsai/test/bim/module/project/test_project_library_data.py @@ -75,6 +75,13 @@ class TestLibraryFile(NewIfc): assert tool.Project.get_parent_library(root) == project + def test_get_parent_library_returns_the_library_for_a_nested_sub_library(self): + library_file = _make_library_file(with_child=True) + root = next(lib for lib in library_file.by_type("IfcProjectLibrary") if lib.Name == "RootLib") + child = next(lib for lib in library_file.by_type("IfcProjectLibrary") if lib.Name == "ChildLib") + + assert tool.Project.get_parent_library(child) == root + def test_get_parent_library_returns_none_for_an_orphaned_library(self): library_file = ifcopenshell.api.project.create_file(version="IFC4") orphan = ifcopenshell.api.root.create_entity(library_file, ifc_class="IfcProjectLibrary", name="Orphan") @@ -117,6 +124,58 @@ class TestLibraryFile(NewIfc): IfcStore.library_file = None ProjectLibraryData.is_loaded = False + def test_edit_project_library_moves_a_project_declared_library_under_another_library(self): + import bpy + + library_file = _make_library_file() + project = library_file.by_type("IfcProject")[0] + root = library_file.by_type("IfcProjectLibrary")[0] + target = ifcopenshell.api.root.create_entity(library_file, ifc_class="IfcProjectLibrary", name="TargetLib") + ifcopenshell.api.project.assign_declaration(library_file, definitions=[target], relating_context=project) + IfcStore.library_file = library_file + try: + props = tool.Project.get_project_props() + props.selected_project_library = str(root.id()) + props.is_editing_project_library = True + props.parent_library = str(target.id()) + + result = bpy.ops.bim.edit_project_library() + + assert result == {"FINISHED"} + assert tool.Project.get_parent_library(root) == target + assert root.Nests and root.Nests[0].RelatingObject == target + assert not root.HasContext + finally: + if props.is_editing_project_library: + props.is_editing_project_library = False + IfcStore.library_file = None + ProjectLibraryData.is_loaded = False + + def test_edit_project_library_moves_a_nested_library_back_under_the_project(self): + import bpy + + library_file = _make_library_file(with_child=True) + project = library_file.by_type("IfcProject")[0] + child = next(lib for lib in library_file.by_type("IfcProjectLibrary") if lib.Name == "ChildLib") + IfcStore.library_file = library_file + try: + props = tool.Project.get_project_props() + props.selected_project_library = str(child.id()) + props.is_editing_project_library = True + props.parent_library = str(project.id()) + + result = bpy.ops.bim.edit_project_library() + + assert result == {"FINISHED"} + assert tool.Project.get_parent_library(child) == project + assert child.HasContext and child.HasContext[0].RelatingContext == project + assert not child.Nests + finally: + if props.is_editing_project_library: + props.is_editing_project_library = False + IfcStore.library_file = None + ProjectLibraryData.is_loaded = False + def test_add_project_library_declares_new_library_under_the_project_root(self): import bpy