From 4efb80efe44184e553d6052091b14eeef5787b35 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 1 Feb 2023 14:54:31 +1100 Subject: [PATCH] See #1848. Refactor structural data class. --- .../blenderbim/bim/module/structural/data.py | 259 ++++++++++++++++- .../bim/module/structural/operator.py | 266 ++++++------------ .../blenderbim/bim/module/structural/prop.py | 89 ++---- .../blenderbim/bim/module/structural/ui.py | 203 +++++++------ 4 files changed, 448 insertions(+), 369 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/structural/data.py b/src/blenderbim/blenderbim/bim/module/structural/data.py index b547c2b2c5..2d08c5a5e0 100644 --- a/src/blenderbim/blenderbim/bim/module/structural/data.py +++ b/src/blenderbim/blenderbim/bim/module/structural/data.py @@ -18,29 +18,262 @@ import bpy import blenderbim.tool as tool +import ifcopenshell.util.doc def refresh(): - StructuralData.is_loaded = False + StructuralBoundaryConditionsData.is_loaded = False + ConnectedStructuralMembersData.is_loaded = False + StructuralMemberData.is_loaded = False + StructuralConnectionData.is_loaded = False + StructuralAnalysisModelsData.is_loaded = False + StructuralLoadCasesData.is_loaded = False + StructuralLoadsData.is_loaded = False + BoundaryConditionsData.is_loaded = False -class StructuralData: - products = {} - number_of_structural_analysis_models = 0 +class StructuralBoundaryConditionsData: + data = {} is_loaded = False @classmethod def load(cls): - cls.load_structural_analysis_models() + cls.data = {"boundary_condition": cls.boundary_condition(), "connection_id": cls.connection_id()} cls.is_loaded = True @classmethod - def load_structural_analysis_models(cls): - cls.products = {} - cls.number_of_structural_analysis_models = len(tool.Ifc.get().by_type("IfcStructuralAnalysisModel")) + def boundary_condition(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if not element or not element.AppliedCondition: + return + condition = element.AppliedCondition + attributes = [] + for name, value in condition.get_info().items(): + if name in ["id", "type"] or value is None: + continue + attributes.append({"name": name, "value": value, "is_bool": isinstance(value, bool)}) + return {"id": condition.id(), "type": condition.is_a(), "attributes": attributes} - for model in tool.Ifc.get().by_type("IfcStructuralAnalysisModel"): - if model.IsGroupedBy: - for rel in model.IsGroupedBy: - for product in rel.RelatedObjects: - cls.products.setdefault(product.id(), []).append(model.id()) + @classmethod + def connection_id(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if element: + return element.id() + + +class ConnectedStructuralMembersData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"connections": cls.connections()} + cls.is_loaded = True + + @classmethod + def connections(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if not element: + return [] + results = [] + props = bpy.context.active_object.BIMStructuralProperties + for rel in element.ConnectsStructuralMembers or []: + condition = rel.AppliedCondition + if condition: + attributes = [] + for name, value in condition.get_info().items(): + if name in ["id", "type"] or value is None: + continue + attributes.append({"name": name, "value": value, "is_bool": isinstance(value, bool)}) + condition = {"id": condition.id(), "type": condition.is_a(), "attributes": attributes} + + results.append( + { + "id": rel.id(), + "member_name": rel.RelatingStructuralMember.Name or "Unnamed", + "is_active_condition": bool(condition and props.active_boundary_condition == condition["id"]), + "condition": condition, + } + ) + return results + + +class StructuralMemberData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"active_object_class": cls.active_object_class()} + cls.is_loaded = True + + @classmethod + def active_object_class(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if element: + return element.is_a() + + +class StructuralConnectionData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"active_object_class": cls.active_object_class()} + cls.is_loaded = True + + @classmethod + def active_object_class(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if element: + return element.is_a() + + +class StructuralAnalysisModelsData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"total_models": cls.total_models(), "active_model_ids": cls.active_model_ids()} + cls.is_loaded = True + + @classmethod + def total_models(cls): + return len(tool.Ifc.get().by_type("IfcStructuralAnalysisModel")) + + @classmethod + def active_model_ids(cls): + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + if not element: + return [] + results = [] + for rel in getattr(element, "HasAssignments", []) or []: + if rel.is_a("IfcRelAssignsToGroup"): + results.append(rel.RelatingGroup.id()) + return results + + +class StructuralLoadCasesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data = { + "load_cases": cls.load_cases(), + "applicable_structural_load_types": cls.applicable_structural_load_types(), + "applicable_structural_loads": cls.applicable_structural_loads(), + } + + @classmethod + def load_cases(cls): + results = [] + for load_case in tool.Ifc.get().by_type("IfcStructuralLoadCase"): + load_groups = [] + for rel in load_case.IsGroupedBy or []: + for related_object in rel.RelatedObjects: + load_groups.append({"id": related_object.id(), "name": related_object.Name or "Unnamed"}) + results.append({"id": load_case.id(), "name": load_case.Name or "Unnamed", "load_groups": load_groups}) + return results + + @classmethod + def applicable_structural_load_types(cls): + element_classes = set() + for obj in bpy.context.selected_objects: + element = tool.Ifc.get_entity(obj) + if element: + element_classes.add(element.is_a()) + types = [("IfcStructuralLoadTemperature", "IfcStructuralLoadTemperature", "")] + if "IfcStructuralPointConnection" in element_classes: + types.extend( + [ + ("IfcStructuralLoadSingleForce", "IfcStructuralLoadSingleForce", ""), + ("IfcStructuralLoadSingleDisplacement", "IfcStructuralLoadSingleDisplacement", ""), + ] + ) + if "IfcStructuralCurveMember" in element_classes: + types.append(("IfcStructuralLoadLinearForce", "IfcStructuralLoadLinearForce", "")) + if "IfcStructuralSurfaceMember" in element_classes: + types.append(("IfcStructuralLoadPlanarForce", "IfcStructuralLoadPlanarForce", "")) + return types + + @classmethod + def applicable_structural_loads(cls): + props = bpy.context.scene.BIMStructuralProperties + results = [] + for load in tool.Ifc.get().by_type("IfcStructuralLoad"): + if not load.Name or not load.is_a(props.applicable_structural_load_types): + continue + results.append((str(load.id()), load.Name or "Unnamed", "")) + return results + + +class StructuralLoadsData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "total_loads": cls.total_loads(), + "load_classes": cls.load_classes(), + "structural_load_types": cls.structural_load_types() + } + cls.is_loaded = True + + @classmethod + def total_loads(cls): + return len(tool.Ifc.get().by_type("IfcStructuralLoad")) + + @classmethod + def load_classes(cls): + return {l.id(): l.is_a() for l in tool.Ifc.get().by_type("IfcStructuralLoad")} + + @classmethod + def structural_load_types(cls): + declaration = tool.Ifc.schema().declaration_by_name("IfcStructuralLoadStatic") + version = tool.Ifc.get_schema() + return [ + (d.name(), d.name(), ifcopenshell.util.doc.get_entity_doc(version, d.name()).get("description", "")) + for d in declaration.subtypes() + ] + + +class BoundaryConditionsData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "total_conditions": cls.total_conditions(), + "condition_classes": cls.condition_classes(), + "boundary_condition_types": cls.boundary_condition_types(), + } + cls.is_loaded = True + + @classmethod + def total_conditions(cls): + return len(tool.Ifc.get().by_type("IfcBoundaryCondition")) + + @classmethod + def condition_classes(cls): + return {c.id(): c.is_a() for c in tool.Ifc.get().by_type("IfcBoundaryCondition")} + + @classmethod + def boundary_condition_types(cls): + declaration = tool.Ifc.schema().declaration_by_name("IfcBoundaryCondition") + version = tool.Ifc.get_schema() + return [ + (d.name(), d.name(), ifcopenshell.util.doc.get_entity_doc(version, d.name()).get("description", "")) + for d in declaration.subtypes() + ] diff --git a/src/blenderbim/blenderbim/bim/module/structural/operator.py b/src/blenderbim/blenderbim/bim/module/structural/operator.py index af624822a7..3662397056 100644 --- a/src/blenderbim/blenderbim/bim/module/structural/operator.py +++ b/src/blenderbim/blenderbim/bim/module/structural/operator.py @@ -28,27 +28,13 @@ import blenderbim.core.context from math import degrees from mathutils import Vector, Matrix from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.structural.prop import purge -from ifcopenshell.api.structural.data import Data -from ifcopenshell.api.context.data import Data as ContextData -from blenderbim.bim.module.structural.data import StructuralData -class Operator: - def execute(self, context): - IfcStore.execute_ifc_operator(self, context) - blenderbim.bim.handler.refresh_ui_data() - return {"FINISHED"} - - -class AddStructuralMemberConnection(bpy.types.Operator): +class AddStructuralMemberConnection(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_member_connection" bl_label = "Add Structural Member Connection" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): obj = context.active_object oprops = obj.BIMObjectProperties @@ -65,7 +51,6 @@ class AddStructuralMemberConnection(bpy.types.Operator): related_structural_connection=related_structural_connection, ) props.relating_structural_member = None - Data.load(IfcStore.get_file(), related_structural_connection.id()) return {"FINISHED"} @@ -79,7 +64,6 @@ class EnableEditingStructuralConnectionCondition(bpy.types.Operator): obj = context.active_object oprops = obj.BIMObjectProperties props = obj.BIMStructuralProperties - applied_condition_id = Data.connects_structural_members[self.connects_structural_member]["AppliedCondition"] props.active_connects_structural_member = self.connects_structural_member return {"FINISHED"} @@ -96,62 +80,43 @@ class DisableEditingStructuralConnectionCondition(bpy.types.Operator): return {"FINISHED"} -class RemoveStructuralConnectionCondition(bpy.types.Operator): +class RemoveStructuralConnectionCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_connection_condition" bl_label = "Remove Structural Connection Condition" bl_options = {"REGISTER", "UNDO"} connects_structural_member: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): file = IfcStore.get_file() relation = file.by_id(self.connects_structural_member) connection = relation.RelatedStructuralConnection ifcopenshell.api.run("structural.remove_structural_connection_condition", file, **{"relation": relation}) - - Data.load(IfcStore.get_file(), connection.id()) return {"FINISHED"} -class AddStructuralBoundaryCondition(bpy.types.Operator): +class AddStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_boundary_condition" bl_label = "Add Structural Boundary Condition" bl_options = {"REGISTER", "UNDO"} connection: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): file = IfcStore.get_file() connection = file.by_id(self.connection) ifcopenshell.api.run("structural.add_structural_boundary_condition", file, **{"connection": connection}) - if connection.is_a("IfcRelConnectsStructuralMember"): - Data.load(IfcStore.get_file(), connection.RelatedStructuralConnection.id()) - else: - Data.load(IfcStore.get_file(), connection.id()) return {"FINISHED"} -class RemoveStructuralBoundaryCondition(bpy.types.Operator): +class RemoveStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_boundary_condition" bl_label = "Remove Structural Boundary Condition" bl_options = {"REGISTER", "UNDO"} connection: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): file = IfcStore.get_file() connection = file.by_id(self.connection) ifcopenshell.api.run("structural.remove_structural_boundary_condition", file, **{"connection": connection}) - if connection.is_a("IfcRelConnectsStructuralMember"): - Data.load(IfcStore.get_file(), connection.RelatedStructuralConnection.id()) - else: - Data.load(IfcStore.get_file(), connection.id()) return {"FINISHED"} @@ -166,10 +131,10 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator): props = obj.BIMStructuralProperties props.boundary_condition_attributes.clear() - data = Data.boundary_conditions[self.boundary_condition] + condition = tool.Ifc.get().by_id(self.boundary_condition) - for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes(): - value = data[attribute.name()] + for attribute in IfcStore.get_schema().declaration_by_name(condition.is_a()).all_attributes(): + value = getattr(condition, attribute.name(), None) data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) new = props.boundary_condition_attributes.add() new.name = attribute.name() @@ -179,30 +144,27 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator): enum_items = [s.name() for s in ifcopenshell.util.attribute.get_select_items(attribute)] new.enum_items = json.dumps(enum_items) if isinstance(value, bool): - new.bool_value = False if new.is_null else data[attribute.name()] + new.bool_value = False if new.is_null else value new.data_type = "bool" new.enum_value = "IfcBoolean" elif isinstance(value, float): - new.float_value = 0.0 if new.is_null else data[attribute.name()] + new.float_value = 0.0 if new.is_null else value new.data_type = "float" new.enum_value = [i for i in enum_items if i != "IfcBoolean"][0] elif data_type == "string": - new.string_value = "" if new.is_null else data[attribute.name()] + new.string_value = "" if new.is_null else value new.data_type = "string" props.active_boundary_condition = self.boundary_condition return {"FINISHED"} -class EditStructuralBoundaryCondition(bpy.types.Operator): +class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_boundary_condition" bl_label = "Edit Structural Boundary Condition" bl_options = {"REGISTER", "UNDO"} connection: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): obj = context.active_object props = obj.BIMStructuralProperties @@ -225,10 +187,6 @@ class EditStructuralBoundaryCondition(bpy.types.Operator): ifcopenshell.api.run( "structural.edit_structural_boundary_condition", file, **{"condition": condition, "attributes": attributes} ) - if connection.is_a("IfcRelConnectsStructuralMember"): - Data.load(IfcStore.get_file(), connection.RelatedStructuralConnection.id()) - else: - Data.load(IfcStore.get_file(), connection.id()) bpy.ops.bim.disable_editing_structural_boundary_condition() return {"FINISHED"} @@ -243,7 +201,7 @@ class DisableEditingStructuralBoundaryCondition(bpy.types.Operator): return {"FINISHED"} -class LoadStructuralAnalysisModels(bpy.types.Operator, Operator): +class LoadStructuralAnalysisModels(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.load_structural_analysis_models" bl_label = "Load Structural Analysis Models" bl_options = {"REGISTER", "UNDO"} @@ -252,7 +210,7 @@ class LoadStructuralAnalysisModels(bpy.types.Operator, Operator): core.load_structural_analysis_models(tool.Structural) -class DisableStructuralAnalysisModelEditingUI(bpy.types.Operator, Operator): +class DisableStructuralAnalysisModelEditingUI(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.disable_structural_analysis_model_editing_ui" bl_label = "Disable Structural Analysis Model Editing UI" bl_options = {"REGISTER", "UNDO"} @@ -261,7 +219,7 @@ class DisableStructuralAnalysisModelEditingUI(bpy.types.Operator, Operator): core.disable_structural_analysis_model_editing_ui(tool.Structural) -class AddStructuralAnalysisModel(bpy.types.Operator, Operator): +class AddStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_analysis_model" bl_label = "Add Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -272,7 +230,7 @@ class AddStructuralAnalysisModel(bpy.types.Operator, Operator): core.enable_editing_structural_analysis_model(tool.Structural, model=model.id()) -class EditStructuralAnalysisModel(bpy.types.Operator, Operator): +class EditStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_analysis_model" bl_label = "Edit Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -281,7 +239,7 @@ class EditStructuralAnalysisModel(bpy.types.Operator, Operator): core.edit_structural_analysis_model(tool.Ifc, tool.Structural) -class RemoveStructuralAnalysisModel(bpy.types.Operator, Operator): +class RemoveStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_analysis_model" bl_label = "Remove Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -291,7 +249,7 @@ class RemoveStructuralAnalysisModel(bpy.types.Operator, Operator): core.remove_structural_analysis_model(tool.Ifc, tool.Structural, model=self.structural_analysis_model) -class EnableEditingStructuralAnalysisModel(bpy.types.Operator, Operator): +class EnableEditingStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_editing_structural_analysis_model" bl_label = "Enable Editing Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -302,7 +260,7 @@ class EnableEditingStructuralAnalysisModel(bpy.types.Operator, Operator): core.enable_editing_structural_analysis_model(tool.Structural, model=self.structural_analysis_model) -class DisableEditingStructuralAnalysisModel(bpy.types.Operator, Operator): +class DisableEditingStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.disable_editing_structural_analysis_model" bl_label = "Disable Editing Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -311,7 +269,7 @@ class DisableEditingStructuralAnalysisModel(bpy.types.Operator, Operator): core.disable_editing_structural_analysis_model(tool.Structural) -class AssignStructuralAnalysisModel(bpy.types.Operator, Operator): +class AssignStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_structural_analysis_model" bl_label = "Assign Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -324,7 +282,7 @@ class AssignStructuralAnalysisModel(bpy.types.Operator, Operator): ) -class UnassignStructuralAnalysisModel(bpy.types.Operator, Operator): +class UnassignStructuralAnalysisModel(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unassign_structural_analysis_model" bl_label = "Unassign Structural Analysis Model" bl_options = {"REGISTER", "UNDO"} @@ -391,13 +349,10 @@ class DisableEditingStructuralItemAxis(bpy.types.Operator): return {"FINISHED"} -class EditStructuralItemAxis(bpy.types.Operator): +class EditStructuralItemAxis(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_item_axis" bl_label = "Edit Structural Item Axis" - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): obj = context.active_object oprops = obj.BIMObjectProperties @@ -481,14 +436,11 @@ class DisableEditingStructuralConnectionCS(bpy.types.Operator): return {"FINISHED"} -class EditStructuralConnectionCS(bpy.types.Operator): +class EditStructuralConnectionCS(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_connection_cs" bl_label = "Edit Structural Connection CS" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): obj = context.active_object oprops = obj.BIMObjectProperties @@ -508,13 +460,13 @@ class EditStructuralConnectionCS(bpy.types.Operator): return {"FINISHED"} -class AssignStructuralLoadCase(bpy.types.Operator): +class AssignStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_structural_load_case" bl_label = "Assign Structural Load Case" work_plan: bpy.props.IntProperty() load_case: bpy.props.IntProperty() - def execute(self, context): + def _execute(self, context): self.file = IfcStore.get_file() ifcopenshell.api.run( "aggregate.assign_object", @@ -524,17 +476,16 @@ class AssignStructuralLoadCase(bpy.types.Operator): "product": self.file.by_id(self.load_case), }, ) - Data.load(IfcStore.get_file()) return {"FINISHED"} -class UnassignStructuralLoadCase(bpy.types.Operator): +class UnassignStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unassign_structural_load_case" bl_label = "Unassign Structural Load Case" work_plan: bpy.props.IntProperty() load_case: bpy.props.IntProperty() - def execute(self, context): + def _execute(self, context): self.file = IfcStore.get_file() ifcopenshell.api.run( "aggregate.unassign_object", @@ -544,32 +495,24 @@ class UnassignStructuralLoadCase(bpy.types.Operator): "product": self.file.by_id(self.load_case), }, ) - Data.load(IfcStore.get_file()) return {"FINISHED"} -class AddStructuralLoadCase(bpy.types.Operator): +class AddStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_load_case" bl_label = "Add Structural Load Case" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): ifcopenshell.api.run("structural.add_structural_load_case", IfcStore.get_file()) - Data.load(IfcStore.get_file()) return {"FINISHED"} -class EditStructuralLoadCase(bpy.types.Operator): +class EditStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_load_case" bl_label = "Edit Structural Load Case" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMStructuralProperties attributes = blenderbim.bim.helper.export_attributes(props.load_case_attributes) @@ -579,26 +522,21 @@ class EditStructuralLoadCase(bpy.types.Operator): self.file, **{"load_case": self.file.by_id(props.active_load_case_id), "attributes": attributes}, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.disable_editing_structural_load_case() return {"FINISHED"} -class RemoveStructuralLoadCase(bpy.types.Operator): +class RemoveStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_load_case" bl_label = "Remove Structural Load Case" bl_options = {"REGISTER", "UNDO"} load_case: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): self.file = IfcStore.get_file() ifcopenshell.api.run( "structural.remove_structural_load_case", self.file, load_case=self.file.by_id(self.load_case) ) - Data.load(self.file) return {"FINISHED"} @@ -613,9 +551,8 @@ class EnableEditingStructuralLoadCase(bpy.types.Operator): self.props.active_load_case_id = self.load_case self.props.load_case_editing_type = "ATTRIBUTES" self.props.load_case_attributes.clear() - data = Data.load_cases[self.load_case] - blenderbim.bim.helper.import_attributes( - "IfcStructuralLoadCase", self.props.load_case_attributes, data, self.import_attributes + blenderbim.bim.helper.import_attributes2( + tool.Ifc.get().by_id(self.load_case), self.props.load_case_attributes, callback=self.import_attributes ) return {"FINISHED"} @@ -634,15 +571,12 @@ class DisableEditingStructuralLoadCase(bpy.types.Operator): return {"FINISHED"} -class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator): +class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_editing_structural_load_case_groups" bl_label = "Enable Editing Structural Load Case Groups" bl_options = {"REGISTER", "UNDO"} load_case: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): self.props = context.scene.BIMStructuralProperties self.props.active_load_case_id = self.load_case @@ -650,37 +584,32 @@ class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator): return {"FINISHED"} -class AddStructuralLoadGroup(bpy.types.Operator): +class AddStructuralLoadGroup(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_load_group" bl_label = "Add Structural Load Group" bl_options = {"REGISTER", "UNDO"} load_case: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): self.file = IfcStore.get_file() load_group = ifcopenshell.api.run("structural.add_structural_load_group", self.file) ifcopenshell.api.run( "group.assign_group", self.file, products=[load_group], group=self.file.by_id(self.load_case) ) - Data.load(IfcStore.get_file()) return {"FINISHED"} -class RemoveStructuralLoadGroup(bpy.types.Operator): +class RemoveStructuralLoadGroup(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_load_group" bl_label = "Remove Structural Load Group" bl_options = {"REGISTER", "UNDO"} load_group: bpy.props.IntProperty() - def execute(self, context): + def _execute(self, context): self.file = IfcStore.get_file() ifcopenshell.api.run( "structural.remove_structural_load_group", self.file, load_group=self.file.by_id(self.load_group) ) - Data.load(self.file) return {"FINISHED"} @@ -696,28 +625,24 @@ class EnableEditingStructuralLoadGroupActivities(bpy.types.Operator): self.props.active_load_group_id = self.load_group self.props.load_group_editing_type = "ACTIVITY" self.load_structural_activities() - purge() return {"FINISHED"} def load_structural_activities(self): self.props.load_group_activities.clear() - for activity_id in Data.load_groups[self.load_group]["IsGroupedBy"]: - activity = Data.structural_activities[activity_id] - new = self.props.load_group_activities.add() - new.ifc_definition_id = activity_id - new.name = self.file.by_id(activity["AssignedToStructuralItem"]).Name or "Unnamed" - new.applied_load_class = self.file.by_id(activity["AppliedLoad"]).is_a() + for rel in tool.Ifc.get().by_id(self.load_group).IsGroupedBy: + for activity in rel.RelatedObjects: + new = self.props.load_group_activities.add() + new.ifc_definition_id = activity.id() + new.name = activity.AssignedToStructuralItem.Name or "Unnamed" + new.applied_load_class = activity.AppliedLoad.is_a() -class AddStructuralActivity(bpy.types.Operator): +class AddStructuralActivity(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_activity" bl_label = "Add Structural Activity" bl_options = {"REGISTER", "UNDO"} load_group: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): self.props = context.scene.BIMStructuralProperties self.file = IfcStore.get_file() @@ -758,39 +683,39 @@ class AddStructuralActivity(bpy.types.Operator): ifcopenshell.api.run( "group.assign_group", self.file, products=[activity], group=self.file.by_id(self.load_group) ) - Data.load(IfcStore.get_file()) bpy.ops.bim.enable_editing_structural_load_group_activities(load_group=self.load_group) return {"FINISHED"} -class LoadStructuralLoads(bpy.types.Operator): +class LoadStructuralLoads(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.load_structural_loads" bl_label = "Load Structural Loads" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): + def _execute(self, context): self.file = IfcStore.get_file() props = context.scene.BIMStructuralProperties props.structural_loads.clear() + loads = tool.Ifc.get().by_type("IfcStructuralLoad") if props.filtered_structural_loads: - names = [structural_load["Name"] or "Unnamed" for _, structural_load in Data.structural_loads.items()] - for ifc_definition_id, structural_load in Data.structural_loads.items(): + names = [structural_load.Name or "Unnamed" for structural_load in loads] + for structural_load in loads: if ( - names.count(structural_load["Name"] or "Unnamed") > 1 - and len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) < 2 + names.count(structural_load.Name or "Unnamed") > 1 + and len(self.file.get_inverse(structural_load)) < 2 ): continue new = props.structural_loads.add() - new.ifc_definition_id = ifc_definition_id - new.name = structural_load["Name"] or "Unnamed" - new.number_of_inverse_references = len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) + new.ifc_definition_id = structural_load.id() + new.name = structural_load.Name or "Unnamed" + new.number_of_inverse_references = self.file.get_total_inverses(structural_load) else: - for ifc_definition_id, structural_load in Data.structural_loads.items(): + for structural_load in loads: new = props.structural_loads.add() - new.ifc_definition_id = ifc_definition_id - new.name = structural_load["Name"] or "Unnamed" - new.number_of_inverse_references = len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) + new.ifc_definition_id = structural_load.id() + new.name = structural_load.Name or "Unnamed" + new.number_of_inverse_references = self.file.get_total_inverses(structural_load) props.is_editing_loads = True bpy.ops.bim.disable_editing_structural_load() return {"FINISHED"} @@ -806,20 +731,16 @@ class DisableStructuralLoadEditingUI(bpy.types.Operator): return {"FINISHED"} -class AddStructuralLoad(bpy.types.Operator): +class AddStructuralLoad(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_structural_load" bl_label = "Add Structural Load" bl_options = {"REGISTER", "UNDO"} ifc_class: bpy.props.StringProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): result = ifcopenshell.api.run( "structural.add_structural_load", IfcStore.get_file(), name="New Load", ifc_class=self.ifc_class ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_structural_loads() bpy.ops.bim.enable_editing_structural_load(structural_load=result.id()) return {"FINISHED"} @@ -834,9 +755,9 @@ class EnableEditingStructuralLoad(bpy.types.Operator): def execute(self, context): props = context.scene.BIMStructuralProperties props.structural_load_attributes.clear() - - data = Data.structural_loads[self.structural_load] - blenderbim.bim.helper.import_attributes(data["type"], props.structural_load_attributes, data) + blenderbim.bim.helper.import_attributes2( + tool.Ifc.get().by_id(self.structural_load), props.structural_load_attributes + ) props.active_structural_load_id = self.structural_load return {"FINISHED"} @@ -851,15 +772,12 @@ class DisableEditingStructuralLoad(bpy.types.Operator): return {"FINISHED"} -class RemoveStructuralLoad(bpy.types.Operator): +class RemoveStructuralLoad(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_structural_load" bl_label = "Remove Structural Load" bl_options = {"REGISTER", "UNDO"} structural_load: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMStructuralProperties self.file = IfcStore.get_file() @@ -868,19 +786,15 @@ class RemoveStructuralLoad(bpy.types.Operator): self.file, **{"structural_load": self.file.by_id(self.structural_load)}, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_structural_loads() return {"FINISHED"} -class EditStructuralLoad(bpy.types.Operator): +class EditStructuralLoad(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_structural_load" bl_label = "Edit Structural Load" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMStructuralProperties attributes = blenderbim.bim.helper.export_attributes(props.structural_load_attributes) @@ -893,7 +807,6 @@ class EditStructuralLoad(bpy.types.Operator): "attributes": attributes, }, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_structural_loads() return {"FINISHED"} @@ -919,27 +832,26 @@ class LoadBoundaryConditions(bpy.types.Operator): self.file = IfcStore.get_file() props = context.scene.BIMStructuralProperties props.boundary_conditions.clear() + conditions = tool.Ifc.get().by_type("IfcBoundaryCondition") if props.filtered_boundary_conditions: - names = [ - boundary_condition["Name"] or "Unnamed" for _, boundary_condition in Data.boundary_conditions.items() - ] - for ifc_definition_id, boundary_condition in Data.boundary_conditions.items(): + names = [boundary_condition.Name or "Unnamed" for boundary_condition in conditions] + for boundary_condition in conditions: if ( names.count(boundary_condition["Name"] or "Unnamed") > 1 - and len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) < 2 + and self.file.get_total_inverses(boundary_condition) < 2 ): continue new = props.boundary_conditions.add() - new.ifc_definition_id = ifc_definition_id - new.name = boundary_condition["Name"] or "Unnamed" - new.number_of_inverse_references = len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) + new.ifc_definition_id = boundary_condition.id() + new.name = boundary_condition.Name or "Unnamed" + new.number_of_inverse_references = self.file.get_total_inverses(boundary_condition) else: - for ifc_definition_id, boundary_condition in Data.boundary_conditions.items(): + for boundary_condition in conditions: new = props.boundary_conditions.add() - new.ifc_definition_id = ifc_definition_id - new.name = boundary_condition["Name"] or "Unnamed" - new.number_of_inverse_references = len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) + new.ifc_definition_id = boundary_condition.id() + new.name = boundary_condition.Name or "Unnamed" + new.number_of_inverse_references = self.file.get_total_inverses(boundary_condition) props.is_editing_boundary_conditions = True bpy.ops.bim.disable_editing_boundary_condition() return {"FINISHED"} @@ -967,15 +879,12 @@ class DisableBoundaryConditionEditingUI(bpy.types.Operator): return {"FINISHED"} -class AddBoundaryCondition(bpy.types.Operator): +class AddBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_boundary_condition" bl_label = "Add Boundary Condition" bl_options = {"REGISTER", "UNDO"} ifc_class: bpy.props.StringProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): result = ifcopenshell.api.run( "structural.add_structural_boundary_condition", @@ -983,7 +892,6 @@ class AddBoundaryCondition(bpy.types.Operator): name="New Load", ifc_class=self.ifc_class, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_boundary_conditions() bpy.ops.bim.enable_editing_boundary_condition(boundary_condition=result.id()) return {"FINISHED"} @@ -999,28 +907,28 @@ class EnableEditingBoundaryCondition(bpy.types.Operator): props = context.scene.BIMStructuralProperties props.boundary_condition_attributes.clear() - data = Data.boundary_conditions[self.boundary_condition] + boundary_condition = tool.Ifc.get().by_id(self.boundary_condition) # blenderbim.bim.helper.import_attributes(data["type"], props.boundary_condition_attributes, data) - for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes(): - value = data[attribute.name()] + for attribute in IfcStore.get_schema().declaration_by_name(boundary_condition.is_a()).all_attributes(): + value = getattr(boundary_condition, attribute.name(), None) data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) new = props.boundary_condition_attributes.add() new.name = attribute.name() new.is_null = value is None new.is_optional = attribute.optional() - if data_type == "select": + if isinstance(data_type, tuple) and data_type[0] == "select": enum_items = [s.name() for s in ifcopenshell.util.attribute.get_select_items(attribute)] new.enum_items = json.dumps(enum_items) if isinstance(value, bool): - new.bool_value = False if new.is_null else data[attribute.name()] + new.bool_value = False if new.is_null else value new.data_type = "bool" new.enum_value = "IfcBoolean" elif isinstance(value, float): - new.float_value = 0.0 if new.is_null else data[attribute.name()] + new.float_value = 0.0 if new.is_null else value new.data_type = "float" new.enum_value = [i for i in enum_items if i != "IfcBoolean"][0] elif data_type == "string": - new.string_value = "" if new.is_null else data[attribute.name()] + new.string_value = "" if new.is_null else value new.data_type = "string" props.active_boundary_condition_id = self.boundary_condition return {"FINISHED"} @@ -1036,15 +944,12 @@ class DisableEditingBoundaryCondition(bpy.types.Operator): return {"FINISHED"} -class RemoveBoundaryCondition(bpy.types.Operator): +class RemoveBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_boundary_condition" bl_label = "Remove Boundary Condition" bl_options = {"REGISTER", "UNDO"} boundary_condition: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMStructuralProperties self.file = IfcStore.get_file() @@ -1053,19 +958,15 @@ class RemoveBoundaryCondition(bpy.types.Operator): self.file, **{"boundary_condition": self.file.by_id(self.boundary_condition)}, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_boundary_conditions() return {"FINISHED"} -class EditBoundaryCondition(bpy.types.Operator): +class EditBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_boundary_condition" bl_label = "Edit Boundary Condition" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMStructuralProperties self.file = IfcStore.get_file() @@ -1085,6 +986,5 @@ class EditBoundaryCondition(bpy.types.Operator): self.file, **{"condition": self.file.by_id(props.active_boundary_condition_id), "attributes": attributes}, ) - Data.load(IfcStore.get_file()) bpy.ops.bim.load_boundary_conditions() return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/structural/prop.py b/src/blenderbim/blenderbim/bim/module/structural/prop.py index ac1a6a0f6d..6c801f583f 100644 --- a/src/blenderbim/blenderbim/bim/module/structural/prop.py +++ b/src/blenderbim/blenderbim/bim/module/structural/prop.py @@ -18,11 +18,10 @@ from math import radians import bpy -from ifcopenshell.api.structural.data import Data -from ifcopenshell.util.doc import get_entity_doc import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore from blenderbim.bim.prop import StrProperty, Attribute +from blenderbim.bim.module.structural.data import StructuralLoadCasesData, StructuralLoadsData, BoundaryConditionsData from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -35,83 +34,35 @@ from bpy.props import ( CollectionProperty, ) -structuralloadtypes_enum = [] -applicablestructuralloads_enum = [] - -def purge(): - global structuralloadtypes_enum - global applicablestructuralloads_enum - structuralloadtypes_enum = [] - applicablestructuralloads_enum = [] - - -def getApplicableStructuralLoadTypes(self, context): - ifc_file = IfcStore.get_file() - element_classes = set( - [ - ifc_file.by_id(o.BIMObjectProperties.ifc_definition_id).is_a() - for o in context.selected_objects - if o.BIMObjectProperties.ifc_definition_id - ] - ) - types = [("IfcStructuralLoadTemperature", "IfcStructuralLoadTemperature", "")] - if "IfcStructuralPointConnection" in element_classes: - types.extend( - [ - ("IfcStructuralLoadSingleForce", "IfcStructuralLoadSingleForce", ""), - ("IfcStructuralLoadSingleDisplacement", "IfcStructuralLoadSingleDisplacement", ""), - ] - ) - if "IfcStructuralCurveMember" in element_classes: - types.append(("IfcStructuralLoadLinearForce", "IfcStructuralLoadLinearForce", "")) - if "IfcStructuralSurfaceMember" in element_classes: - types.append(("IfcStructuralLoadPlanarForce", "IfcStructuralLoadPlanarForce", "")) - return types +def get_applicable_structural_load_types(self, context): + if not StructuralLoadCasesData.is_loaded: + StructuralLoadCasesData.load() + return StructuralLoadCasesData.data["applicable_structural_load_types"] def updateApplicableStructuralLoadTypes(self, context): - global applicablestructuralloads_enum - applicablestructuralloads_enum.clear() + StructuralLoadCasesData.data[ + "applicable_structural_load_types" + ] = StructuralLoadCasesData.applicable_structural_load_types() -def getApplicableStructuralLoads(self, context): - global applicablestructuralloads_enum - file = IfcStore.get_file() - if len(applicablestructuralloads_enum) < 1 and file: - for ifc_definition_id, load in Data.structural_loads.items(): - if not load["Name"] or load["type"] != self.applicable_structural_load_types: - continue - applicablestructuralloads_enum.append((str(ifc_definition_id), load["Name"], "")) - return applicablestructuralloads_enum +def get_applicable_structural_loads(self, context): + if not StructuralLoadCasesData.is_loaded: + StructuralLoadCasesData.load() + return StructuralLoadCasesData.data["applicable_structural_loads"] def get_structural_load_types(self, context): - global structuralloadtypes_enum - file = IfcStore.get_file() - if len(structuralloadtypes_enum) < 1 and file: - declaration = IfcStore.get_schema().declaration_by_name("IfcStructuralLoadStatic") - version = tool.Ifc.get_schema() - structuralloadtypes_enum.extend( - [ - (d.name(), d.name(), get_entity_doc(version, d.name()).get("description", "")) - for d in declaration.subtypes() - ] - ) - return structuralloadtypes_enum + if not StructuralLoadsData.is_loaded: + StructuralLoadsData.load() + return StructuralLoadsData.data["structural_load_types"] def get_boundary_condition_types(self, context): - file = IfcStore.get_file() - if file: - declaration = IfcStore.get_schema().declaration_by_name("IfcBoundaryCondition") - version = tool.Ifc.get_schema() - boundaryconditiontypes_enum = [ - (d.name(), d.name(), get_entity_doc(version, d.name()).get("description", "")) - for d in declaration.subtypes() - ] - return boundaryconditiontypes_enum - return [] + if not BoundaryConditionsData.is_loaded: + BoundaryConditionsData.load() + return BoundaryConditionsData.data["boundary_condition_types"] def updateAxisAngle(self, context): @@ -177,11 +128,11 @@ class BIMStructuralProperties(PropertyGroup): # load_group_attributes: CollectionProperty(name="Load Group Attributes", type=Attribute) active_load_group_id: IntProperty(name="Active Load Group Id") applicable_structural_load_types: EnumProperty( - items=getApplicableStructuralLoadTypes, + items=get_applicable_structural_load_types, name="Applicable Structural Load Types", update=updateApplicableStructuralLoadTypes, ) - applicable_structural_loads: EnumProperty(items=getApplicableStructuralLoads, name="Applicable Structural Loads") + applicable_structural_loads: EnumProperty(items=get_applicable_structural_loads, name="Applicable Structural Loads") load_group_activities: CollectionProperty(name="Load Group Activities", type=StructuralActivity) active_load_group_activity_index: IntProperty(name="Active Load Group Activity Index") diff --git a/src/blenderbim/blenderbim/bim/module/structural/ui.py b/src/blenderbim/blenderbim/bim/module/structural/ui.py index 5ebad12343..be97edf45d 100644 --- a/src/blenderbim/blenderbim/bim/module/structural/ui.py +++ b/src/blenderbim/blenderbim/bim/module/structural/ui.py @@ -21,39 +21,42 @@ import blenderbim.bim.helper from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore from blenderbim.bim.helper import draw_attributes, prop_with_search -from ifcopenshell.api.structural.data import Data -from blenderbim.bim.module.structural.data import StructuralData +from blenderbim.bim.module.structural.data import ( + StructuralBoundaryConditionsData, + ConnectedStructuralMembersData, + StructuralMemberData, + StructuralAnalysisModelsData, + StructuralLoadCasesData, + StructuralLoadsData, + StructuralConnectionData, + BoundaryConditionsData, +) -def draw_boundary_condition_ui(layout, boundary_condition_id, connection_id, props): - data = ( - Data.boundary_conditions[boundary_condition_id] - if boundary_condition_id and boundary_condition_id in Data.boundary_conditions.keys() - else {} - ) +def draw_boundary_condition_ui(layout, boundary_condition, connection_id, props): row = layout.row(align=True) - if not data: + if not boundary_condition: row.label(text="No Boundary Condition Found", icon="CON_TRACKTO") row.operator("bim.add_structural_boundary_condition", text="", icon="ADD").connection = connection_id return - if props.active_boundary_condition and props.active_boundary_condition == boundary_condition_id: - row.label(text=data["type"], icon="CON_TRACKTO") + if props.active_boundary_condition and props.active_boundary_condition == boundary_condition["id"]: + row.label(text=boundary_condition["type"], icon="CON_TRACKTO") row.operator("bim.edit_structural_boundary_condition", text="", icon="CHECKMARK").connection = connection_id row.operator("bim.disable_editing_structural_boundary_condition", text="", icon="CANCEL") - elif props.active_boundary_condition and props.active_boundary_condition != boundary_condition_id: - row.label(text=data["type"], icon="CON_TRACKTO") + elif props.active_boundary_condition and props.active_boundary_condition != boundary_condition["id"]: + row.label(text=boundary_condition["type"], icon="CON_TRACKTO") row.operator("bim.remove_structural_boundary_condition", text="", icon="X").connection = connection_id else: - row.label(text=data["type"], icon="CON_TRACKTO") + row.label(text=boundary_condition["type"], icon="CON_TRACKTO") op = row.operator("bim.enable_editing_structural_boundary_condition", text="", icon="GREASEPENCIL") - op.boundary_condition = data["id"] + op.boundary_condition = boundary_condition["id"] row.operator("bim.remove_structural_boundary_condition", text="", icon="X").connection = connection_id - if props.active_boundary_condition and props.active_boundary_condition == boundary_condition_id: + if props.active_boundary_condition and props.active_boundary_condition == boundary_condition["id"]: draw_boundary_condition_editable_ui(layout, props) else: - draw_boundary_condition_read_only_ui(layout, data) + draw_boundary_condition_read_only_ui(layout, boundary_condition) def draw_boundary_condition_editable_ui(layout, props): @@ -72,16 +75,14 @@ def draw_boundary_condition_editable_ui(layout, props): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") -def draw_boundary_condition_read_only_ui(layout, boundary_condition_data): - for key, value in boundary_condition_data.items(): - if key == "id" or key == "type" or value == None: - continue +def draw_boundary_condition_read_only_ui(layout, boundary_condition): + for attribute in boundary_condition["attributes"]: row = layout.row(align=True) - row.label(text=key) - if isinstance(value, bool): - row.label(text="", icon="CHECKBOX_HLT" if value else "CHECKBOX_DEHLT") + row.label(text=attribute["name"]) + if attribute["is_bool"]: + row.label(text="", icon="CHECKBOX_HLT" if attribute["value"] else "CHECKBOX_DEHLT") else: - row.label(text=str(value)) + row.label(text=str(attribute["value"])) class BIM_PT_structural_boundary_conditions(Panel): @@ -91,7 +92,6 @@ class BIM_PT_structural_boundary_conditions(Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" - # bl_parent_id = "BIM_PT_structural_connection" bl_parent_id = "BIM_PT_misc_object" @classmethod @@ -108,13 +108,15 @@ class BIM_PT_structural_boundary_conditions(Panel): return True def draw(self, context): - self.oprops = context.active_object.BIMObjectProperties - self.props = context.active_object.BIMStructuralProperties - if self.oprops.ifc_definition_id not in Data.connections: - Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id) + if not StructuralBoundaryConditionsData.is_loaded: + StructuralBoundaryConditionsData.load() - applied_condition_id = Data.connections[self.oprops.ifc_definition_id]["AppliedCondition"] - draw_boundary_condition_ui(self.layout, applied_condition_id, self.oprops.ifc_definition_id, self.props) + draw_boundary_condition_ui( + self.layout, + StructuralBoundaryConditionsData.data["boundary_condition"], + StructuralBoundaryConditionsData.data["connection_id"], + context.active_object.BIMStructuralProperties, + ) class BIM_PT_connected_structural_members(Panel): @@ -124,7 +126,6 @@ class BIM_PT_connected_structural_members(Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" - # bl_parent_id = "BIM_PT_structural_connection" bl_parent_id = "BIM_PT_misc_object" @classmethod @@ -141,38 +142,33 @@ class BIM_PT_connected_structural_members(Panel): return True def draw(self, context): - self.oprops = context.active_object.BIMObjectProperties - self.props = context.active_object.BIMStructuralProperties - if self.oprops.ifc_definition_id not in Data.connections: - Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id) + if not ConnectedStructuralMembersData.is_loaded: + ConnectedStructuralMembersData.load() - rel_ids = Data.connections[self.oprops.ifc_definition_id]["ConnectsStructuralMembers"] + self.props = context.active_object.BIMStructuralProperties row = self.layout.row(align=True) row.prop(self.props, "relating_structural_member", text="", icon="CON_TRACKTO") row.operator("bim.add_structural_member_connection", text="", icon="ADD") - for rel_id in rel_ids: - rel = Data.connects_structural_members[rel_id] + for connection in ConnectedStructuralMembersData.data["connections"]: row = self.layout.row(align=True) - row.label(text=f"To Member #{IfcStore.get_file().by_id(rel['RelatingStructuralMember']).Name}") - if self.props.active_connects_structural_member and self.props.active_connects_structural_member == rel_id: + row.label(text=f"To Member #{connection['member_name']}") + if ( + self.props.active_connects_structural_member + and self.props.active_connects_structural_member == connection["id"] + ): row.operator("bim.disable_editing_structural_connection_condition", text="", icon="CANCEL") - row.enabled = self.props.active_boundary_condition != rel["AppliedCondition"] - self.draw_editable_ui(context, self.layout, rel) + row.enabled = connection["is_active_condition"] + draw_boundary_condition_ui(self.layout.box(), connection["condition"], connection["id"], self.props) elif self.props.active_connects_structural_member: op = row.operator("bim.remove_structural_connection_condition", text="", icon="X") - op.connects_structural_member = rel_id + op.connects_structural_member = connection["id"] else: op = row.operator("bim.enable_editing_structural_connection_condition", text="", icon="GREASEPENCIL") - op.connects_structural_member = rel_id + op.connects_structural_member = connection["id"] op = row.operator("bim.remove_structural_connection_condition", text="", icon="X") - op.connects_structural_member = rel_id - - def draw_editable_ui(self, context, layout, data): - box = layout.box() - row = box.row(align=True) - draw_boundary_condition_ui(box, data["AppliedCondition"], data["id"], self.props) + op.connects_structural_member = connection["id"] class BIM_PT_structural_member(Panel): @@ -198,11 +194,12 @@ class BIM_PT_structural_member(Panel): return True def draw(self, context): - self.oprops = context.active_object.BIMObjectProperties - self.props = context.active_object.BIMStructuralProperties - self.file = IfcStore.get_file() + if not StructuralMemberData.is_loaded: + StructuralMemberData.load() - if self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcStructuralCurveMember"): + self.props = context.active_object.BIMStructuralProperties + + if StructuralMemberData.data["active_object_class"] == "IfcStructuralCurveMember": if self.props.is_editing_axis: row = self.layout.row(align=True) row.prop(self.props, "axis_angle") @@ -239,11 +236,12 @@ class BIM_PT_structural_connection(Panel): return True def draw(self, context): - self.oprops = context.active_object.BIMObjectProperties - self.props = context.active_object.BIMStructuralProperties - self.file = IfcStore.get_file() + if not StructuralConnectionData.is_loaded: + StructuralConnectionData.load() - if self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcStructuralCurveConnection"): + self.props = context.active_object.BIMStructuralProperties + + if StructuralConnectionData.data["active_object_class"] == "IfcStructuralCurveConnection": if self.props.is_editing_axis: row = self.layout.row(align=True) row.prop(self.props, "axis_angle") @@ -252,8 +250,7 @@ class BIM_PT_structural_connection(Panel): else: row = self.layout.row() row.operator("bim.enable_editing_structural_item_axis", text="Edit Axis", icon="GREASEPENCIL") - - elif self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcStructuralPointConnection"): + elif StructuralConnectionData.data["active_object_class"] == "IfcStructuralPointConnection": if self.props.is_editing_connection_cs: row = self.layout.row(align=True) row.label(text="Editing Connection CS") @@ -290,13 +287,14 @@ class BIM_PT_structural_analysis_models(Panel): return file and hasattr(file, "schema") and file.schema != "IFC2X3" def draw(self, context): - if not StructuralData.is_loaded: - StructuralData.load() + if not StructuralAnalysisModelsData.is_loaded: + StructuralAnalysisModelsData.load() + self.props = context.scene.BIMStructuralProperties row = self.layout.row(align=True) row.label( - text="{} Structural Analysis Models Found".format(StructuralData.number_of_structural_analysis_models), + text=f"{StructuralAnalysisModelsData.data['total_models']} Structural Analysis Models Found", icon="MOD_SIMPLIFY", ) if self.props.is_editing: @@ -316,10 +314,7 @@ class BIM_PT_structural_analysis_models(Panel): ) if self.props.active_structural_analysis_model_id: - self.draw_editable_ui(context) - - def draw_editable_ui(self, context): - draw_attributes(self.props.structural_analysis_model_attributes, self.layout) + draw_attributes(self.props.structural_analysis_model_attributes, self.layout) class BIM_UL_structural_analysis_models(UIList): @@ -330,10 +325,7 @@ class BIM_UL_structural_analysis_models(UIList): if context.active_object: oprops = context.active_object.BIMObjectProperties - if ( - oprops.ifc_definition_id in StructuralData.products - and item.ifc_definition_id in StructuralData.products[oprops.ifc_definition_id] - ): + if item.ifc_definition_id in StructuralAnalysisModelsData.data["active_model_ids"]: op = row.operator( "bim.unassign_structural_analysis_model", text="", icon="KEYFRAME_HLT", emboss=False ) @@ -370,39 +362,39 @@ class BIM_PT_structural_load_cases(Panel): return file and hasattr(file, "schema") and file.schema != "IFC2X3" def draw(self, context): - self.props = context.scene.BIMStructuralProperties + if not StructuralLoadCasesData.is_loaded: + StructuralLoadCasesData.load() - if not Data.is_loaded: - Data.load(IfcStore.get_file()) + self.props = context.scene.BIMStructuralProperties row = self.layout.row() row.operator("bim.add_structural_load_case", icon="ADD") - for load_case_id, load_case in Data.load_cases.items(): - self.draw_load_case_ui(load_case_id, load_case) + for load_case in StructuralLoadCasesData.data["load_cases"]: + self.draw_load_case_ui(load_case) - def draw_load_case_ui(self, load_case_id, load_case): + def draw_load_case_ui(self, load_case): row = self.layout.row(align=True) - row.label(text=load_case["Name"] or "Unnamed", icon="CON_CLAMPTO") + row.label(text=load_case["name"], icon="CON_CLAMPTO") - if self.props.active_load_case_id and self.props.active_load_case_id == load_case_id: + if self.props.active_load_case_id and self.props.active_load_case_id == load_case["id"]: if self.props.load_case_editing_type == "ATTRIBUTES": row.operator("bim.edit_structural_load_case", text="", icon="CHECKMARK") elif self.props.load_case_editing_type == "GROUPS": - row.operator("bim.add_structural_load_group", text="", icon="ADD").load_case = load_case_id + row.operator("bim.add_structural_load_group", text="", icon="ADD").load_case = load_case["id"] row.operator("bim.disable_editing_structural_load_case", text="", icon="CANCEL") elif self.props.active_load_case_id: - row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id + row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case["id"] else: row.operator( "bim.enable_editing_structural_load_case_groups", text="", icon="GHOST_ENABLED" - ).load_case = load_case_id - row.operator( - "bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL" - ).load_case = load_case_id - row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id + ).load_case = load_case["id"] + row.operator("bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL").load_case = load_case[ + "id" + ] + row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case["id"] - if self.props.active_load_case_id == load_case_id: + if self.props.active_load_case_id == load_case["id"]: if self.props.load_case_editing_type == "ATTRIBUTES": self.draw_editable_load_case_ui() elif self.props.load_case_editing_type == "GROUPS": @@ -413,18 +405,17 @@ class BIM_PT_structural_load_cases(Panel): def draw_editable_load_case_group_ui(self, load_case): box = self.layout.box() - if not len(load_case["IsGroupedBy"]): + if not load_case["load_groups"]: row = box.row(align=True) row.label(text="No Load Groups Found") - for load_group_id in load_case["IsGroupedBy"]: - load_group = Data.load_groups[load_group_id] + for load_group in load_case["load_groups"]: row = box.row(align=True) - row.label(text=load_group["Name"] or "Unnamed", icon="GHOST_ENABLED") + row.label(text=load_group["name"], icon="GHOST_ENABLED") op = row.operator("bim.enable_editing_structural_load_group_activities", text="", icon="GHOST_ENABLED") - op.load_group = load_group_id - row.operator("bim.remove_structural_load_group", text="", icon="X").load_group = load_group_id + op.load_group = load_group["id"] + row.operator("bim.remove_structural_load_group", text="", icon="X").load_group = load_group["id"] - if self.props.active_load_group_id == load_group_id: + if self.props.active_load_group_id == load_group["id"]: if self.props.load_group_editing_type == "ACTIVITY": self.draw_editable_load_group_activities_ui(box, load_group) @@ -467,12 +458,13 @@ class BIM_PT_structural_loads(Panel): return file and hasattr(file, "schema") and file.schema != "IFC2X3" def draw(self, context): - if not Data.is_loaded: - Data.load(IfcStore.get_file()) + if not StructuralLoadsData.is_loaded: + StructuralLoadsData.load() + self.props = context.scene.BIMStructuralProperties row = self.layout.row(align=True) - row.label(text="{} Structural Loads Found".format(len(Data.structural_loads)), icon="ANIM_DATA") + row.label(text=f"{StructuralLoadsData.data['total_loads']} Structural Loads Found", icon="ANIM_DATA") if self.props.is_editing_loads: row.operator( "bim.toggle_filter_structural_loads", @@ -506,7 +498,7 @@ class BIM_UL_structural_loads(UIList): if item: row = layout.row(align=True) row.label(text=f"{item.name} ({item.number_of_inverse_references})") - row.label(text=Data.structural_loads[item.ifc_definition_id]["type"]) + row.label(text=StructuralLoadsData.data["load_classes"][item.ifc_definition_id]) if context.scene.BIMStructuralProperties.active_structural_load_id == item.ifc_definition_id: row.operator("bim.edit_structural_load", text="", icon="CHECKMARK") @@ -536,12 +528,15 @@ class BIM_PT_boundary_conditions(Panel): return file and hasattr(file, "schema") and file.schema != "IFC2X3" def draw(self, context): - if not Data.is_loaded: - Data.load(IfcStore.get_file()) + if not BoundaryConditionsData.is_loaded: + BoundaryConditionsData.load() + self.props = context.scene.BIMStructuralProperties row = self.layout.row(align=True) - row.label(text="{} Boundary Conditions Found".format(len(Data.boundary_conditions)), icon="CON_TRACKTO") + row.label( + text=f"{BoundaryConditionsData.data['total_conditions']} Boundary Conditions Found", icon="CON_TRACKTO" + ) if self.props.is_editing_boundary_conditions: row.operator( "bim.toggle_filter_boundary_conditions", @@ -578,7 +573,7 @@ class BIM_UL_boundary_conditions(UIList): if item: row = layout.row(align=True) row.label(text=f"{item.name} ({item.number_of_inverse_references})") - row.label(text=Data.boundary_conditions[item.ifc_definition_id]["type"]) + row.label(text=BoundaryConditionsData.data["condition_classes"][item.ifc_definition_id]) if context.scene.BIMStructuralProperties.active_boundary_condition_id == item.ifc_definition_id: row.operator("bim.edit_boundary_condition", text="", icon="CHECKMARK")