diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py
index 08285b5c40..e96088af94 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+import bpy
import blenderbim.tool as tool
from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore
@@ -34,16 +35,21 @@ from blenderbim.bim.module.pset.data import (
ProfilePsetsData,
WorkSchedulePsetsData,
)
+from typing import Any, Optional
-def draw_property(prop, layout, copy_operator=None):
+def draw_property(
+ prop: bpy.types.PropertyGroup, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
+) -> None:
if prop.value_type == "IfcPropertySingleValue":
draw_single_property(prop, layout, copy_operator)
elif prop.value_type == "IfcPropertyEnumeratedValue":
draw_enumerated_property(prop, layout, copy_operator)
-def draw_single_property(prop, layout, copy_operator=None):
+def draw_single_property(
+ prop: bpy.types.PropertyGroup, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
+) -> None:
value_name = prop.metadata.get_value_name(display_only=True)
if not value_name:
layout.label(text=prop["Name"])
@@ -63,7 +69,9 @@ def draw_single_property(prop, layout, copy_operator=None):
op.name = prop.metadata.name
-def draw_enumerated_property(prop, layout, copy_operator=None):
+def draw_enumerated_property(
+ prop: bpy.types.PropertyGroup, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
+) -> None:
value_name = prop.metadata.get_value_name()
if not value_name:
layout.label(text=prop.metadata.name)
@@ -78,13 +86,21 @@ def draw_enumerated_property(prop, layout, copy_operator=None):
op.name = prop.metadata.name
-def get_active_pset_obj_name(context, obj_type):
+def get_active_pset_obj_name(context: bpy.types.Context, obj_type: tool.Ifc.OBJECT_TYPE) -> str:
if obj_type in ("Object", "MaterialSet", "MaterialSetItem"):
return context.active_object.name
return ""
-def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_removing=True):
+def draw_psetqto_ui(
+ context: bpy.types.Context,
+ pset_id: int,
+ pset: dict[str, Any],
+ props: bpy.types.PropertyGroup,
+ layout: bpy.types.UILayout,
+ obj_type: tool.Ifc.OBJECT_TYPE,
+ allow_removing: bool = True,
+) -> None:
box = layout.box()
row = box.row(align=True)
if "is_expanded" not in pset:
@@ -155,7 +171,9 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
row.label(text="No Properties Set")
-def draw_psetqto_editable_ui(box, props, prop):
+def draw_psetqto_editable_ui(
+ box: bpy.types.UILayout, props: bpy.types.PropertyGroup, prop: bpy.types.PropertyGroup
+) -> None:
row = box.row(align=True)
draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
diff --git a/src/blenderbim/blenderbim/bim/module/style/data.py b/src/blenderbim/blenderbim/bim/module/style/data.py
index 7bcb05482b..0970221caa 100644
--- a/src/blenderbim/blenderbim/bim/module/style/data.py
+++ b/src/blenderbim/blenderbim/bim/module/style/data.py
@@ -18,6 +18,7 @@
import bpy
import ifcopenshell
+import ifcopenshell.util.schema
import blenderbim.tool as tool
from ifcopenshell.util.doc import get_entity_doc
from typing import Union
diff --git a/src/blenderbim/blenderbim/core/pset.py b/src/blenderbim/blenderbim/core/pset.py
index c21b59ccf5..ab2370db38 100644
--- a/src/blenderbim/blenderbim/core/pset.py
+++ b/src/blenderbim/blenderbim/core/pset.py
@@ -17,7 +17,24 @@
# along with BlenderBIM Add-on. If not, see .
-def copy_property_to_selection(ifc, pset, is_pset=True, obj=None, pset_name=None, prop_name=None, prop_value=None):
+from __future__ import annotations
+from typing import TYPE_CHECKING, Optional, Union, Any
+
+if TYPE_CHECKING:
+ import bpy
+ import ifcopenshell
+ import blenderbim.tool as tool
+
+
+def copy_property_to_selection(
+ ifc: tool.Ifc,
+ pset: tool.Pset,
+ obj: bpy.types.Object,
+ pset_name: str,
+ prop_name: str,
+ prop_value: Any,
+ is_pset: bool = True,
+) -> None:
element = ifc.get_entity(obj)
if not element:
return
@@ -30,7 +47,9 @@ def copy_property_to_selection(ifc, pset, is_pset=True, obj=None, pset_name=None
ifc.run("pset.edit_qto", qto=ifc_pset, properties={prop_name: prop_value})
-def add_pset(ifc, pset, blender, obj_name, obj_type):
+def add_pset(
+ ifc: tool.Ifc, pset: tool.Pset, blender: tool.Blender, obj_name: str, obj_type: tool.Ifc.OBJECT_TYPE
+) -> None:
pset_name = pset.get_pset_name(obj_name, obj_type, pset_type="PSET")
if obj_type == "Object":
elements = [ifc.get_entity(obj) for obj in blender.get_selected_objects()]
@@ -44,7 +63,14 @@ def add_pset(ifc, pset, blender, obj_name, obj_type):
pset.enable_pset_editing(pset_id=0, pset_name=pset_name, pset_type="PSET", obj=obj_name, obj_type=obj_type)
-def enable_pset_editing(pset_tool, pset, pset_name, pset_type, obj_name, obj_type):
+def enable_pset_editing(
+ pset_tool: tool.Pset,
+ pset: ifcopenshell.entity_instance,
+ pset_name: str,
+ pset_type: tool.Pset.PSET_TYPE,
+ obj_name: str,
+ obj_type: tool.Ifc.OBJECT_TYPE,
+) -> None:
props = pset_tool.get_pset_props(obj_name, obj_type)
pset_tool.clear_blender_pset_properties(props)
@@ -63,6 +89,6 @@ def enable_pset_editing(pset_tool, pset, pset_name, pset_type, obj_name, obj_typ
pset_tool.enable_proposed_pset(props, pset_name, pset_type, has_template)
-def add_proposed_prop(pset, obj_name, obj_type, name, value):
+def add_proposed_prop(pset: tool.Pset, obj_name: str, obj_type: tool.Ifc.OBJECT_TYPE, name: str, value: Any) -> None:
props = pset.get_pset_props(obj_name, obj_type)
pset.add_proposed_property(name, pset.cast_string_to_primitive(value), props)
diff --git a/src/blenderbim/blenderbim/tool/blender.py b/src/blenderbim/blenderbim/tool/blender.py
index 004c4cc34b..17010b1070 100644
--- a/src/blenderbim/blenderbim/tool/blender.py
+++ b/src/blenderbim/blenderbim/tool/blender.py
@@ -32,6 +32,7 @@ from mathutils import Vector
from pathlib import Path
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
from typing import Any, Optional, Union, Literal, Iterable, Callable
+from typing_extensions import assert_never
VIEWPORT_ATTRIBUTES = [
@@ -163,8 +164,14 @@ class Blender(blenderbim.core.tool.Blender):
@classmethod
def get_obj_ifc_definition_id(
- cls, obj: Optional[str] = None, obj_type: Optional[str] = None, context: Optional[bpy.types.Context] = None
+ cls,
+ obj: Optional[str] = None,
+ obj_type: Optional[tool.Ifc.OBJECT_TYPE] = None,
+ context: Optional[bpy.types.Context] = None,
) -> Union[int, None]:
+ # TODO: is it ever used as None?
+ if obj_type is None:
+ return None
if context is None:
context = bpy.context
if obj_type == "Object":
@@ -200,6 +207,7 @@ class Blender(blenderbim.core.tool.Blender):
elif obj_type == "Group":
prop = context.scene.BIMGroupProperties
return prop.groups[prop.active_group_index].ifc_definition_id
+ assert_never(obj_type)
@classmethod
def is_ifc_object(cls, obj: bpy.types.Object) -> bool:
diff --git a/src/blenderbim/blenderbim/tool/ifc.py b/src/blenderbim/blenderbim/tool/ifc.py
index 43b8e9bd46..77be4a34db 100644
--- a/src/blenderbim/blenderbim/tool/ifc.py
+++ b/src/blenderbim/blenderbim/tool/ifc.py
@@ -26,10 +26,23 @@ import blenderbim.core.tool
import blenderbim.bim.handler
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
-from typing import Optional, Union, Any, final
+from typing import Optional, Union, Any, final, Literal
class Ifc(blenderbim.core.tool.Ifc):
+ OBJECT_TYPE = Literal[
+ "Object",
+ "Material",
+ "MaterialSet",
+ "MaterialSetItem",
+ "Task",
+ "Cost",
+ "Resource",
+ "Profile",
+ "WorkSchedule",
+ "Group",
+ ]
+
@classmethod
def run(cls, command: str, **kwargs) -> Any:
return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs)
diff --git a/src/blenderbim/blenderbim/tool/pset.py b/src/blenderbim/blenderbim/tool/pset.py
index f26f2c478b..d278e20a7c 100644
--- a/src/blenderbim/blenderbim/tool/pset.py
+++ b/src/blenderbim/blenderbim/tool/pset.py
@@ -18,14 +18,18 @@
import bpy
import ifcopenshell
+import ifcopenshell.util.attribute
import ifcopenshell.util.element
import blenderbim.core.tool
import blenderbim.tool as tool
import blenderbim.bim.schema
-from typing import Union
+from typing import Union, Literal, Any
+from typing_extensions import assert_never
class Pset(blenderbim.core.tool.Pset):
+ PSET_TYPE = Literal["PSET", "QTO"]
+
@classmethod
def get_element_pset(
cls, element: ifcopenshell.entity_instance, pset_name: str
@@ -35,7 +39,7 @@ class Pset(blenderbim.core.tool.Pset):
return tool.Ifc.get().by_id(pset["id"])
@classmethod
- def get_pset_props(cls, obj, obj_type):
+ def get_pset_props(cls, obj: str, obj_type: tool.Ifc.OBJECT_TYPE) -> bpy.types.PropertyGroup:
if obj_type == "Object":
return bpy.data.objects.get(obj).PsetProperties
elif obj_type == "Material":
@@ -54,9 +58,10 @@ class Pset(blenderbim.core.tool.Pset):
return bpy.context.scene.WorkSchedulePsetProperties
elif obj_type == "Group":
return bpy.context.scene.GroupPsetProperties
+ assert_never(obj_type)
@classmethod
- def get_pset_name(cls, obj, obj_type, pset_type="PSET"):
+ def get_pset_name(cls, obj: str, obj_type: tool.Ifc.OBJECT_TYPE, pset_type: PSET_TYPE = "PSET") -> str:
props = cls.get_pset_props(obj, obj_type)
name = props.pset_name if pset_type == "PSET" else props.qto_name
if name == "BBIM_CUSTOM":
@@ -73,7 +78,7 @@ class Pset(blenderbim.core.tool.Pset):
)
@classmethod
- def is_pset_empty(cls, pset):
+ def is_pset_empty(cls, pset: ifcopenshell.entity_instance) -> bool:
pset_dict = ifcopenshell.util.element.get_property_definition(pset)
del pset_dict["id"]
for value in pset_dict.values():
@@ -82,14 +87,16 @@ class Pset(blenderbim.core.tool.Pset):
return True
@classmethod
- def enable_pset_editing(cls, pset_id=None, pset_name=None, pset_type=None, obj=None, obj_type=None):
- # TODO REFACTOR ONCE toll/CORE functions are available
+ def enable_pset_editing(
+ cls, pset_id: int, pset_name: str, pset_type: PSET_TYPE, obj: str, obj_type: tool.Ifc.OBJECT_TYPE
+ ) -> None:
+ # TODO REFACTOR ONCE tool/CORE functions are available
bpy.ops.bim.enable_pset_editing(
pset_id=0, pset_name=cls.get_pset_name(obj, obj_type), pset_type="PSET", obj=obj, obj_type=obj_type
)
@classmethod
- def import_pset_from_existing(cls, pset, props):
+ def import_pset_from_existing(cls, pset: ifcopenshell.entity_instance, props: bpy.types.PropertyGroup) -> None:
pset_props = []
if pset.is_a("IfcElementQuantity"):
pset_props = pset.Quantities
@@ -134,7 +141,7 @@ class Pset(blenderbim.core.tool.Pset):
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
@classmethod
- def get_prop_template_primitive_type(cls, prop_template):
+ def get_prop_template_primitive_type(cls, prop_template: ifcopenshell.entity_instance) -> str:
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
return "float"
elif prop_template.TemplateType == "Q_COUNT":
@@ -144,7 +151,9 @@ class Pset(blenderbim.core.tool.Pset):
)
@classmethod
- def import_enumerated_value_from_template(cls, prop_template, data, props):
+ def import_enumerated_value_from_template(
+ cls, prop_template: ifcopenshell.entity_instance, data: dict[str, Any], props: bpy.types.PropertyGroup
+ ) -> None:
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
selected_enum_items = data.get(prop_template.Name, []) or []
@@ -167,7 +176,13 @@ class Pset(blenderbim.core.tool.Pset):
new.is_selected = enum in selected_enum_items
@classmethod
- def import_single_value_from_template(cls, pset_template, prop_template, data, props):
+ def import_single_value_from_template(
+ cls,
+ pset_template: ifcopenshell.entity_instance,
+ prop_template: ifcopenshell.entity_instance,
+ data: dict[str, Any],
+ props: bpy.types.PropertyGroup,
+ ) -> None:
prop = props.properties.add()
prop.name = prop_template.Name
prop.value_type = "IfcPropertySingleValue"
@@ -207,7 +222,12 @@ class Pset(blenderbim.core.tool.Pset):
blenderbim.bim.helper.add_attribute_description(metadata, prop_template)
@classmethod
- def import_pset_from_template(cls, pset_template, pset, props):
+ def import_pset_from_template(
+ cls,
+ pset_template: ifcopenshell.entity_instance,
+ pset: Union[ifcopenshell.entity_instance, None],
+ props: bpy.types.PropertyGroup,
+ ) -> None:
if pset:
data = ifcopenshell.util.element.get_property_definition(pset)
del data["id"]
@@ -231,28 +251,32 @@ class Pset(blenderbim.core.tool.Pset):
pass
@classmethod
- def clear_blender_pset_properties(cls, props):
+ def clear_blender_pset_properties(cls, props: bpy.types.PropertyGroup) -> None:
props.properties.clear()
@classmethod
- def set_active_pset(cls, props, pset, has_template):
+ def set_active_pset(
+ cls, props: bpy.types.PropertyGroup, pset: ifcopenshell.entity_instance, has_template: bool
+ ) -> None:
props.active_pset_id = pset.id()
props.active_pset_name = pset.Name
props.active_pset_has_template = has_template
@classmethod
- def enable_proposed_pset(cls, props, pset_name, pset_type, has_template):
+ def enable_proposed_pset(
+ cls, props: bpy.types.PropertyGroup, pset_name: str, pset_type: PSET_TYPE, has_template: bool
+ ) -> None:
props.active_pset_id = 0
props.active_pset_name = pset_name or "My_Data"
props.active_pset_type = pset_type
props.active_pset_has_template = has_template
@classmethod
- def get_pset_template(cls, name):
+ def get_pset_template(cls, name: str) -> Union[ifcopenshell.entity_instance, None]:
return blenderbim.bim.schema.ifc.psetqto.get_by_name(name)
@classmethod
- def add_proposed_property(cls, name, value, props):
+ def add_proposed_property(cls, name: str, value: Any, props: bpy.types.PropertyGroup) -> None:
if props.properties.get(name):
return
prop = props.properties.add()
@@ -265,7 +289,7 @@ class Pset(blenderbim.core.tool.Pset):
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
@classmethod
- def cast_string_to_primitive(cls, value: str):
+ def cast_string_to_primitive(cls, value: str) -> Any:
value = value.strip()
if value.lower() == "true":
return True
diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py
index 29ba26e92b..8fd68a4c62 100644
--- a/src/ifcopenshell-python/ifcopenshell/geom/main.py
+++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py
@@ -33,6 +33,8 @@ from typing import TypeVar, Union, Optional, Generator, Any, Literal, overload,
if TYPE_CHECKING:
from OCC.Core import TopoDS
+ IteratorOutput = Union["ShapeElementType", "utils.shape_tuple"]
+
T = TypeVar("T")
ShapeElementType = Union[
ifcopenshell_wrapper.BRepElement, ifcopenshell_wrapper.TriangulationElement, ifcopenshell_wrapper.SerializedElement
@@ -273,7 +275,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
def get(self):
return wrap_shape_creation(self.settings, ifcopenshell_wrapper.Iterator.get(self))
- def __iter__(self) -> Generator[ShapeElementType, None, None]:
+ def __iter__(self) -> Generator[IteratorOutput, None, None]:
if self.initialize():
while True:
yield self.get()
@@ -412,7 +414,19 @@ def create_shape(
)
-def consume_iterator(it, with_progress=False):
+@overload
+def consume_iterator(it: iterator, with_progress: Literal[False] = False) -> Generator[IteratorOutput, None, None]: ...
+@overload
+def consume_iterator(
+ it: iterator, with_progress: Literal[True]
+) -> Generator[tuple[int, IteratorOutput], None, None]: ...
+@overload
+def consume_iterator(
+ it: iterator, with_progress: bool
+) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]: ...
+def consume_iterator(
+ it: iterator, with_progress: bool = False
+) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]:
if it.initialize():
while True:
if with_progress:
@@ -423,16 +437,49 @@ def consume_iterator(it, with_progress=False):
break
+@overload
def iterate(
- settings,
- file_or_filename,
- num_threads=1,
- include=None,
- exclude=None,
- with_progress=False,
- cache=None,
+ settings: settings,
+ file_or_filename: Union[file, str],
+ num_threads: int = 1,
+ include: Optional[Union[list[entity_instance], list[str]]] = None,
+ exclude: Optional[Union[list[entity_instance], list[str]]] = None,
+ with_progress: Literal[False] = False,
+ cache: Optional[serializers.hdf5] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade",
-):
+) -> Generator[IteratorOutput, None, None]: ...
+@overload
+def iterate(
+ settings: settings,
+ file_or_filename: Union[file, str],
+ num_threads: int = 1,
+ include: Optional[Union[list[entity_instance], list[str]]] = None,
+ exclude: Optional[Union[list[entity_instance], list[str]]] = None,
+ with_progress: Literal[True] = True,
+ cache: Optional[serializers.hdf5] = None,
+ geometry_library: GEOMETRY_LIBRARY = "opencascade",
+) -> Generator[tuple[int, IteratorOutput], None, None]: ...
+@overload
+def iterate(
+ settings: settings,
+ file_or_filename: Union[file, str],
+ num_threads: int = 1,
+ include: Optional[Union[list[entity_instance], list[str]]] = None,
+ exclude: Optional[Union[list[entity_instance], list[str]]] = None,
+ with_progress: bool = False,
+ cache: Optional[serializers.hdf5] = None,
+ geometry_library: GEOMETRY_LIBRARY = "opencascade",
+) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]: ...
+def iterate(
+ settings: settings,
+ file_or_filename: Union[file, str],
+ num_threads: int = 1,
+ include: Optional[Union[list[entity_instance], list[str]]] = None,
+ exclude: Optional[Union[list[entity_instance], list[str]]] = None,
+ with_progress: bool = False,
+ cache: Optional[serializers.hdf5] = None,
+ geometry_library: GEOMETRY_LIBRARY = "opencascade",
+) -> Generator[Union[IteratorOutput, tuple[int, IteratorOutput]], None, None]:
it = iterator(settings, file_or_filename, num_threads, include, exclude, geometry_library)
if cache:
hdf5_cache = serializers.hdf5(cache, settings)
diff --git a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py
index b5e8b1ccae..d8ba66d093 100644
--- a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py
+++ b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py
@@ -40,6 +40,7 @@ except ImportError:
class shape_tuple(NamedTuple):
+ """A tuple containing IfcOpenShell serialized element/shape and pythonOCC shape."""
data: Union[ifcopenshell_wrapper.SerializedElement, ifcopenshell_wrapper.Serialization]
geometry: TopoDS.TopoDS_Shape
styles: tuple[tuple[float, float, float, float], ...]