mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
Structural UI - refactor common code to tool, fix bugs
Also revert previous change in UI - `draw_attributes` doesn't cover it and there indeed should be special handling.
This commit is contained in:
@@ -23,14 +23,13 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.api.aggregate
|
import ifcopenshell.api.aggregate
|
||||||
import ifcopenshell.api.group
|
import ifcopenshell.api.group
|
||||||
import ifcopenshell.api.structural
|
import ifcopenshell.api.structural
|
||||||
import ifcopenshell.util.attribute
|
|
||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
import bonsai.core.structural as core
|
import bonsai.core.structural as core
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from math import degrees
|
from math import degrees
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bonsai.bim.module.structural.decorator import LoadsDecorator
|
from bonsai.bim.module.structural.decorator import LoadsDecorator
|
||||||
from typing import Literal
|
from typing import Literal, Any, TYPE_CHECKING
|
||||||
|
|
||||||
|
|
||||||
class ShowLoads(bpy.types.Operator):
|
class ShowLoads(bpy.types.Operator):
|
||||||
@@ -175,39 +174,17 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
||||||
bl_label = "Enable Editing Structural Boundary Condition"
|
bl_label = "Enable Editing Structural Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
boundary_condition: bpy.props.IntProperty()
|
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
boundary_condition: int
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
assert obj
|
assert obj
|
||||||
props = tool.Structural.get_object_structural_props(obj)
|
props = tool.Structural.get_object_structural_props(obj)
|
||||||
props.boundary_condition_attributes.clear()
|
|
||||||
|
|
||||||
condition = tool.Ifc.get().by_id(self.boundary_condition)
|
condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||||
schema = tool.Ifc.schema()
|
tool.Structural.import_boundary_condition_attributes(condition, props)
|
||||||
|
|
||||||
for attribute in 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()
|
|
||||||
new.is_null = value is None
|
|
||||||
new.is_optional = attribute.optional()
|
|
||||||
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 value
|
|
||||||
new.data_type = "bool"
|
|
||||||
new.enum_value = "IfcBoolean"
|
|
||||||
elif isinstance(value, float):
|
|
||||||
new.float_value = 0.0 if new.is_null else value
|
|
||||||
new.data_type = "float"
|
|
||||||
new.enum_value = next(i for i in enum_items if i != "IfcBoolean")
|
|
||||||
elif data_type == "string":
|
|
||||||
new.string_value = "" if new.is_null else value
|
|
||||||
new.data_type = "string"
|
|
||||||
|
|
||||||
props.active_boundary_condition = self.boundary_condition
|
props.active_boundary_condition = self.boundary_condition
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -216,7 +193,10 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.edit_structural_boundary_condition"
|
bl_idname = "bim.edit_structural_boundary_condition"
|
||||||
bl_label = "Edit Structural Boundary Condition"
|
bl_label = "Edit Structural Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
connection: bpy.props.IntProperty()
|
connection: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
connection: int
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
@@ -227,18 +207,7 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
connection = file.by_id(self.connection)
|
connection = file.by_id(self.connection)
|
||||||
condition = connection.AppliedCondition
|
condition = connection.AppliedCondition
|
||||||
|
|
||||||
attributes = {}
|
tool.Structural.export_and_apply_boundary_condition_attributes(condition, props)
|
||||||
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.structural.edit_structural_boundary_condition(file, condition=condition, attributes=attributes)
|
|
||||||
bpy.ops.bim.disable_editing_structural_boundary_condition()
|
bpy.ops.bim.disable_editing_structural_boundary_condition()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -955,36 +924,15 @@ class EnableEditingBoundaryCondition(bpy.types.Operator):
|
|||||||
bl_idname = "bim.enable_editing_boundary_condition"
|
bl_idname = "bim.enable_editing_boundary_condition"
|
||||||
bl_label = "Enable Editing Boundary Condition"
|
bl_label = "Enable Editing Boundary Condition"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
boundary_condition: bpy.props.IntProperty()
|
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
boundary_condition: int
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = tool.Structural.get_structural_props()
|
|
||||||
props.boundary_condition_attributes.clear()
|
|
||||||
|
|
||||||
boundary_condition = tool.Ifc.get().by_id(self.boundary_condition)
|
boundary_condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||||
# bonsai.bim.helper.import_attributes(data["type"], props.boundary_condition_attributes, data)
|
props = tool.Structural.get_structural_props()
|
||||||
schema = tool.Ifc.schema()
|
tool.Structural.import_boundary_condition_attributes(boundary_condition, props)
|
||||||
for attribute in 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 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 value
|
|
||||||
new.data_type = "bool"
|
|
||||||
new.enum_value = "IfcBoolean"
|
|
||||||
elif isinstance(value, float):
|
|
||||||
new.float_value = 0.0 if new.is_null else value
|
|
||||||
new.data_type = "float"
|
|
||||||
new.enum_value = next(i for i in enum_items if i != "IfcBoolean")
|
|
||||||
elif data_type == "string":
|
|
||||||
new.string_value = "" if new.is_null else value
|
|
||||||
new.data_type = "string"
|
|
||||||
props.active_boundary_condition_id = self.boundary_condition
|
props.active_boundary_condition_id = self.boundary_condition
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -1023,22 +971,8 @@ class EditBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = tool.Structural.get_structural_props()
|
props = tool.Structural.get_structural_props()
|
||||||
self.file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
# attributes = bonsai.bim.helper.export_attributes(props.boundary_condition_attributes)
|
condition = ifc_file.by_id(props.active_boundary_condition_id)
|
||||||
attributes = {}
|
tool.Structural.export_and_apply_boundary_condition_attributes(condition, props)
|
||||||
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.structural.edit_structural_boundary_condition(
|
|
||||||
self.file,
|
|
||||||
condition=self.file.by_id(props.active_boundary_condition_id),
|
|
||||||
attributes=attributes,
|
|
||||||
)
|
|
||||||
bpy.ops.bim.load_boundary_conditions()
|
bpy.ops.bim.load_boundary_conditions()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -79,7 +79,20 @@ def draw_boundary_condition_ui(
|
|||||||
def draw_boundary_condition_editable_ui(
|
def draw_boundary_condition_editable_ui(
|
||||||
layout: bpy.types.UILayout, props: Union[BIMStructuralProperties, BIMObjectStructuralProperties]
|
layout: bpy.types.UILayout, props: Union[BIMStructuralProperties, BIMObjectStructuralProperties]
|
||||||
) -> None:
|
) -> None:
|
||||||
draw_attributes(props.boundary_condition_attributes, layout)
|
# Reimplement `draw_attributes` as we need to support multiple types for the same attribute.
|
||||||
|
for attribute in props.boundary_condition_attributes:
|
||||||
|
if attribute.data_type == "string":
|
||||||
|
row = layout.row(align=True)
|
||||||
|
bonsai.bim.helper.draw_attribute(attribute, row, enable_search=True)
|
||||||
|
else:
|
||||||
|
row = 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_boundary_condition_read_only_ui(layout: bpy.types.UILayout, boundary_condition: dict[str, Any]) -> None:
|
def draw_boundary_condition_read_only_ui(layout: bpy.types.UILayout, boundary_condition: dict[str, Any]) -> None:
|
||||||
@@ -605,6 +618,7 @@ class BIM_PT_boundary_conditions(Panel):
|
|||||||
BoundaryConditionsData.load()
|
BoundaryConditionsData.load()
|
||||||
|
|
||||||
self.props = tool.Structural.get_structural_props()
|
self.props = tool.Structural.get_structural_props()
|
||||||
|
assert self.layout
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(
|
row.label(
|
||||||
@@ -636,9 +650,8 @@ class BIM_PT_boundary_conditions(Panel):
|
|||||||
"active_boundary_condition_index",
|
"active_boundary_condition_index",
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.props.active_boundary_condition_id:
|
if self.props.active_boundary_condition_id:
|
||||||
draw_boundary_condition_editable_ui(self.layout, self.props)
|
draw_boundary_condition_editable_ui(self.layout, self.props)
|
||||||
# bonsai.bim.helper.draw_attributes(self.props.boundary_condition_attributes, self.layout)
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_boundary_conditions(UIList):
|
class BIM_UL_boundary_conditions(UIList):
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ from __future__ import annotations
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api.context
|
import ifcopenshell.api.context
|
||||||
|
import ifcopenshell.api.structural
|
||||||
|
import ifcopenshell.util.attribute
|
||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import json
|
import json
|
||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
@@ -29,6 +31,7 @@ from typing import Union, Any, TYPE_CHECKING
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.structural.prop import BIMStructuralProperties, BIMObjectStructuralProperties
|
from bonsai.bim.module.structural.prop import BIMStructuralProperties, BIMObjectStructuralProperties
|
||||||
|
from ifcopenshell.api.structural.edit_structural_boundary_condition import AttributeDict
|
||||||
|
|
||||||
|
|
||||||
class Structural(bonsai.core.tool.Structural):
|
class Structural(bonsai.core.tool.Structural):
|
||||||
@@ -214,3 +217,68 @@ class Structural(bonsai.core.tool.Structural):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
return undefined_representation
|
return undefined_representation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_boundary_condition_attributes(
|
||||||
|
cls,
|
||||||
|
boundary_condition: ifcopenshell.entity_instance,
|
||||||
|
props: Union[BIMStructuralProperties, BIMObjectStructuralProperties],
|
||||||
|
) -> None:
|
||||||
|
props_attrs = props.boundary_condition_attributes
|
||||||
|
props_attrs.clear()
|
||||||
|
schema = tool.Ifc.schema()
|
||||||
|
# Don't use `import_attributes`,
|
||||||
|
# because we need to support 2 types of values for the same props.
|
||||||
|
ifc_class = boundary_condition.is_a()
|
||||||
|
entity = schema.declaration_by_name(ifc_class).as_entity()
|
||||||
|
assert entity
|
||||||
|
for attribute in entity.all_attributes():
|
||||||
|
attribute_name = attribute.name()
|
||||||
|
value = getattr(boundary_condition, attribute_name)
|
||||||
|
new = props_attrs.add()
|
||||||
|
new.name = attribute_name
|
||||||
|
new.ifc_class = ifc_class
|
||||||
|
new.is_null = value is None
|
||||||
|
new.is_optional = attribute.optional()
|
||||||
|
|
||||||
|
# Select attribute values are typically wrapped.
|
||||||
|
if isinstance(value, ifcopenshell.entity_instance):
|
||||||
|
value = value.wrappedValue
|
||||||
|
|
||||||
|
if attribute_name == "Name":
|
||||||
|
new.string_value = "" if new.is_null else value
|
||||||
|
new.data_type = "string"
|
||||||
|
else:
|
||||||
|
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 value
|
||||||
|
new.data_type = "boolean"
|
||||||
|
new.enum_value = "IfcBoolean"
|
||||||
|
else:
|
||||||
|
print(value)
|
||||||
|
new.float_value = 0 if new.is_null else value
|
||||||
|
new.data_type = "float"
|
||||||
|
new.enum_value = next(i for i in enum_items if i != "IfcBoolean")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_and_apply_boundary_condition_attributes(
|
||||||
|
cls,
|
||||||
|
boundary_condition: ifcopenshell.entity_instance,
|
||||||
|
props: Union[BIMStructuralProperties, BIMObjectStructuralProperties],
|
||||||
|
) -> None:
|
||||||
|
attributes: dict[str, AttributeDict] = {}
|
||||||
|
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}
|
||||||
|
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
ifcopenshell.api.structural.edit_structural_boundary_condition(
|
||||||
|
ifc_file, condition=boundary_condition, attributes=attributes
|
||||||
|
)
|
||||||
|
|||||||
+20
-2
@@ -16,11 +16,28 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from typing import Any
|
from typing import Any, TypedDict, Literal, Union
|
||||||
|
|
||||||
|
|
||||||
|
AttributeDict = TypedDict(
|
||||||
|
"AttributeDict",
|
||||||
|
{
|
||||||
|
"type": Union[
|
||||||
|
Literal[
|
||||||
|
"string",
|
||||||
|
"null",
|
||||||
|
],
|
||||||
|
str, # IFC Class.
|
||||||
|
],
|
||||||
|
"value": Any,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def edit_structural_boundary_condition(
|
def edit_structural_boundary_condition(
|
||||||
file: ifcopenshell.file, condition: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
file: ifcopenshell.file,
|
||||||
|
condition: ifcopenshell.entity_instance,
|
||||||
|
attributes: dict[str, AttributeDict],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Edits the attributes of an IfcBoundaryCondition
|
"""Edits the attributes of an IfcBoundaryCondition
|
||||||
|
|
||||||
@@ -29,6 +46,7 @@ def edit_structural_boundary_condition(
|
|||||||
|
|
||||||
:param condition: The IfcBoundaryCondition entity you want to edit
|
:param condition: The IfcBoundaryCondition entity you want to edit
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
|
Each value is represented by a dictionary.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
for name, data in attributes.items():
|
for name, data in attributes.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user