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() IfcStore.update_cache()
self.sync_all_objects() self.sync_all_objects()
self.sync_edited_objects() self.sync_edited_objects()
tool.Project.save_linked_models_to_ifc()
extension = self.ifc_export_settings.output_file.split(".")[-1].lower() extension = self.ifc_export_settings.output_file.split(".")[-1].lower()
if extension == "ifczip": if extension == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path: with tempfile.TemporaryDirectory() as unzipped_path:
+2
View File
@@ -258,6 +258,8 @@ class IfcImporter:
self.profile_code("Place objects in collections") self.profile_code("Place objects in collections")
self.setup_arrays() self.setup_arrays()
self.profile_code("Setup arrays") self.profile_code("Setup arrays")
tool.Project.load_linked_models_from_ifc()
self.profile_code("Load linked models")
self.lock_scales() self.lock_scales()
self.profile_code("Lock objects scales") self.profile_code("Lock objects scales")
self.add_project_to_scene() self.add_project_to_scene()
+11 -2
View File
@@ -21,7 +21,7 @@ import ifcopenshell.util.system
import bonsai.bim.helper import bonsai.bim.helper
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
from typing import Any, Union from typing import Any, Union, Sequence
class Document(bonsai.core.tool.Document): 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: def import_references(cls, document: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMDocumentProperties props = bpy.context.scene.BIMDocumentProperties
is_ifc2x3 = tool.Ifc.get_schema() == "IFC2X3" 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: for element in references:
new = props.documents.add() new = props.documents.add()
new.ifc_definition_id = element.id() 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: def set_external_reference_id(cls, reference: ifcopenshell.entity_instance, value: Union[str, None]) -> None:
"""Set IfcExternalReference.ItemReference/Identification, compatible with IFC2X3.""" """Set IfcExternalReference.ItemReference/Identification, compatible with IFC2X3."""
reference[1] = value 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 os
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.util.representation import ifcopenshell.util.representation
import ifcopenshell.util.unit import ifcopenshell.util.unit
import bonsai.core.aggregate import bonsai.core.aggregate
@@ -32,7 +33,7 @@ import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional, Union
class Project(bonsai.core.tool.Project): class Project(bonsai.core.tool.Project):
@@ -214,3 +215,74 @@ class Project(bonsai.core.tool.Project):
@classmethod @classmethod
def run_root_reload_grid_decorator(cls) -> None: def run_root_reload_grid_decorator(cls) -> None:
tool.Root.reload_grid_decorator() 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 bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.api.root
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
import tempfile import tempfile
@@ -238,3 +240,108 @@ class TestLoadProject(NewFile):
assert not tool.Ifc.get() assert not tool.Ifc.get()
assert bpy.data.objects["IfcWall/Wall"] assert bpy.data.objects["IfcWall/Wall"]
assert tool.Blender.is_valid_data_block(monkey) 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