Add general CRUD operations for boundary conditions (from scene properties)

This commit is contained in:
Jesusbill
2021-06-20 20:57:12 +02:00
parent a0b7fe2736
commit e569b97c56
7 changed files with 312 additions and 22 deletions
@@ -44,9 +44,18 @@ classes = (
operator.RemoveStructuralLoad,
operator.EditStructuralLoad,
operator.ToggleFilterStructuralLoads,
operator.LoadBoundaryConditions,
operator.DisableBoundaryConditionEditingUI,
operator.AddBoundaryCondition,
operator.EnableEditingBoundaryCondition,
operator.DisableEditingBoundaryCondition,
operator.RemoveBoundaryCondition,
operator.EditBoundaryCondition,
operator.ToggleFilterBoundaryConditions,
prop.StructuralAnalysisModel,
prop.StructuralActivity,
prop.StructuralLoad,
prop.BoundaryCondition,
prop.BIMStructuralProperties,
prop.BIMObjectStructuralProperties,
ui.BIM_PT_structural_analysis_models,
@@ -59,6 +68,8 @@ classes = (
ui.BIM_PT_structural_load_cases,
ui.BIM_UL_structural_loads,
ui.BIM_PT_structural_loads,
ui.BIM_UL_boundary_conditions,
ui.BIM_PT_boundary_conditions,
)
@@ -888,3 +888,166 @@ class ToggleFilterStructuralLoads(bpy.types.Operator):
return {"FINISHED"}
class LoadBoundaryConditions(bpy.types.Operator):
bl_idname = "bim.load_boundary_conditions"
bl_label = "Load Boundary Conditions"
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMStructuralProperties
while len(props.boundary_conditions) > 0:
props.boundary_conditions.remove(0)
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():
if (
names.count(boundary_condition["Name"] or "Unnamed") > 1
and len(self.file.get_inverse(self.file.by_id(ifc_definition_id))) < 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)))
else:
for ifc_definition_id, boundary_condition in Data.boundary_conditions.items():
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)))
props.is_editing_boundary_conditions = True
bpy.ops.bim.disable_editing_boundary_condition()
return {"FINISHED"}
class ToggleFilterBoundaryConditions(bpy.types.Operator):
bl_idname = "bim.toggle_filter_boundary_conditions"
bl_label = "Toggle Filter Boundary Conditions"
def execute(self, context):
props = context.scene.BIMStructuralProperties
props.filtered_boundary_conditions = not props.filtered_boundary_conditions
bpy.ops.bim.load_boundary_conditions()
return {"FINISHED"}
class DisableBoundaryConditionEditingUI(bpy.types.Operator):
bl_idname = "bim.disable_boundary_condition_editing_ui"
bl_label = "Disable Boundary Condition Editing UI"
def execute(self, context):
context.scene.BIMStructuralProperties.is_editing_boundary_conditions = False
return {"FINISHED"}
class AddBoundaryCondition(bpy.types.Operator):
bl_idname = "bim.add_boundary_condition"
bl_label = "Add Boundary Condition"
ifc_class: bpy.props.StringProperty()
def execute(self, context):
result = ifcopenshell.api.run(
"structural.add_structural_boundary_condition",
IfcStore.get_file(),
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"}
class EnableEditingBoundaryCondition(bpy.types.Operator):
bl_idname = "bim.enable_editing_boundary_condition"
bl_label = "Enable Editing Boundary Condition"
boundary_condition: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMStructuralProperties
while len(props.boundary_condition_attributes) > 0:
props.boundary_condition_attributes.remove(0)
data = Data.boundary_conditions[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()]
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":
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.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.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.data_type = "string"
props.active_boundary_condition_id = self.boundary_condition
return {"FINISHED"}
class DisableEditingBoundaryCondition(bpy.types.Operator):
bl_idname = "bim.disable_editing_boundary_condition"
bl_label = "Disable Editing Boundary Condition"
def execute(self, context):
context.scene.BIMStructuralProperties.active_boundary_condition_id = 0
return {"FINISHED"}
class RemoveBoundaryCondition(bpy.types.Operator):
bl_idname = "bim.remove_boundary_condition"
bl_label = "Remove Boundary Condition"
boundary_condition: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMStructuralProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"structural.remove_structural_boundary_condition",
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):
bl_idname = "bim.edit_boundary_condition"
bl_label = "Edit Boundary Condition"
def execute(self, context):
props = context.scene.BIMStructuralProperties
self.file = IfcStore.get_file()
# attributes = blenderbim.bim.helper.export_attributes(props.boundary_condition_attributes)
attributes = {}
for attribute in props.boundary_condition_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}
ifcopenshell.api.run(
"structural.edit_structural_boundary_condition",
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"}
@@ -73,6 +73,15 @@ def getStructuralLoadTypes(self, context):
return structuralloadtypes_enum
def getBoundaryConditionTypes(self, context):
file = IfcStore.get_file()
if file:
declaration = IfcStore.get_schema().declaration_by_name("IfcBoundaryCondition")
boundaryconditiontypes_enum = [(d.name(), d.name(), "") for d in declaration.subtypes()]
return boundaryconditiontypes_enum
return []
def updateAxisAngle(self, context):
if not self.axis_empty:
return
@@ -115,6 +124,12 @@ class StructuralLoad(PropertyGroup):
number_of_inverse_references: IntProperty(name="Number of Inverse References")
class BoundaryCondition(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
number_of_inverse_references: IntProperty(name="Number of Inverse References")
class BIMStructuralProperties(PropertyGroup):
structural_analysis_model_attributes: CollectionProperty(
name="Structural Analysis Model Attributes", type=Attribute
@@ -147,6 +162,13 @@ class BIMStructuralProperties(PropertyGroup):
structural_load_attributes: CollectionProperty(name="Structural Load Attributes", type=Attribute)
filtered_structural_loads: BoolProperty(name="Filtered Structural Loads", default=False)
boundary_conditions: CollectionProperty(name="Boundary Conditions", type=BoundaryCondition)
active_boundary_condition_index: IntProperty(name="Active Boundary Condition Index")
active_boundary_condition_id: IntProperty(name="Active Boundary Condition Id")
is_editing_boundary_conditions: BoolProperty(name="Is Editing Boundary Conditions", default=False)
boundary_condition_types: EnumProperty(items=getBoundaryConditionTypes, name="Boundary Condition Types")
boundary_condition_attributes: CollectionProperty(name="Boundary Condition Attributes", type=Attribute)
filtered_boundary_conditions: BoolProperty(name="Filtered Boundary Conditions", default=False)
class BIMObjectStructuralProperties(PropertyGroup):
@@ -6,7 +6,11 @@ from ifcopenshell.api.structural.data import Data
def draw_boundary_condition_ui(layout, boundary_condition_id, connection_id, props):
data = Data.boundary_conditions[boundary_condition_id] if boundary_condition_id else {}
data = (
Data.boundary_conditions[boundary_condition_id]
if boundary_condition_id and boundary_condition_id in Data.boundary_conditions.keys()
else {}
)
row = layout.row(align=True)
if not data:
row.label(text="No Boundary Condition Found", icon="CON_TRACKTO")
@@ -486,7 +490,7 @@ class BIM_UL_structural_loads(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.name)
row.label(text=f"{item.name} ({item.number_of_inverse_references})")
row.label(text=Data.structural_loads[item.ifc_definition_id]["type"])
if context.scene.BIMStructuralProperties.active_structural_load_id == item.ifc_definition_id:
@@ -500,3 +504,73 @@ class BIM_UL_structural_loads(UIList):
op.structural_load = item.ifc_definition_id
op = row.operator("bim.remove_structural_load", text="", icon="X")
op.structural_load = item.ifc_definition_id
class BIM_PT_boundary_conditions(Panel):
bl_label = "IFC Boundary Conditions"
bl_idname = "BIM_PT_boundary_conditions"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
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")
if self.props.is_editing_boundary_conditions:
row.operator(
"bim.toggle_filter_boundary_conditions",
text="FILTER - OFF" if not self.props.filtered_boundary_conditions else "FILTER - ON",
icon="FILTER",
)
row.operator("bim.disable_boundary_condition_editing_ui", text="", icon="SCREEN_BACK")
row = self.layout.row(align=True)
row.prop(self.props, "boundary_condition_types", text="")
row.operator(
"bim.add_boundary_condition", text="", icon="ADD"
).ifc_class = self.props.boundary_condition_types
else:
row.operator("bim.load_boundary_conditions", text="", icon="GREASEPENCIL")
if self.props.is_editing_boundary_conditions:
self.layout.template_list(
"BIM_UL_boundary_conditions",
"",
self.props,
"boundary_conditions",
self.props,
"active_boundary_condition_index",
)
if self.props.active_boundary_condition_id:
draw_boundary_condition_editable_ui(self.layout, self.props)
# blenderbim.bim.helper.draw_attributes(self.props.boundary_condition_attributes, self.layout)
class BIM_UL_boundary_conditions(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
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"])
if context.scene.BIMStructuralProperties.active_boundary_condition_id == item.ifc_definition_id:
row.operator("bim.edit_boundary_condition", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_boundary_condition", text="", icon="CANCEL")
elif context.scene.BIMStructuralProperties.active_boundary_condition_id:
op = row.operator("bim.remove_boundary_condition", text="", icon="X")
op.boundary_condition = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_boundary_condition", text="", icon="GREASEPENCIL")
op.boundary_condition = item.ifc_definition_id
op = row.operator("bim.remove_boundary_condition", text="", icon="X")
op.boundary_condition = item.ifc_definition_id
@@ -1,21 +1,28 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"connection": None}
self.settings = {"name": None, "connection": None, "ifc_class": "IfcBoundaryNodeCondition"}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.settings["connection"].is_a("IfcRelConnectsStructuralMember"):
related_connection = self.settings["connection"].RelatedStructuralConnection
if self.settings["connection"]:
# assign boundary condition to a connection
if self.settings["connection"].is_a("IfcRelConnectsStructuralMember"):
related_connection = self.settings["connection"].RelatedStructuralConnection
else:
related_connection = self.settings["connection"]
if related_connection.is_a("IfcStructuralPointConnection"):
boundary_class = "IfcBoundaryNodeCondition"
elif related_connection.is_a("IfcStructuralCurveConnection"):
boundary_class = "IfcBoundaryEdgeCondition"
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
boundary_class = "IfcBoundaryFaceCondition"
self.settings["connection"].AppliedCondition = self.file.create_entity(
boundary_class, Name=self.settings["name"]
)
else:
related_connection = self.settings["connection"]
if related_connection.is_a("IfcStructuralPointConnection"):
boundary_class = "IfcBoundaryNodeCondition"
elif related_connection.is_a("IfcStructuralCurveConnection"):
boundary_class = "IfcBoundaryEdgeCondition"
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
boundary_class = "IfcBoundaryFaceCondition"
self.settings["connection"].AppliedCondition = self.file.create_entity(boundary_class)
# add an orphan boundary condition
return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"])
@@ -50,6 +50,7 @@ class Data:
cls.load_structural_load_groups()
cls.load_structural_activities()
cls.load_structural_loads()
cls.load_boundary_conditions()
cls.is_loaded = True
@classmethod
@@ -143,7 +144,6 @@ class Data:
@classmethod
def load_structural_connection(cls, product_id):
cls.boundary_conditions = {}
cls.connects_structural_members = {}
connection = cls._file.by_id(product_id)
@@ -205,6 +205,12 @@ class Data:
def load_structural_load(cls, load):
cls.structural_loads[load.id()] = load.get_info()
@classmethod
def load_boundary_conditions(cls):
cls.boundary_conditions = {}
for bc in cls._file.by_type("IfcBoundaryCondition"):
cls.load_boundary_condition(bc)
@classmethod
def load_connects_structural_activity(cls, rel):
rel_data = rel.get_info()
@@ -1,13 +1,20 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"connection": None}
self.settings = {"connection": None, "boundary_condition": 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.file.remove(self.settings["connection"].AppliedCondition)
self.settings["connection"].AppliedCondition = None
if self.settings["connection"]:
# remove boundary condition from a connection
if not self.settings["connection"].AppliedCondition:
return
if len(self.file.get_inverse(self.settings["connection"].AppliedCondition)) == 1:
self.file.remove(self.settings["connection"].AppliedCondition)
self.settings["connection"].AppliedCondition = None
else:
# remove the boundary condition
for conn in self.file.get_inverse(self.settings["boundary_condition"]):
conn.AppliedCondition = None
self.file.remove(self.settings["boundary_condition"])