mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +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.group
|
||||
import ifcopenshell.api.structural
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.core.structural as core
|
||||
import bonsai.tool as tool
|
||||
from math import degrees
|
||||
from mathutils import Vector, Matrix
|
||||
from bonsai.bim.module.structural.decorator import LoadsDecorator
|
||||
from typing import Literal
|
||||
from typing import Literal, Any, TYPE_CHECKING
|
||||
|
||||
|
||||
class ShowLoads(bpy.types.Operator):
|
||||
@@ -175,39 +174,17 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
||||
bl_label = "Enable Editing Structural Boundary Condition"
|
||||
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):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.boundary_condition_attributes.clear()
|
||||
|
||||
condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||
schema = tool.Ifc.schema()
|
||||
|
||||
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"
|
||||
|
||||
tool.Structural.import_boundary_condition_attributes(condition, props)
|
||||
props.active_boundary_condition = self.boundary_condition
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -216,7 +193,10 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_structural_boundary_condition"
|
||||
bl_label = "Edit Structural Boundary Condition"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
connection: bpy.props.IntProperty()
|
||||
connection: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
connection: int
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
@@ -227,18 +207,7 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
connection = file.by_id(self.connection)
|
||||
condition = connection.AppliedCondition
|
||||
|
||||
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.structural.edit_structural_boundary_condition(file, condition=condition, attributes=attributes)
|
||||
tool.Structural.export_and_apply_boundary_condition_attributes(condition, props)
|
||||
bpy.ops.bim.disable_editing_structural_boundary_condition()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -955,36 +924,15 @@ class EnableEditingBoundaryCondition(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_boundary_condition"
|
||||
bl_label = "Enable Editing Boundary Condition"
|
||||
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):
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.boundary_condition_attributes.clear()
|
||||
|
||||
boundary_condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||
# bonsai.bim.helper.import_attributes(data["type"], props.boundary_condition_attributes, data)
|
||||
schema = tool.Ifc.schema()
|
||||
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 = tool.Structural.get_structural_props()
|
||||
tool.Structural.import_boundary_condition_attributes(boundary_condition, props)
|
||||
props.active_boundary_condition_id = self.boundary_condition
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1023,22 +971,8 @@ class EditBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = tool.Structural.get_structural_props()
|
||||
self.file = tool.Ifc.get()
|
||||
# attributes = bonsai.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.structural.edit_structural_boundary_condition(
|
||||
self.file,
|
||||
condition=self.file.by_id(props.active_boundary_condition_id),
|
||||
attributes=attributes,
|
||||
)
|
||||
ifc_file = tool.Ifc.get()
|
||||
condition = ifc_file.by_id(props.active_boundary_condition_id)
|
||||
tool.Structural.export_and_apply_boundary_condition_attributes(condition, props)
|
||||
bpy.ops.bim.load_boundary_conditions()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -79,7 +79,20 @@ def draw_boundary_condition_ui(
|
||||
def draw_boundary_condition_editable_ui(
|
||||
layout: bpy.types.UILayout, props: Union[BIMStructuralProperties, BIMObjectStructuralProperties]
|
||||
) -> 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:
|
||||
@@ -605,6 +618,7 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
BoundaryConditionsData.load()
|
||||
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
assert self.layout
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
@@ -636,9 +650,8 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
"active_boundary_condition_index",
|
||||
)
|
||||
|
||||
if self.props.active_boundary_condition_id:
|
||||
draw_boundary_condition_editable_ui(self.layout, self.props)
|
||||
# bonsai.bim.helper.draw_attributes(self.props.boundary_condition_attributes, self.layout)
|
||||
if self.props.active_boundary_condition_id:
|
||||
draw_boundary_condition_editable_ui(self.layout, self.props)
|
||||
|
||||
|
||||
class BIM_UL_boundary_conditions(UIList):
|
||||
|
||||
@@ -20,6 +20,8 @@ from __future__ import annotations
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.context
|
||||
import ifcopenshell.api.structural
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.representation
|
||||
import json
|
||||
import bonsai.bim.helper
|
||||
@@ -29,6 +31,7 @@ from typing import Union, Any, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
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):
|
||||
@@ -214,3 +217,68 @@ class Structural(bonsai.core.tool.Structural):
|
||||
return None
|
||||
|
||||
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
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
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(
|
||||
file: ifcopenshell.file, condition: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
file: ifcopenshell.file,
|
||||
condition: ifcopenshell.entity_instance,
|
||||
attributes: dict[str, AttributeDict],
|
||||
) -> None:
|
||||
"""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 attributes: a dictionary of attribute names and values.
|
||||
Each value is represented by a dictionary.
|
||||
:return: None
|
||||
"""
|
||||
for name, data in attributes.items():
|
||||
|
||||
Reference in New Issue
Block a user