Save loaded paths to linked IFC models in IFC #5899

Short demo - https://imgur.com/a/3LIt9Sr
This commit is contained in:
Andrej730
2024-12-26 16:37:49 +05:00
parent f8828d3853
commit 51c9e1eaa2
5 changed files with 194 additions and 3 deletions
+1
View File
@@ -49,6 +49,7 @@ class IfcExporter:
IfcStore.update_cache()
self.sync_all_objects()
self.sync_edited_objects()
tool.Project.save_linked_models_to_ifc()
extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
+2
View File
@@ -258,6 +258,8 @@ class IfcImporter:
self.profile_code("Place objects in collections")
self.setup_arrays()
self.profile_code("Setup arrays")
tool.Project.load_linked_models_from_ifc()
self.profile_code("Load linked models")
self.lock_scales()
self.profile_code("Lock objects scales")
self.add_project_to_scene()
+11 -2
View File
@@ -21,7 +21,7 @@ import ifcopenshell.util.system
import bonsai.bim.helper
import bonsai.core.tool
import bonsai.tool as tool
from typing import Any, Union
from typing import Any, Union, Sequence
class Document(bonsai.core.tool.Document):
@@ -102,7 +102,7 @@ class Document(bonsai.core.tool.Document):
def import_references(cls, document: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMDocumentProperties
is_ifc2x3 = tool.Ifc.get_schema() == "IFC2X3"
references = (document.DocumentReferences or []) if is_ifc2x3 else document.HasDocumentReferences
references = cls.get_document_references(document)
for element in references:
new = props.documents.add()
new.ifc_definition_id = element.id()
@@ -157,3 +157,12 @@ class Document(bonsai.core.tool.Document):
def set_external_reference_id(cls, reference: ifcopenshell.entity_instance, value: Union[str, None]) -> None:
"""Set IfcExternalReference.ItemReference/Identification, compatible with IFC2X3."""
reference[1] = value
@classmethod
def get_document_references(
cls, document: ifcopenshell.entity_instance
) -> tuple[ifcopenshell.entity_instance, ...]:
"""Get IfcDocumentReference.ReferencedDocuments, compatible with IFC2X3."""
if document.file.schema == "IFC2X3":
return document.DocumentReferences or ()
return document.HasDocumentReferences
+73 -1
View File
@@ -19,6 +19,7 @@
import os
import bpy
import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.util.representation
import ifcopenshell.util.unit
import bonsai.core.aggregate
@@ -32,7 +33,7 @@ import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
from pathlib import Path
from typing import Optional
from typing import Optional, Union
class Project(bonsai.core.tool.Project):
@@ -214,3 +215,74 @@ class Project(bonsai.core.tool.Project):
@classmethod
def run_root_reload_grid_decorator(cls) -> None:
tool.Root.reload_grid_decorator()
@classmethod
def get_linked_models_document(cls) -> Union[ifcopenshell.entity_instance, None]:
for document in tool.Ifc.get().by_type("IfcDocumentInformation"):
if document.Name == "BBIM_Linked_Models":
return document
@classmethod
def load_linked_models_from_ifc(cls) -> None:
links = bpy.context.scene.BIMProjectProperties.links
links.clear()
links_document = cls.get_linked_models_document()
if not links_document:
return
references = tool.Document.get_document_references(links_document)
if not references:
return
for reference in references:
link = links.add()
link.name = reference.Location
@classmethod
def save_linked_models_to_ifc(cls) -> None:
ifc_file = tool.Ifc.get()
links = bpy.context.scene.BIMProjectProperties.links
filepaths: set[Path] = set()
for link in links:
filepaths.add(Path(link.name))
links_document = next(
(
document
for document in ifc_file.by_type("IfcDocumentInformation")
if document.Name == "BBIM_Linked_Models"
),
None,
)
if not filepaths and links_document is None:
return
paths_to_add = filepaths.copy()
references_to_remove: list[ifcopenshell.entity_instance] = []
if links_document:
references = tool.Document.get_document_references(links_document)
for reference in references:
# I guess got corrupted by the user.
if not (location := reference.Location):
references_to_remove.remove(reference)
continue
path = Path(location)
if path in paths_to_add:
paths_to_add.remove(path)
else:
references_to_remove.append(reference)
if paths_to_add:
if links_document is None:
links_document = ifcopenshell.api.document.add_information(ifc_file)
links_document.Name = "BBIM_Linked_Models"
links_document.Description = "Bonsai internal document containing references to currently linked models"
for path in paths_to_add:
reference = ifcopenshell.api.document.add_reference(ifc_file, links_document)
reference.Location = path.as_posix()
if references_to_remove:
for reference in references_to_remove:
ifcopenshell.api.document.remove_reference(ifc_file, reference)
+107
View File
@@ -18,6 +18,8 @@
import bpy
import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.api.root
import bonsai.core.tool
import bonsai.tool as tool
import tempfile
@@ -238,3 +240,108 @@ class TestLoadProject(NewFile):
assert not tool.Ifc.get()
assert bpy.data.objects["IfcWall/Wall"]
assert tool.Blender.is_valid_data_block(monkey)
class TestLoadLinkedModels(NewFile):
def test_load_linked_models_no_document(self):
links = bpy.context.scene.BIMProjectProperties.links
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
subject.load_linked_models_from_ifc()
assert len(links) == 0
def test_load_linked_models_document_no_references(self):
ifc = ifcopenshell.file()
links = bpy.context.scene.BIMProjectProperties.links
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
document = ifcopenshell.api.document.add_information(ifc)
document.Name = "BBIM_Linked_Models"
tool.Ifc.set(ifc)
subject.load_linked_models_from_ifc()
assert len(links) == 0
def test_load_linked_models_document_with_references(self):
ifc = ifcopenshell.file()
links = bpy.context.scene.BIMProjectProperties.links
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
document = ifcopenshell.api.document.add_information(ifc)
document.Name = "BBIM_Linked_Models"
reference = ifcopenshell.api.document.add_reference(ifc, document)
linked_model_path = "test.ifc"
reference.Location = linked_model_path
tool.Ifc.set(ifc)
subject.load_linked_models_from_ifc()
assert len(links) == 1
assert links[0].name == linked_model_path
class TestSaveLinkedModelsToIfc(NewFile):
def test_save_linked_models_to_ifc_no_links(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
subject.save_linked_models_to_ifc()
assert len(ifc.by_type("IfcDocumentInformation")) == 0
assert len(ifc.by_type("IfcDocumentReference")) == 0
def test_save_linked_models_to_ifc_paths_to_add(self):
ifc = ifcopenshell.file()
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
links = bpy.context.scene.BIMProjectProperties.links
link = links.add()
linked_model_path = "test.ifc"
link.name = linked_model_path
tool.Ifc.set(ifc)
subject.save_linked_models_to_ifc()
assert len(documents := ifc.by_type("IfcDocumentInformation")) == 1
assert documents[0].Name == "BBIM_Linked_Models"
assert len(references := ifc.by_type("IfcDocumentReference")) == 1
assert references[0].Location == linked_model_path
def test_save_linked_models_to_ifc_already_created_references(self):
ifc = ifcopenshell.file()
links = bpy.context.scene.BIMProjectProperties.links
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
document = ifcopenshell.api.document.add_information(ifc)
document.Name = "BBIM_Linked_Models"
document_id = document.id()
reference = ifcopenshell.api.document.add_reference(ifc, document)
linked_model_path = "test.ifc"
reference.Location = linked_model_path
reference_id = reference.id()
link = links.add()
linked_model_path = "test.ifc"
link.name = linked_model_path
tool.Ifc.set(ifc)
subject.save_linked_models_to_ifc()
# Information and references to stay intact.
assert len(documents := ifc.by_type("IfcDocumentInformation")) == 1
assert documents[0].id() == document_id
assert documents[0].Name == "BBIM_Linked_Models"
assert len(references := ifc.by_type("IfcDocumentReference")) == 1
assert references[0].id() == reference_id
assert references[0].Location == linked_model_path
def test_save_linked_models_to_ifc_references_to_remove(self):
ifc = ifcopenshell.file()
links = bpy.context.scene.BIMProjectProperties.links
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
document = ifcopenshell.api.document.add_information(ifc)
document.Name = "BBIM_Linked_Models"
document_id = document.id()
reference = ifcopenshell.api.document.add_reference(ifc, document)
linked_model_path = "test.ifc"
reference.Location = linked_model_path
tool.Ifc.set(ifc)
subject.save_linked_models_to_ifc()
links.clear()
# Remove reference for removed link.
assert len(documents := ifc.by_type("IfcDocumentInformation")) == 1
assert documents[0].id() == document_id
assert documents[0].Name == "BBIM_Linked_Models"
assert len(ifc.by_type("IfcDocumentReference")) == 0