diff --git a/src/bonsai/bonsai/bim/handler.py b/src/bonsai/bonsai/bim/handler.py index 5f5b65095f..48931cc4f1 100644 --- a/src/bonsai/bonsai/bim/handler.py +++ b/src/bonsai/bonsai/bim/handler.py @@ -24,6 +24,7 @@ import ifcopenshell.util.unit import ifcopenshell.api.owner.settings import bonsai.bim import bonsai.tool as tool +import weakref from bpy.app.handlers import persistent from bonsai.bim.ifc import IfcStore from bonsai.bim.module.model.data import AuthoringData @@ -257,24 +258,61 @@ def redo_post(scene: bpy.types.Scene) -> None: tool.Ifc.rebuild_element_maps() +# Cache is important as those entities will be retrieved very often, +# for every IfcOwnerHistory creation or update. +class SettingsCache: + APPLICATION_ID: Union[int, None] = None + USER_ID: Union[int, None] = None + + _file: Union[weakref.ReferenceType[ifcopenshell.file], None] = None + + @classmethod + def get_file(cls) -> Union[ifcopenshell.file, None]: + if cls._file is None: + return None + return cls._file() + + @classmethod + def set_file(cls, file: ifcopenshell.file) -> None: + cls._file = weakref.ref(file) + + def get_application(ifc: ifcopenshell.file) -> ifcopenshell.entity_instance: - # TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts. + if SettingsCache.get_file() is ifc and SettingsCache.APPLICATION_ID is not None: + try: + app = ifc.by_id(SettingsCache.APPLICATION_ID) + return app + except RuntimeError: + pass + # Use only main part from the version to avoid flooding advanced users projects with IfcApplications. version = tool.Blender.get_bonsai_version().split("-")[0] for element in ifc.by_type("IfcApplication"): if element.ApplicationIdentifier == "Bonsai" and element.Version == version: return element - return ifcopenshell.api.owner.add_application( + application = ifcopenshell.api.owner.add_application( ifc, version=version, application_full_name="Bonsai", application_identifier="Bonsai", ) + SettingsCache.APPLICATION_ID = application.id() + SettingsCache.set_file(ifc) + return application def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]: # TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts. + if SettingsCache.get_file() is ifc and SettingsCache.USER_ID is not None: + try: + user = ifc.by_id(SettingsCache.USER_ID) + return user + except RuntimeError: + pass + if pao := next(iter(ifc.by_type("IfcPersonAndOrganization")), None): + SettingsCache.USER_ID = pao.id() + SettingsCache.set_file(ifc) return pao elif ifc.schema == "IFC2X3": if (person := next(iter(ifc.by_type("IfcPerson")), None)) is None: @@ -282,6 +320,8 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None if (organization := next(iter(ifc.by_type("IfcOrganization")), None)) is None: organization = ifcopenshell.api.owner.add_organisation(ifc) pao = ifcopenshell.api.owner.add_person_and_organisation(ifc, person=person, organisation=organization) + SettingsCache.USER_ID = pao.id() + SettingsCache.set_file(ifc) return pao