From 8b0a7821b1ccb822893ab698e683136bdfaf40b3 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 19 Mar 2021 10:55:52 +1100 Subject: [PATCH] You can now view / add / edit / remove structural boundary conditions --- .../bim/module/structural/__init__.py | 9 ++ .../add_structural_boundary_condition.py | 15 +++ .../blenderbim/bim/module/structural/data.py | 25 +++- .../edit_structural_boundary_condition.py | 19 ++++ .../bim/module/structural/operator.py | 107 ++++++++++++++++++ .../blenderbim/bim/module/structural/prop.py | 9 +- .../remove_structural_boundary_condition.py | 14 +++ .../blenderbim/bim/module/structural/ui.py | 72 ++++++++++++ 8 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/structural/add_structural_boundary_condition.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/structural/edit_structural_boundary_condition.py create mode 100644 src/ifcblenderexport/blenderbim/bim/module/structural/remove_structural_boundary_condition.py diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/structural/__init__.py index 234a1a8742..4f05dcd3e7 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/structural/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/__init__.py @@ -11,16 +11,25 @@ classes = ( operator.UnassignStructuralAnalysisModel, operator.EnableEditingStructuralAnalysisModel, operator.DisableEditingStructuralAnalysisModel, + operator.AddStructuralBoundaryCondition, + operator.EditStructuralBoundaryCondition, + operator.RemoveStructuralBoundaryCondition, + operator.EnableEditingStructuralBoundaryCondition, + operator.DisableEditingStructuralBoundaryCondition, prop.StructuralAnalysisModel, prop.BIMStructuralProperties, + prop.BIMObjectStructuralProperties, ui.BIM_PT_structural, + ui.BIM_PT_structural_connections, ui.BIM_UL_structural_analysis_models, ) def register(): bpy.types.Scene.BIMStructuralProperties = bpy.props.PointerProperty(type=prop.BIMStructuralProperties) + bpy.types.Object.BIMStructuralProperties = bpy.props.PointerProperty(type=prop.BIMObjectStructuralProperties) def unregister(): del bpy.types.Scene.BIMStructuralProperties + del bpy.types.Object.BIMStructuralProperties diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/add_structural_boundary_condition.py b/src/ifcblenderexport/blenderbim/bim/module/structural/add_structural_boundary_condition.py new file mode 100644 index 0000000000..4a3e8412c1 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/add_structural_boundary_condition.py @@ -0,0 +1,15 @@ +class Usecase: + def __init__(self, file, settings={}): + self.file = file + self.settings = {"connection": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + if self.settings["connection"].is_a("IfcStructuralPointConnection"): + boundary_class = "IfcBoundaryNodeCondition" + elif self.settings["connection"].is_a("IfcStructuralCurveConnection"): + boundary_class = "IfcBoundaryEdgeCondition" + elif self.settings["connection"].is_a("IfcStructuralSurfaceConnection"): + boundary_class = "IfcBoundaryFaceCondition" + self.settings["connection"].AppliedCondition = self.file.create_entity(boundary_class) diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/data.py b/src/ifcblenderexport/blenderbim/bim/module/structural/data.py index 4016609056..d22a826b05 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/structural/data.py +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/data.py @@ -10,12 +10,21 @@ class Data: def purge(cls): cls.is_loaded = False cls.products = {} + cls.connections = {} cls.structural_analysis_models = {} @classmethod - def load(cls): + def load(cls, product_id=None): + if product_id: + cls.connections = {} + return cls.load_structural_connection(product_id) cls.products = {} cls.structural_analysis_models = {} + cls.load_structural_analysis_models() + cls.is_loaded = True + + @classmethod + def load_structural_analysis_models(cls): for model in IfcStore.get_file().by_type("IfcStructuralAnalysisModel"): if model.IsGroupedBy: for rel in model.IsGroupedBy: @@ -36,4 +45,16 @@ class Data: data["HasResults"] = has_results cls.structural_analysis_models[model.id()] = data - cls.is_loaded=True + + @classmethod + def load_structural_connection(cls, product_id): + connection = IfcStore.get_file().by_id(product_id) + if connection.AppliedCondition: + data = connection.AppliedCondition.get_info() + for key, value in data.items(): + if not value or key in ["Name", "type", "id"]: + continue + data[key] = value.wrappedValue + else: + data = {} + cls.connections[connection.id()] = data diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/edit_structural_boundary_condition.py b/src/ifcblenderexport/blenderbim/bim/module/structural/edit_structural_boundary_condition.py new file mode 100644 index 0000000000..151c2a46c7 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/edit_structural_boundary_condition.py @@ -0,0 +1,19 @@ +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "condition": None, + "attributes": {} + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, data in self.settings["attributes"].items(): + if data["type"] == "string" or data["type"] == "null": + value = data["value"] + elif data["type"] == "IfcBoolean": + value = self.file.createIfcBoolean(data["value"]) + else: + value = self.file.create_entity(data["type"], data["value"]) + setattr(self.settings["condition"], name, value) diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/operator.py b/src/ifcblenderexport/blenderbim/bim/module/structural/operator.py index a4dfa6d073..c0773eed02 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/structural/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/operator.py @@ -1,5 +1,8 @@ import bpy import json +import blenderbim.bim.module.structural.add_structural_boundary_condition as add_structural_boundary_condition +import blenderbim.bim.module.structural.edit_structural_boundary_condition as edit_structural_boundary_condition +import blenderbim.bim.module.structural.remove_structural_boundary_condition as remove_structural_boundary_condition import blenderbim.bim.module.structural.add_structural_analysis_model as add_structural_analysis_model import blenderbim.bim.module.structural.edit_structural_analysis_model as edit_structural_analysis_model import blenderbim.bim.module.structural.remove_structural_analysis_model as remove_structural_analysis_model @@ -9,6 +12,110 @@ from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.structural.data import Data +class AddStructuralBoundaryCondition(bpy.types.Operator): + bl_idname = "bim.add_structural_boundary_condition" + bl_label = "Add Structural Boundary Condition" + + def execute(self, context): + obj = context.active_object + file = IfcStore.get_file() + connection = file.by_id(obj.BIMObjectProperties.ifc_definition_id) + add_structural_boundary_condition.Usecase(file, {"connection": connection}).execute() + Data.load(connection.id()) + return {"FINISHED"} + + +class RemoveStructuralBoundaryCondition(bpy.types.Operator): + bl_idname = "bim.remove_structural_boundary_condition" + bl_label = "Enable Editing Structural Boundary Condition" + + def execute(self, context): + obj = context.active_object + file = IfcStore.get_file() + connection = file.by_id(obj.BIMObjectProperties.ifc_definition_id) + remove_structural_boundary_condition.Usecase(file, {"connection": connection}).execute() + Data.load(connection.id()) + return {"FINISHED"} + + +class EnableEditingStructuralBoundaryCondition(bpy.types.Operator): + bl_idname = "bim.enable_editing_structural_boundary_condition" + bl_label = "Enable Editing Structural Boundary Condition" + + def execute(self, context): + obj = context.active_object + oprops = obj.BIMObjectProperties + props = obj.BIMStructuralProperties + while len(props.connection_attributes) > 0: + props.connection_attributes.remove(0) + + data = Data.connections[oprops.ifc_definition_id] + + for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes(): + value = data[attribute.name()] + data_type = str(attribute.type_of_attribute) + new = props.connection_attributes.add() + new.name = attribute.name() + new.is_null = value is None + new.is_optional = attribute.optional() + if "" in data_type: + new.string_value = "" if new.is_null else data[attribute.name()] + new.data_type = "string" + + props.is_editing_connection = True + return {"FINISHED"} + + +class EditStructuralBoundaryCondition(bpy.types.Operator): + bl_idname = "bim.edit_structural_boundary_condition" + bl_label = "Edit Structural Boundary Condition" + + def execute(self, context): + obj = context.active_object + oprops = obj.BIMObjectProperties + props = obj.BIMStructuralProperties + + file = IfcStore.get_file() + connection = file.by_id(obj.BIMObjectProperties.ifc_definition_id) + condition = connection.AppliedCondition + + attributes = {} + for attribute in props.connection_attributes: + if attribute.is_null: + attributes[attribute.name] = {"value": None, "type": "null"} + elif attribute.data_type == "string": + attributes[attribute.name] = {"value": attribute.string_value, "type": "string"} + elif attribute.enum_value == "IfcBoolean": + attributes[attribute.name] = {"value": attribute.bool_value, "type": attribute.enum_value} + else: + attributes[attribute.name] = {"value": attribute.float_value, "type": attribute.enum_value} + + edit_structural_boundary_condition.Usecase(file, {"condition": condition, "attributes": attributes}).execute() + Data.load(connection.id()) + bpy.ops.bim.disable_editing_structural_boundary_condition() + return {"FINISHED"} + + +class DisableEditingStructuralBoundaryCondition(bpy.types.Operator): + bl_idname = "bim.disable_editing_structural_boundary_condition" + bl_label = "Disable Editing Structural Boundary Condition" + + def execute(self, context): + context.active_object.BIMStructuralProperties.is_editing_connection = False + return {"FINISHED"} + + class LoadStructuralAnalysisModels(bpy.types.Operator): bl_idname = "bim.load_structural_analysis_models" bl_label = "Load Structural Analysis Models" diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/prop.py b/src/ifcblenderexport/blenderbim/bim/module/structural/prop.py index b54fdc807c..a8f549d150 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/structural/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/prop.py @@ -19,8 +19,15 @@ class StructuralAnalysisModel(PropertyGroup): class BIMStructuralProperties(PropertyGroup): - structural_analysis_model_attributes: CollectionProperty(name="Structural Analysis Model Attributes", type=Attribute) + structural_analysis_model_attributes: CollectionProperty( + name="Structural Analysis Model Attributes", type=Attribute + ) is_editing: BoolProperty(name="Is Editing", default=False) structural_analysis_models: CollectionProperty(name="Structural Analysis Models", type=StructuralAnalysisModel) active_structural_analysis_model_index: IntProperty(name="Active Structural Analysis Model Index") active_structural_analysis_model_id: IntProperty(name="Active Structural Analysis Model Id") + + +class BIMObjectStructuralProperties(PropertyGroup): + connection_attributes: CollectionProperty(name="Connection Attributes", type=Attribute) + is_editing_connection: BoolProperty(name="Is Editing Connection", default=False) diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/remove_structural_boundary_condition.py b/src/ifcblenderexport/blenderbim/bim/module/structural/remove_structural_boundary_condition.py new file mode 100644 index 0000000000..f861943c63 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/remove_structural_boundary_condition.py @@ -0,0 +1,14 @@ +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = {"connection": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + if not self.settings["connection"].AppliedCondition: + return + if len(self.file.get_inverse(self.settings["connection"].AppliedCondition)) > 1: + self.settings["connection"].AppliedCondition = None + else: + self.file.remove(settings["connection"].AppliedCondition) diff --git a/src/ifcblenderexport/blenderbim/bim/module/structural/ui.py b/src/ifcblenderexport/blenderbim/bim/module/structural/ui.py index 3d84c0b68b..b690b44100 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/structural/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/structural/ui.py @@ -3,6 +3,78 @@ from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.structural.data import Data +class BIM_PT_structural_connections(Panel): + bl_label = "IFC Structural Connections" + bl_idname = "BIM_PT_structural_connections" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + if not context.active_object: + return False + props = context.active_object.BIMObjectProperties + if not props.ifc_definition_id: + return False + if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcStructuralConnection"): + return False + 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(self.oprops.ifc_definition_id) + + self.data = Data.connections[self.oprops.ifc_definition_id] + + row = self.layout.row(align=True) + if self.data and self.props.is_editing_connection: + row.label(text=self.data["type"], icon="CON_TRACKTO") + row.operator("bim.edit_structural_boundary_condition", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_structural_boundary_condition", text="", icon="X") + elif self.data and not self.props.is_editing_connection: + row.label(text=self.data["type"], icon="CON_TRACKTO") + row.operator("bim.enable_editing_structural_boundary_condition", text="", icon="GREASEPENCIL") + row.operator("bim.remove_structural_boundary_condition", text="", icon="X") + else: + row.label(text="No Connection Found", icon="CON_TRACKTO") + row.operator("bim.add_structural_boundary_condition", text="", icon="ADD") + + if self.props.is_editing_connection: + self.draw_editable_ui(context) + else: + self.draw_read_only_ui(context) + + def draw_editable_ui(self, context): + for attribute in self.props.connection_attributes: + if attribute.data_type == "string": + row = self.layout.row() + row.prop(attribute, "string_value", text=attribute["name"]) + else: + row = self.layout.row(align=True) + row.prop(attribute, "enum_value", text=attribute["name"]) + if attribute.enum_value == "IfcBoolean": + row.prop(attribute, "bool_value", text="") + else: + row.prop(attribute, "float_value", text="") + if attribute.is_optional: + row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") + + def draw_read_only_ui(self, context): + for key, value in self.data.items(): + if key == "id" or key == "type" or value == None: + continue + row = self.layout.row(align=True) + row.label(text=key) + if isinstance(value, bool): + row.label(text="", icon="CHECKBOX_HLT" if value else "CHECKBOX_DEHLT") + else: + row.label(text=str(value)) + + class BIM_PT_structural(Panel): bl_label = "IFC Structural Analysis Models" bl_idname = "BIM_PT_structural"