mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-23 23:27:54 +00:00
typing
This commit is contained in:
@@ -16,11 +16,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# from datetime import date
|
from __future__ import annotations
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.ifcopenshell_wrapper as W
|
||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
@@ -29,14 +30,27 @@ from mathutils import geometry
|
|||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from bonsai.bim.ifc import IfcStore
|
from bonsai.bim.ifc import IfcStore
|
||||||
from typing import Optional, Callable, Any, Union
|
from typing import Optional, Callable, Any, Union, Iterable, TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import bonsai.bim.prop
|
||||||
|
|
||||||
|
# ImportCallback return values:
|
||||||
|
# - None - property should be imported by default workflow
|
||||||
|
# - True - setting value for imported attribute should be skipped
|
||||||
|
# - False - property should be skipped entirely from import
|
||||||
|
ImportCallback = Callable[[str, Optional[bonsai.bim.prop.Attribute], dict[str, Any], Union[bool, None]]]
|
||||||
|
# ExportCallback return values:
|
||||||
|
# - True - property should be skipped entirely from export
|
||||||
|
# - False - property should be exproted by default workflow
|
||||||
|
ExportCallback = Callable[[dict[str, Any], bonsai.bim.prop.Attribute], bool]
|
||||||
|
|
||||||
|
|
||||||
def draw_attributes(
|
def draw_attributes(
|
||||||
props: list[bpy.types.PropertyGroup],
|
props: list[bonsai.bim.prop.Attribute],
|
||||||
layout: bpy.types.UILayout,
|
layout: bpy.types.UILayout,
|
||||||
copy_operator: Optional[str] = None,
|
copy_operator: Optional[str] = None,
|
||||||
popup_active_attribute: Optional[bpy.types.PropertyGroup] = None,
|
popup_active_attribute: Optional[bonsai.bim.prop.Attribute] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""you can set attribute active in popup with `active_attribute`
|
"""you can set attribute active in popup with `active_attribute`
|
||||||
meaning you will be able to type into attribute's field without having to click
|
meaning you will be able to type into attribute's field without having to click
|
||||||
@@ -50,7 +64,7 @@ def draw_attributes(
|
|||||||
|
|
||||||
|
|
||||||
def draw_attribute(
|
def draw_attribute(
|
||||||
attribute: bpy.types.PropertyGroup, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
|
attribute: bonsai.bim.prop.Attribute, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
value_name = attribute.get_value_name()
|
value_name = attribute.get_value_name()
|
||||||
if not value_name:
|
if not value_name:
|
||||||
@@ -94,7 +108,12 @@ def draw_attribute(
|
|||||||
op.name = attribute.name
|
op.name = attribute.name
|
||||||
|
|
||||||
|
|
||||||
def import_attributes(ifc_class, props, data, callback=None):
|
def import_attributes(
|
||||||
|
ifc_class: str,
|
||||||
|
props: bpy.types.bpy_prop_collection,
|
||||||
|
data: dict[str, Any],
|
||||||
|
callback: Optional[ImportCallback] = None,
|
||||||
|
) -> None:
|
||||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||||
import_attribute(attribute, props, data, callback=callback)
|
import_attribute(attribute, props, data, callback=callback)
|
||||||
|
|
||||||
@@ -102,8 +121,8 @@ def import_attributes(ifc_class, props, data, callback=None):
|
|||||||
# A more elegant attribute importer signature, intended to supersede import_attributes
|
# A more elegant attribute importer signature, intended to supersede import_attributes
|
||||||
def import_attributes2(
|
def import_attributes2(
|
||||||
element: Union[str, ifcopenshell.entity_instance],
|
element: Union[str, ifcopenshell.entity_instance],
|
||||||
props: bpy.types.PropertyGroup,
|
props: bpy.types.bpy_prop_collection,
|
||||||
callback: Optional[Callable] = None,
|
callback: Optional[ImportCallback] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if isinstance(element, str):
|
if isinstance(element, str):
|
||||||
attributes = tool.Ifc.schema().declaration_by_name(element).as_entity().all_attributes()
|
attributes = tool.Ifc.schema().declaration_by_name(element).as_entity().all_attributes()
|
||||||
@@ -116,8 +135,14 @@ def import_attributes2(
|
|||||||
import_attribute(attribute, props, info, callback=callback)
|
import_attribute(attribute, props, info, callback=callback)
|
||||||
|
|
||||||
|
|
||||||
def import_attribute(attribute, props, data, callback=None):
|
def import_attribute(
|
||||||
|
attribute: W.attribute,
|
||||||
|
props: bpy.types.bpy_prop_collection,
|
||||||
|
data: dict[str, Any],
|
||||||
|
callback: Optional[ImportCallback] = None,
|
||||||
|
) -> None:
|
||||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||||
|
# Complex data types (aggregates and entities) are handled only by callback.
|
||||||
if isinstance(data_type, tuple) or data_type == "entity":
|
if isinstance(data_type, tuple) or data_type == "entity":
|
||||||
callback(attribute.name(), None, data) if callback else None
|
callback(attribute.name(), None, data) if callback else None
|
||||||
return
|
return
|
||||||
@@ -156,7 +181,7 @@ def import_attribute(attribute, props, data, callback=None):
|
|||||||
ATTRIBUTE_MIN_MAX_CONSTRAINTS = {"IfcMaterialLayer": {"Priority": {"value_min": 0, "value_max": 100}}}
|
ATTRIBUTE_MIN_MAX_CONSTRAINTS = {"IfcMaterialLayer": {"Priority": {"value_min": 0, "value_max": 100}}}
|
||||||
|
|
||||||
|
|
||||||
def add_attribute_min_max(attribute_blender):
|
def add_attribute_min_max(attribute_blender: bonsai.bim.prop.Attribute) -> None:
|
||||||
if attribute_blender.ifc_class in ATTRIBUTE_MIN_MAX_CONSTRAINTS:
|
if attribute_blender.ifc_class in ATTRIBUTE_MIN_MAX_CONSTRAINTS:
|
||||||
constraints = ATTRIBUTE_MIN_MAX_CONSTRAINTS[attribute_blender.ifc_class].get(attribute_blender.name, {})
|
constraints = ATTRIBUTE_MIN_MAX_CONSTRAINTS[attribute_blender.ifc_class].get(attribute_blender.name, {})
|
||||||
for constraint, value in constraints.items():
|
for constraint, value in constraints.items():
|
||||||
@@ -164,7 +189,9 @@ def add_attribute_min_max(attribute_blender):
|
|||||||
setattr(attribute_blender, constraint + "_constraint", True)
|
setattr(attribute_blender, constraint + "_constraint", True)
|
||||||
|
|
||||||
|
|
||||||
def add_attribute_enum_items_descriptions(attribute_blender, enum_items):
|
def add_attribute_enum_items_descriptions(
|
||||||
|
attribute_blender: bonsai.bim.prop.Attribute, enum_items: Iterable[str]
|
||||||
|
) -> None:
|
||||||
attribute_blender.enum_descriptions.clear()
|
attribute_blender.enum_descriptions.clear()
|
||||||
if isinstance(enum_items, dict):
|
if isinstance(enum_items, dict):
|
||||||
enum_items = enum_items.keys()
|
enum_items = enum_items.keys()
|
||||||
@@ -178,7 +205,7 @@ def add_attribute_enum_items_descriptions(attribute_blender, enum_items):
|
|||||||
new_enum_description.name = description
|
new_enum_description.name = description
|
||||||
|
|
||||||
|
|
||||||
def add_attribute_description(attribute_blender, attribute_ifc=None):
|
def add_attribute_description(attribute_blender: bonsai.bim.prop.Attribute, attribute_ifc=None):
|
||||||
if not attribute_blender.name:
|
if not attribute_blender.name:
|
||||||
return
|
return
|
||||||
version = tool.Ifc.get_schema()
|
version = tool.Ifc.get_schema()
|
||||||
@@ -196,8 +223,11 @@ def add_attribute_description(attribute_blender, attribute_ifc=None):
|
|||||||
attribute_blender.description = description
|
attribute_blender.description = description
|
||||||
|
|
||||||
|
|
||||||
def export_attributes(props, callback: Optional[Callable] = None) -> dict[str, Any]:
|
def export_attributes(
|
||||||
attributes = {}
|
props: bpy.types.bpy_prop_collection,
|
||||||
|
callback: Optional[ExportCallback] = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
attributes: dict[str, Any] = {}
|
||||||
for prop in props:
|
for prop in props:
|
||||||
is_handled_by_callback = callback(attributes, prop) if callback else False
|
is_handled_by_callback = callback(attributes, prop) if callback else False
|
||||||
if is_handled_by_callback:
|
if is_handled_by_callback:
|
||||||
@@ -206,7 +236,13 @@ def export_attributes(props, callback: Optional[Callable] = None) -> dict[str, A
|
|||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
def prop_with_search(layout, data, prop_name, should_click_ok_to_validate=False, **kwargs):
|
def prop_with_search(
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: Union[bpy.types.PropertyGroup, bpy.types.ID],
|
||||||
|
prop_name: str,
|
||||||
|
should_click_ok_to_validate: bool = False,
|
||||||
|
**kwargs: Any,
|
||||||
|
):
|
||||||
# kwargs are layout.prop arguments (text, icon, etc.)
|
# kwargs are layout.prop arguments (text, icon, etc.)
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(data, prop_name, **kwargs)
|
row.prop(data, prop_name, **kwargs)
|
||||||
@@ -221,7 +257,11 @@ def prop_with_search(layout, data, prop_name, should_click_ok_to_validate=False,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_enum_items(data, prop_name, context=None):
|
def get_enum_items(
|
||||||
|
data: Union[bpy.types.PropertyGroup, bpy.types.ID], prop_name: str, context: Optional[bpy.types.Context] = None
|
||||||
|
) -> Union[
|
||||||
|
Iterable[Union[tuple[str, str, str], tuple[str, str, str, int], tuple[str, str, str, str, int], None]], None
|
||||||
|
]:
|
||||||
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
||||||
# Or throws an error in the console when the items callback returns an empty list
|
# Or throws an error in the console when the items callback returns an empty list
|
||||||
# See https://blender.stackexchange.com/q/215781/86891
|
# See https://blender.stackexchange.com/q/215781/86891
|
||||||
@@ -235,7 +275,7 @@ def get_enum_items(data, prop_name, context=None):
|
|||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def convert_property_group_from_si(property_group, skip_props=()):
|
def convert_property_group_from_si(property_group: bpy.types.PropertyGroup, skip_props: tuple[str, ...] = ()) -> None:
|
||||||
"""Method converts property group values from si to current ifc project units
|
"""Method converts property group values from si to current ifc project units
|
||||||
|
|
||||||
based on default values of the properties.
|
based on default values of the properties.
|
||||||
@@ -254,7 +294,7 @@ def convert_property_group_from_si(property_group, skip_props=()):
|
|||||||
setattr(property_group, prop_name, prop_value)
|
setattr(property_group, prop_name, prop_value)
|
||||||
|
|
||||||
|
|
||||||
def draw_filter(layout, filter_groups, data, module):
|
def draw_filter(layout: bpy.types.UILayout, filter_groups, data, module: str) -> None:
|
||||||
if not data.is_loaded:
|
if not data.is_loaded:
|
||||||
data.load()
|
data.load()
|
||||||
|
|
||||||
|
|||||||
@@ -1095,7 +1095,9 @@ class IfcImporter:
|
|||||||
self.mesh_data["loop_start"].extend(loop_start)
|
self.mesh_data["loop_start"].extend(loop_start)
|
||||||
# list(di1.keys())
|
# list(di1.keys())
|
||||||
|
|
||||||
def create_native_swept_disk_solid(self, element, mesh_name, native_data):
|
def create_native_swept_disk_solid(
|
||||||
|
self, element: ifcopenshell.entity_instance, mesh_name: str, native_data: dict[str, Any]
|
||||||
|
) -> bpy.types.Curve:
|
||||||
# TODO: georeferencing?
|
# TODO: georeferencing?
|
||||||
curve = bpy.data.curves.new(mesh_name, type="CURVE")
|
curve = bpy.data.curves.new(mesh_name, type="CURVE")
|
||||||
curve.dimensions = "3D"
|
curve.dimensions = "3D"
|
||||||
|
|||||||
@@ -16,8 +16,22 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
def add_context(ifc, context_type=None, context_identifier=None, target_view=None, parent=None):
|
if TYPE_CHECKING:
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import bonsai.tool as tool
|
||||||
|
|
||||||
|
|
||||||
|
def add_context(
|
||||||
|
ifc: tool.Ifc,
|
||||||
|
context_type: Optional[str] = None,
|
||||||
|
context_identifier: Optional[str] = None,
|
||||||
|
target_view: Optional[str] = None,
|
||||||
|
parent: Optional[str] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
return ifc.run(
|
return ifc.run(
|
||||||
"context.add_context",
|
"context.add_context",
|
||||||
context_type=context_type,
|
context_type=context_type,
|
||||||
@@ -27,19 +41,19 @@ def add_context(ifc, context_type=None, context_identifier=None, target_view=Non
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def remove_context(ifc, context=None):
|
def remove_context(ifc: tool.Ifc, context: ifcopenshell.entity_instance) -> None:
|
||||||
ifc.run("context.remove_context", context=context)
|
ifc.run("context.remove_context", context=context)
|
||||||
|
|
||||||
|
|
||||||
def enable_editing_context(context_tool, context=None):
|
def enable_editing_context(context_tool: tool.Context, context: ifcopenshell.entity_instance) -> None:
|
||||||
context_tool.set_context(context)
|
context_tool.set_context(context)
|
||||||
context_tool.import_attributes()
|
context_tool.import_attributes()
|
||||||
|
|
||||||
|
|
||||||
def disable_editing_context(context):
|
def disable_editing_context(context: tool.Context) -> None:
|
||||||
context.clear_context()
|
context.clear_context()
|
||||||
|
|
||||||
|
|
||||||
def edit_context(ifc, context):
|
def edit_context(ifc: tool.Ifc, context: tool.Context) -> None:
|
||||||
ifc.run("context.edit_context", context=context.get_context(), attributes=context.export_attributes())
|
ifc.run("context.edit_context", context=context.get_context(), attributes=context.export_attributes())
|
||||||
disable_editing_context(context)
|
disable_editing_context(context)
|
||||||
|
|||||||
@@ -20,20 +20,22 @@ import bpy
|
|||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Any, Union
|
||||||
|
|
||||||
|
|
||||||
class Context(bonsai.core.tool.Context):
|
class Context(bonsai.core.tool.Context):
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_context(cls, context):
|
def set_context(cls, context: ifcopenshell.entity_instance) -> None:
|
||||||
bpy.context.scene.BIMContextProperties.active_context_id = context.id()
|
bpy.context.scene.BIMContextProperties.active_context_id = context.id()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_attributes(cls):
|
def import_attributes(cls) -> None:
|
||||||
props = bpy.context.scene.BIMContextProperties
|
props = bpy.context.scene.BIMContextProperties
|
||||||
props.context_attributes.clear()
|
props.context_attributes.clear()
|
||||||
context = cls.get_context()
|
context = cls.get_context()
|
||||||
|
|
||||||
def callback(name, prop, data):
|
def callback(name: str, prop, data) -> Union[bool, None]:
|
||||||
if context.is_a("IfcGeometricRepresentationSubContext"):
|
if context.is_a("IfcGeometricRepresentationSubContext"):
|
||||||
if name == "Precision":
|
if name == "Precision":
|
||||||
props.context_attributes.remove(props.context_attributes.find("Precision"))
|
props.context_attributes.remove(props.context_attributes.find("Precision"))
|
||||||
@@ -45,13 +47,13 @@ class Context(bonsai.core.tool.Context):
|
|||||||
bonsai.bim.helper.import_attributes(context.is_a(), props.context_attributes, context.get_info(), callback)
|
bonsai.bim.helper.import_attributes(context.is_a(), props.context_attributes, context.get_info(), callback)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_context(cls):
|
def clear_context(cls) -> None:
|
||||||
bpy.context.scene.BIMContextProperties.active_context_id = 0
|
bpy.context.scene.BIMContextProperties.active_context_id = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_context(cls):
|
def get_context(cls) -> ifcopenshell.entity_instance:
|
||||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMContextProperties.active_context_id)
|
return tool.Ifc.get().by_id(bpy.context.scene.BIMContextProperties.active_context_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_attributes(cls):
|
def export_attributes(cls) -> dict[str, Any]:
|
||||||
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMContextProperties.context_attributes)
|
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMContextProperties.context_attributes)
|
||||||
|
|||||||
Reference in New Issue
Block a user