mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Add general CRUD operations for boundary conditions (from scene properties)
This commit is contained in:
@@ -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
|
||||
|
||||
+20
-13
@@ -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()
|
||||
|
||||
+13
-6
@@ -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"])
|
||||
|
||||
Reference in New Issue
Block a user