diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py new file mode 100644 index 0000000000..b358c6323d --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -0,0 +1,59 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import blenderbim.tool as tool + + +def refresh(): + AuthoringData.is_loaded = False + WorkspaceData.is_loaded = False + + +class AuthoringData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "ifc_classes": cls.ifc_classes(), + "relating_types": cls.relating_types(), + } + cls.is_loaded = True + + @classmethod + def ifc_classes(cls): + results = [] + classes = set([e.is_a() for e in tool.Ifc.get().by_type("IfcElementType")]) + results.extend([(c, c, "") for c in sorted(classes)]) + return results + + @classmethod + def relating_types(cls): + results = [] + ifc_class = bpy.context.scene.BIMModelProperties.ifc_class + if ifc_class: + elements = [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type(ifc_class)] + results.extend(sorted(elements, key=lambda s: s[1])) + return results + return [] + + +class WorkspaceData(AuthoringData): + pass diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index a64a9d3ee9..d52d2b3fdc 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -63,9 +63,9 @@ class AddTypeInstance(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - tprops = context.scene.BIMTypeProperties - ifc_class = self.ifc_class or tprops.ifc_class - relating_type_id = self.relating_type or tprops.relating_type + props = context.scene.BIMModelProperties + ifc_class = self.ifc_class or props.ifc_class + relating_type_id = self.relating_type or props.relating_type if not ifc_class or not relating_type_id: return {"FINISHED"} @@ -118,7 +118,7 @@ class AddTypeInstance(bpy.types.Operator): tool.Ifc, tool.Type, element=tool.Ifc.get_entity(obj), - type=tool.Ifc.get().by_id(int(tprops.relating_type)), + type=tool.Ifc.get().by_id(int(props.relating_type)), ) if building_obj: diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 1d5f985fb8..5852118f9b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell.util.type +from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.ifc import IfcStore from bpy.types import PropertyGroup @@ -32,21 +33,23 @@ from bpy.props import ( CollectionProperty, ) -relating_types_enum = [] + +def get_ifc_class(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["ifc_classes"] -def purge(): - global relating_types_enum - relating_types_enum = [] +def get_relating_type(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["relating_types"] -def getRelatingTypes(self, context): - global relating_types_enum - if len(relating_types_enum) < 1: - elements = IfcStore.get_file().by_type("IfcWallType") - relating_types_enum.extend((str(e.id()), e.Name, "") for e in elements) - return relating_types_enum +def update_ifc_class(self, context): + AuthoringData.is_loaded = False class BIMModelProperties(PropertyGroup): - relating_type: EnumProperty(items=getRelatingTypes, name="Relating Type") + ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class) + relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type") diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 338b7eb6df..8a198ce7a1 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -19,6 +19,7 @@ import blenderbim.bim.module.type.prop as type_prop from bpy.types import Panel from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.model.data import AuthoringData class BIM_PT_authoring(Panel): @@ -33,17 +34,20 @@ class BIM_PT_authoring(Panel): return IfcStore.get_file() def draw(self, context): - tprops = context.scene.BIMTypeProperties + if not AuthoringData.is_loaded: + AuthoringData.load() + + props = context.scene.BIMModelProperties col = self.layout.column(align=True) enabled = True - if type_prop.getIfcTypes(tprops, context): - col.prop(tprops, "ifc_class", text="", icon="FILE_VOLUME") + if AuthoringData.data["ifc_classes"]: + col.prop(props, "ifc_class", text="", icon="FILE_VOLUME") else: col.label(text="No IFC Class", icon="FILE_VOLUME") enabled = False - if type_prop.get_relating_type(tprops, context): - col.prop(tprops, "relating_type", text="", icon="FILE_3D") + if AuthoringData.data["relating_types"]: + col.prop(props, "relating_type", text="", icon="FILE_3D") else: col.label(text="No Relating Type", icon="FILE_3D") enabled = False diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 2c845965e5..a2ae6e52ad 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -21,6 +21,7 @@ import bpy import blenderbim.bim.module.type.prop as type_prop from bpy.types import WorkSpaceTool from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.model.data import WorkspaceData class BimTool(WorkSpaceTool): @@ -51,17 +52,19 @@ class BimTool(WorkSpaceTool): ) def draw_settings(context, layout, tool): + if not WorkspaceData.is_loaded: + WorkspaceData.load() + row = layout.row(align=True) if not IfcStore.get_file(): row.label(text="No IFC Project", icon="ERROR") return - props = context.scene.BIMTypeProperties - ifc_classes_is_empty = not bool(type_prop.getIfcTypes(props, context)) - if ifc_classes_is_empty: - row.label(text="No IFC Class") - else: + props = context.scene.BIMModelProperties + if WorkspaceData.data["ifc_classes"]: row.prop(props, "ifc_class", text="") - if type_prop.get_relating_type(props, context): + else: + row.label(text="No IFC Class") + if WorkspaceData.data["relating_types"]: row.prop(props, "relating_type", text="") else: row.label(text="No Relating Type") @@ -143,8 +146,12 @@ class Hotkey(bpy.types.Operator): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - self.props = context.scene.BIMTypeProperties - self.ifc_classes_is_empty = not bool(type_prop.getIfcTypes(self.props, context)) + self.props = context.scene.BIMModelProperties + self.ifc_classes_is_empty = True + try: + self.ifc_classes_is_empty = bool(self.props.ifc_class) + except: + pass getattr(self, f"hotkey_{self.hotkey}")() return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/type/__init__.py b/src/blenderbim/blenderbim/bim/module/type/__init__.py index 72f1a07edc..8e5b44357b 100644 --- a/src/blenderbim/blenderbim/bim/module/type/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/type/__init__.py @@ -28,16 +28,13 @@ classes = ( operator.SelectTypeObjects, operator.SelectType, prop.BIMTypeProperties, - prop.BIMTypeObjectProperties, ui.BIM_PT_type, ) def register(): - bpy.types.Scene.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeProperties) - bpy.types.Object.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeObjectProperties) + bpy.types.Object.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeProperties) def unregister(): - del bpy.types.Scene.BIMTypeProperties del bpy.types.Object.BIMTypeProperties diff --git a/src/blenderbim/blenderbim/bim/module/type/data.py b/src/blenderbim/blenderbim/bim/module/type/data.py new file mode 100644 index 0000000000..32533bed93 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/type/data.py @@ -0,0 +1,53 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import blenderbim.tool as tool + + +def refresh(): + TypeData.is_loaded = False + + +class TypeData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "relating_type_classes": cls.relating_type_classes(), + "relating_types": cls.relating_types(), + } + cls.is_loaded = True + + @classmethod + def relating_type_classes(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object) + types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=tool.Ifc.get_schema()) + applicable_types_enum.extend((t, t, "") for t in types) + return results + + @classmethod + def relating_types(cls): + results = [] + elements = tool.Ifc.get().by_type(bpy.context.active_object.BIMTypeProperties.relating_type_class) + elements = [(str(e.id()), e.Name, "") for e in elements] + relating_types_enum.extend(sorted(elements, key=lambda s: s[1])) + return results diff --git a/src/blenderbim/blenderbim/bim/module/type/prop.py b/src/blenderbim/blenderbim/bim/module/type/prop.py index 01376affd2..93fa63e2d1 100644 --- a/src/blenderbim/blenderbim/bim/module/type/prop.py +++ b/src/blenderbim/blenderbim/bim/module/type/prop.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell.util.type +from blenderbim.bim.module.type.data import TypeData from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.ifc import IfcStore from bpy.types import PropertyGroup @@ -32,78 +33,24 @@ from bpy.props import ( CollectionProperty, ) -applicable_types_enum = [] -relating_types_enum = [] -type_classes_enum = [] -available_types_enum = [] - -def purge(): - global applicable_types_enum - global relating_types_enum - global type_classes_enum - global available_types_enum - applicable_types_enum = [] - relating_types_enum = [] - type_classes_enum = [] - available_types_enum = [] - - -def getIfcTypes(self, context): - global type_classes_enum - file = IfcStore.get_file() - if len(type_classes_enum) < 1 and file: - classes = set([e.is_a() for e in file.by_type("IfcElementType")]) - type_classes_enum.extend([(c, c, "") for c in sorted(classes)]) - return type_classes_enum +def get_relating_type_class(self, context): + if not TypeData.is_loaded: + TypeData.load() + return TypeData.data["relating_type_classes"] def get_relating_type(self, context): - global available_types_enum - if len(available_types_enum) < 1 and getIfcTypes(self, context): - elements = [(str(e.id()), e.Name, "") for e in IfcStore.get_file().by_type(self.ifc_class)] - available_types_enum.extend(sorted(elements, key=lambda s: s[1])) - return available_types_enum + if not TypeData.is_loaded: + TypeData.load() + return TypeData.data["relating_types"] -def updateTypeInstanceIfcClass(self, context): - global type_classes_enum - global available_types_enum - type_classes_enum.clear() - available_types_enum.clear() - - -def getApplicableTypes(self, context): - global applicable_types_enum - if len(applicable_types_enum) < 1: - element = IfcStore.get_file().by_id(context.active_object.BIMObjectProperties.ifc_definition_id) - types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=IfcStore.get_file().schema) - applicable_types_enum.extend((t, t, "") for t in types) - return applicable_types_enum - - -def get_object_relating_type(self, context): - global relating_types_enum - if len(relating_types_enum) < 1: - elements = IfcStore.get_file().by_type(context.active_object.BIMTypeProperties.relating_type_class) - elements = [(str(e.id()), e.Name, "") for e in elements] - relating_types_enum.extend(sorted(elements, key=lambda s: s[1])) - return relating_types_enum - - -def updateTypeDropdowns(self, context): - global applicable_types_enum - global relating_types_enum - applicable_types_enum.clear() - relating_types_enum.clear() +def update_is_editing_type(self, context): + TypeData.is_loaded = False class BIMTypeProperties(PropertyGroup): - ifc_class: bpy.props.EnumProperty(items=getIfcTypes, name="IFC Class", update=updateTypeInstanceIfcClass) - relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type") - - -class BIMTypeObjectProperties(PropertyGroup): - is_editing_type: BoolProperty(name="Is Editing Type", update=updateTypeDropdowns) - relating_type_class: EnumProperty(items=getApplicableTypes, name="Relating Type Class") - relating_type: EnumProperty(items=get_object_relating_type, name="Relating Type") + is_editing_type: BoolProperty(name="Is Editing Type", update=update_is_editing_type) + relating_type_class: EnumProperty(items=get_relating_type_class, name="Relating Type Class") + relating_type: EnumProperty(items=get_relating_type, name="Relating Type") diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index 0aa5efee59..edbf8de1df 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -275,9 +275,9 @@ Scenario: Override duplicate move - copying a type instance with a representatio And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMTypeProperties.relating_type" to "{cube}" + And I set "scene.BIMModelProperties.relating_type" to "{cube}" And I press "bim.add_type_instance" And the object "IfcWall/Instance" is selected When I press "object.duplicate_move" diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index c6ab626fdb..52b63b988d 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -8,9 +8,9 @@ Scenario: Add type instance - add from a mesh And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMTypeProperties.relating_type" to "{cube}" + And I set "scene.BIMModelProperties.relating_type" to "{cube}" When I press "bim.add_type_instance" Then the object "IfcWall/Instance" exists @@ -21,9 +21,9 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMTypeProperties.relating_type" to "{empty}" + And I set "scene.BIMModelProperties.relating_type" to "{empty}" When I press "bim.add_type_instance" Then the object "IfcWall/Instance" exists @@ -34,9 +34,9 @@ Scenario: Add type instance - add a mesh where existing instances have changed c And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I press "bim.assign_class" - And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" - And I set "scene.BIMTypeProperties.relating_type" to "{cube}" + And I set "scene.BIMModelProperties.relating_type" to "{cube}" And I press "bim.add_type_instance" And the object "IfcWall/Instance" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Instance" is selected