Implement add / remove load groups, and half implement structural activities. Thanks Jesusbill and ihabelaghoury

This commit is contained in:
Dion Moult
2021-05-07 18:41:03 +10:00
parent 15927dd501
commit f9b79b4beb
8 changed files with 281 additions and 26 deletions
@@ -29,10 +29,15 @@ classes = (
operator.AddStructuralLoadCase, operator.AddStructuralLoadCase,
operator.EditStructuralLoadCase, operator.EditStructuralLoadCase,
operator.RemoveStructuralLoadCase, operator.RemoveStructuralLoadCase,
operator.AddStructuralLoadGroup,
operator.RemoveStructuralLoadGroup,
operator.AddStructuralActivity,
operator.EnableEditingStructuralLoadCase, operator.EnableEditingStructuralLoadCase,
operator.EnableEditingStructuralLoadCaseActivity, operator.EnableEditingStructuralLoadCaseGroups,
operator.DisableEditingStructuralLoadCase, operator.DisableEditingStructuralLoadCase,
operator.EnableEditingStructuralLoadGroupActivities,
prop.StructuralAnalysisModel, prop.StructuralAnalysisModel,
prop.StructuralActivity,
prop.BIMStructuralProperties, prop.BIMStructuralProperties,
prop.BIMObjectStructuralProperties, prop.BIMObjectStructuralProperties,
ui.BIM_PT_structural_analysis_models, ui.BIM_PT_structural_analysis_models,
@@ -41,6 +46,7 @@ classes = (
ui.BIM_PT_structural_member, ui.BIM_PT_structural_member,
ui.BIM_PT_structural_connection, ui.BIM_PT_structural_connection,
ui.BIM_UL_structural_analysis_models, ui.BIM_UL_structural_analysis_models,
ui.BIM_UL_structural_activities,
ui.BIM_PT_structural_load_cases, ui.BIM_PT_structural_load_cases,
) )
@@ -627,13 +627,106 @@ class DisableEditingStructuralLoadCase(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class EnableEditingStructuralLoadCaseActivity(bpy.types.Operator): class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_load_case_activity" bl_idname = "bim.enable_editing_structural_load_case_groups"
bl_label = "Enable Editing Structural Load Case Activity" bl_label = "Enable Editing Structural Load Case Groups"
load_case: bpy.props.IntProperty() load_case: bpy.props.IntProperty()
def execute(self, context): def execute(self, context):
self.props = context.scene.BIMStructuralProperties self.props = context.scene.BIMStructuralProperties
self.props.active_load_case_id = self.load_case self.props.active_load_case_id = self.load_case
self.props.load_case_editing_type = "ACTIVITY" self.props.load_case_editing_type = "GROUPS"
return {"FINISHED"}
class AddStructuralLoadGroup(bpy.types.Operator):
bl_idname = "bim.add_structural_load_group"
bl_label = "Add Structural Load Group"
load_case: bpy.props.IntProperty()
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, product=load_group, group=self.file.by_id(self.load_case))
Data.load(IfcStore.get_file())
return {"FINISHED"}
class RemoveStructuralLoadGroup(bpy.types.Operator):
bl_idname = "bim.remove_structural_load_group"
bl_label = "Remove Structural Load Group"
load_group: bpy.props.IntProperty()
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"}
class EnableEditingStructuralLoadGroupActivities(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_load_group_activities"
bl_label = "Enable Editing Structural Load Group Activities"
load_group: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
self.props = context.scene.BIMStructuralProperties
self.props.active_load_group_id = self.load_group
self.props.load_group_editing_type = "ACTIVITY"
self.load_structural_activities()
return {"FINISHED"}
def load_structural_activities(self):
while len(self.props.load_group_activities) > 0:
self.props.load_group_activities.remove(0)
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()
class AddStructuralActivity(bpy.types.Operator):
bl_idname = "bim.add_structural_activity"
bl_label = "Add Structural Activity"
load_group: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMStructuralProperties
self.file = IfcStore.get_file()
for obj in context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
applied_load_class = self.props.applicable_structural_activity_types
if element.is_a("IfcStructuralPointConnection"):
if applied_load_class not in ["IfcStructuralLoadSingleForce", "IfcStructuralLoadSingleDisplacement"]:
continue
ifc_class = "IfcStructuralPointAction"
elif element.is_a("IfcStructuralCurveMember"):
if applied_load_class != "IfcStructuralLoadLinearForce":
continue
ifc_class = "IfcStructuralLinearAction"
elif element.is_a("IfcStructuralSurfaceMember"):
if applied_load_class != "IfcStructuralLoadPlanarForce":
continue
ifc_class = "IfcStructuralPlanarAction"
activity = ifcopenshell.api.run(
"structural.add_structural_activity",
self.file,
ifc_class=ifc_class,
applied_load=None, # TODO
structural_member=element
)
ifcopenshell.api.run(
"group.assign_group", self.file, product=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"} return {"FINISHED"}
@@ -1,4 +1,5 @@
import bpy import bpy
from blenderbim.bim.ifc import IfcStore
from math import radians from math import radians
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -14,6 +15,28 @@ from bpy.props import (
) )
def getApplicableStructuralActivityTypes(self, context):
ifc_file = IfcStore.get_file()
element_classes = set(
[
ifc_file.by_id(o.BIMObjectProperties.ifc_definition_id).is_a()
for o in bpy.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 updateAxisAngle(self, context): def updateAxisAngle(self, context):
if not self.axis_empty: if not self.axis_empty:
return return
@@ -26,6 +49,7 @@ def updateAxisAngle(self, context):
empty.rotation_mode = "XYZ" empty.rotation_mode = "XYZ"
empty.rotation_euler[0] = radians(self.axis_angle) empty.rotation_euler[0] = radians(self.axis_angle)
def updateConnectionCS(self, context): def updateConnectionCS(self, context):
if not self.ccs_empty: if not self.ccs_empty:
return return
@@ -43,6 +67,12 @@ class StructuralAnalysisModel(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
class StructuralActivity(PropertyGroup):
name: StringProperty(name="Name")
applied_load_class: StringProperty(name="Applied Load Class")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMStructuralProperties(PropertyGroup): class BIMStructuralProperties(PropertyGroup):
structural_analysis_model_attributes: CollectionProperty( structural_analysis_model_attributes: CollectionProperty(
name="Structural Analysis Model Attributes", type=Attribute name="Structural Analysis Model Attributes", type=Attribute
@@ -54,6 +84,14 @@ class BIMStructuralProperties(PropertyGroup):
load_case_editing_type: StringProperty(name="Load Case Editing Type") load_case_editing_type: StringProperty(name="Load Case Editing Type")
load_case_attributes: CollectionProperty(name="Load Case Attributes", type=Attribute) load_case_attributes: CollectionProperty(name="Load Case Attributes", type=Attribute)
active_load_case_id: IntProperty(name="Active Load Case Id") active_load_case_id: IntProperty(name="Active Load Case Id")
load_group_editing_type: StringProperty(name="Load Group Editing Type")
# load_group_attributes: CollectionProperty(name="Load Group Attributes", type=Attribute)
active_load_group_id: IntProperty(name="Active Load Group Id")
applicable_structural_activity_types: EnumProperty(
items=getApplicableStructuralActivityTypes, name="Applicable Structural Activity Types"
)
load_group_activities: CollectionProperty(name="Load Group Activities", type=StructuralActivity)
active_load_group_activity_index: IntProperty(name="Active Load Group Activity Index")
class BIMObjectStructuralProperties(PropertyGroup): class BIMObjectStructuralProperties(PropertyGroup):
@@ -1,3 +1,4 @@
import bpy
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.structural.data import Data from ifcopenshell.api.structural.data import Data
@@ -344,13 +345,16 @@ class BIM_PT_structural_load_cases(Panel):
row.label(text=load_case["Name"] or "Unnamed", icon="CON_CLAMPTO") row.label(text=load_case["Name"] or "Unnamed", 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:
row.operator("bim.edit_structural_load_case", text="", icon="CHECKMARK") 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.disable_editing_structural_load_case", text="", icon="CANCEL") row.operator("bim.disable_editing_structural_load_case", text="", icon="CANCEL")
elif self.props.active_load_case_id: 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: else:
row.operator( row.operator(
"bim.enable_editing_structural_load_case_activity", text="", icon="GHOST_ENABLED" "bim.enable_editing_structural_load_case_groups", text="", icon="GHOST_ENABLED"
).load_case = load_case_id ).load_case = load_case_id
row.operator( row.operator(
"bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL" "bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL"
@@ -360,8 +364,8 @@ class BIM_PT_structural_load_cases(Panel):
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": if self.props.load_case_editing_type == "ATTRIBUTES":
self.draw_editable_load_case_ui() self.draw_editable_load_case_ui()
elif self.props.load_case_editing_type == "ACTIVITY": elif self.props.load_case_editing_type == "GROUPS":
self.draw_editable_load_case_activity_ui() self.draw_editable_load_case_group_ui(load_case)
def draw_editable_load_case_ui(self): def draw_editable_load_case_ui(self):
for attribute in self.props.load_case_attributes: for attribute in self.props.load_case_attributes:
@@ -375,6 +379,42 @@ class BIM_PT_structural_load_cases(Panel):
if attribute.is_optional: if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_load_case_activity_ui(self): def draw_editable_load_case_group_ui(self, load_case):
row = self.layout.row(align=True) box = self.layout.box()
row.label(text="Activities!") if not len(load_case["IsGroupedBy"]):
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]
row = box.row(align=True)
row.label(text=load_group["Name"] or "Unnamed", 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
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)
def draw_editable_load_group_activities_ui(self, layout, load_group):
row = layout.row(align=True)
row.prop(self.props, "applicable_structural_activity_types", text="")
op = row.operator("bim.add_structural_activity", text="", icon="ADD")
op.load_group = load_group["id"]
layout.template_list(
"BIM_UL_structural_activities",
"",
self.props,
"load_group_activities",
self.props,
"active_load_group_activity_index",
)
class BIM_UL_structural_activities(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=item.applied_load_class)
@@ -0,0 +1,31 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"ifc_class": "IfcStructuralPlanarAction",
"predefined_type": "CONST",
"global_or_local": "GLOBAL_COORDS",
"applied_load": None,
"structural_member": None,
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
activity = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class=self.settings["ifc_class"],
predefined_type=self.settings["predefined_type"],
)
# TODO
# activity.AppliedLoad = self.settings["applied_load"]
activity.GlobalOrLocal = self.settings["global_or_local"]
rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralActivity")
rel.RelatingElement = self.settings["structural_member"]
rel.RelatedStructuralActivity = activity
return activity
@@ -0,0 +1,26 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"name": "Unnamed",
"predefined_type": "LOAD_GROUP",
"action_type": "NOTDEFINED",
"action_source": "NOTDEFINED",
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
load_group = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class="IfcStructuralLoadGroup",
predefined_type=self.settings["predefined_type"],
name=self.settings["name"],
)
load_group.ActionType = self.settings["action_type"]
load_group.ActionSource = self.settings["action_source"]
return load_group
@@ -48,6 +48,7 @@ class Data:
cls.load_structural_load_cases() cls.load_structural_load_cases()
cls.load_structural_load_case_combinations() cls.load_structural_load_case_combinations()
cls.load_structural_load_groups() cls.load_structural_load_groups()
cls.load_structural_activities()
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
@@ -83,18 +84,12 @@ class Data:
cls.load_cases = {} cls.load_cases = {}
for case in cls._file.by_type("IfcStructuralLoadCase"): for case in cls._file.by_type("IfcStructuralLoadCase"):
# if case.IsGroupedBy:
# for rel in case.IsGroupedBy:
# for product in rel.RelatedObjects:
# cls.products.setdefault(product.id(), []).append(case.id())
data = case.get_info() data = case.get_info()
del data["OwnerHistory"] del data["OwnerHistory"]
is_grouped_by = [] is_grouped_by = []
for load_group in case.IsGroupedBy or []: for rel in case.IsGroupedBy or []:
is_grouped_by.append(load_group.id()) is_grouped_by.extend([o.id() for o in rel.RelatedObjects])
data["IsGroupedBy"] = is_grouped_by data["IsGroupedBy"] = is_grouped_by
cls.load_cases[case.id()] = data cls.load_cases[case.id()] = data
@classmethod @classmethod
@@ -121,20 +116,30 @@ class Data:
for case in cls._file.by_type("IfcStructuralLoadGroup", include_subtypes=False): for case in cls._file.by_type("IfcStructuralLoadGroup", include_subtypes=False):
if case.PredefinedType == "LOAD_COMBINATION": if case.PredefinedType == "LOAD_COMBINATION":
continue continue
# if case.IsGroupedBy:
# for rel in case.IsGroupedBy:
# for product in rel.RelatedObjects:
# cls.products.setdefault(product.id(), []).append(case.id())
data = case.get_info() data = case.get_info()
del data["OwnerHistory"] del data["OwnerHistory"]
is_grouped_by = [] is_grouped_by = []
for load_group in case.IsGroupedBy or []: for rel in case.IsGroupedBy or []:
is_grouped_by.append(load_group.id()) is_grouped_by.extend([o.id() for o in rel.RelatedObjects])
data["IsGroupedBy"] = is_grouped_by data["IsGroupedBy"] = is_grouped_by
cls.load_groups[case.id()] = data cls.load_groups[case.id()] = data
@classmethod
def load_structural_activities(cls):
cls.structural_activities = {}
for activity in cls._file.by_type("IfcStructuralActivity"):
data = activity.get_info()
del data["OwnerHistory"]
del data["ObjectPlacement"]
del data["Representation"]
data["AppliedLoad"] = data["AppliedLoad"].id() if data["AppliedLoad"] else None
data["AssignedToStructuralItem"] = None
if activity.AssignedToStructuralItem:
data["AssignedToStructuralItem"] = activity.AssignedToStructuralItem[0].RelatingElement.id()
cls.structural_activities[activity.id()] = data
@classmethod @classmethod
def load_structural_connection(cls, product_id): def load_structural_connection(cls, product_id):
# cls.connections = {} # cls.connections = {}
@@ -0,0 +1,16 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"load_group": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
# TODO: do a deep purge
for inverse in self.file.get_inverse(self.settings["load_group"]):
if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1:
self.file.remove(inverse)
self.file.remove(self.settings["load_group"])