This commit is contained in:
Andrej730
2024-07-27 12:27:23 +05:00
parent e0790601df
commit a0bda1afd3
8 changed files with 177 additions and 39 deletions
@@ -16,6 +16,7 @@
# 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool import blenderbim.tool as tool
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -34,16 +35,21 @@ from blenderbim.bim.module.pset.data import (
ProfilePsetsData, ProfilePsetsData,
WorkSchedulePsetsData, 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": if prop.value_type == "IfcPropertySingleValue":
draw_single_property(prop, layout, copy_operator) draw_single_property(prop, layout, copy_operator)
elif prop.value_type == "IfcPropertyEnumeratedValue": elif prop.value_type == "IfcPropertyEnumeratedValue":
draw_enumerated_property(prop, layout, copy_operator) 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) value_name = prop.metadata.get_value_name(display_only=True)
if not value_name: if not value_name:
layout.label(text=prop["Name"]) layout.label(text=prop["Name"])
@@ -63,7 +69,9 @@ def draw_single_property(prop, layout, copy_operator=None):
op.name = prop.metadata.name 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() value_name = prop.metadata.get_value_name()
if not value_name: if not value_name:
layout.label(text=prop.metadata.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 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"): if obj_type in ("Object", "MaterialSet", "MaterialSetItem"):
return context.active_object.name return context.active_object.name
return "" 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() box = layout.box()
row = box.row(align=True) row = box.row(align=True)
if "is_expanded" not in pset: 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") 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) row = box.row(align=True)
draw_property(prop, row, copy_operator="bim.copy_property_to_selection") draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
@@ -18,6 +18,7 @@
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.schema
import blenderbim.tool as tool import blenderbim.tool as tool
from ifcopenshell.util.doc import get_entity_doc from ifcopenshell.util.doc import get_entity_doc
from typing import Union from typing import Union
+30 -4
View File
@@ -17,7 +17,24 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
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) element = ifc.get_entity(obj)
if not element: if not element:
return 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}) 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") pset_name = pset.get_pset_name(obj_name, obj_type, pset_type="PSET")
if obj_type == "Object": if obj_type == "Object":
elements = [ifc.get_entity(obj) for obj in blender.get_selected_objects()] 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) 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) props = pset_tool.get_pset_props(obj_name, obj_type)
pset_tool.clear_blender_pset_properties(props) 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) 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) props = pset.get_pset_props(obj_name, obj_type)
pset.add_proposed_property(name, pset.cast_string_to_primitive(value), props) pset.add_proposed_property(name, pset.cast_string_to_primitive(value), props)
+9 -1
View File
@@ -32,6 +32,7 @@ from mathutils import Vector
from pathlib import Path from pathlib import Path
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
from typing import Any, Optional, Union, Literal, Iterable, Callable from typing import Any, Optional, Union, Literal, Iterable, Callable
from typing_extensions import assert_never
VIEWPORT_ATTRIBUTES = [ VIEWPORT_ATTRIBUTES = [
@@ -163,8 +164,14 @@ class Blender(blenderbim.core.tool.Blender):
@classmethod @classmethod
def get_obj_ifc_definition_id( 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]: ) -> Union[int, None]:
# TODO: is it ever used as None?
if obj_type is None:
return None
if context is None: if context is None:
context = bpy.context context = bpy.context
if obj_type == "Object": if obj_type == "Object":
@@ -200,6 +207,7 @@ class Blender(blenderbim.core.tool.Blender):
elif obj_type == "Group": elif obj_type == "Group":
prop = context.scene.BIMGroupProperties prop = context.scene.BIMGroupProperties
return prop.groups[prop.active_group_index].ifc_definition_id return prop.groups[prop.active_group_index].ifc_definition_id
assert_never(obj_type)
@classmethod @classmethod
def is_ifc_object(cls, obj: bpy.types.Object) -> bool: def is_ifc_object(cls, obj: bpy.types.Object) -> bool:
+14 -1
View File
@@ -26,10 +26,23 @@ import blenderbim.core.tool
import blenderbim.bim.handler import blenderbim.bim.handler
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore, IFC_CONNECTED_TYPE 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): class Ifc(blenderbim.core.tool.Ifc):
OBJECT_TYPE = Literal[
"Object",
"Material",
"MaterialSet",
"MaterialSetItem",
"Task",
"Cost",
"Resource",
"Profile",
"WorkSchedule",
"Group",
]
@classmethod @classmethod
def run(cls, command: str, **kwargs) -> Any: def run(cls, command: str, **kwargs) -> Any:
return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs) return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs)
+41 -17
View File
@@ -18,14 +18,18 @@
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.attribute
import ifcopenshell.util.element import ifcopenshell.util.element
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.schema 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): class Pset(blenderbim.core.tool.Pset):
PSET_TYPE = Literal["PSET", "QTO"]
@classmethod @classmethod
def get_element_pset( def get_element_pset(
cls, element: ifcopenshell.entity_instance, pset_name: str 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"]) return tool.Ifc.get().by_id(pset["id"])
@classmethod @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": if obj_type == "Object":
return bpy.data.objects.get(obj).PsetProperties return bpy.data.objects.get(obj).PsetProperties
elif obj_type == "Material": elif obj_type == "Material":
@@ -54,9 +58,10 @@ class Pset(blenderbim.core.tool.Pset):
return bpy.context.scene.WorkSchedulePsetProperties return bpy.context.scene.WorkSchedulePsetProperties
elif obj_type == "Group": elif obj_type == "Group":
return bpy.context.scene.GroupPsetProperties return bpy.context.scene.GroupPsetProperties
assert_never(obj_type)
@classmethod @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) props = cls.get_pset_props(obj, obj_type)
name = props.pset_name if pset_type == "PSET" else props.qto_name name = props.pset_name if pset_type == "PSET" else props.qto_name
if name == "BBIM_CUSTOM": if name == "BBIM_CUSTOM":
@@ -73,7 +78,7 @@ class Pset(blenderbim.core.tool.Pset):
) )
@classmethod @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) pset_dict = ifcopenshell.util.element.get_property_definition(pset)
del pset_dict["id"] del pset_dict["id"]
for value in pset_dict.values(): for value in pset_dict.values():
@@ -82,14 +87,16 @@ class Pset(blenderbim.core.tool.Pset):
return True return True
@classmethod @classmethod
def enable_pset_editing(cls, pset_id=None, pset_name=None, pset_type=None, obj=None, obj_type=None): def enable_pset_editing(
# TODO REFACTOR ONCE toll/CORE functions are available 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( 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 pset_id=0, pset_name=cls.get_pset_name(obj, obj_type), pset_type="PSET", obj=obj, obj_type=obj_type
) )
@classmethod @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 = [] pset_props = []
if pset.is_a("IfcElementQuantity"): if pset.is_a("IfcElementQuantity"):
pset_props = pset.Quantities 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) metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
@classmethod @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"]: if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
return "float" return "float"
elif prop_template.TemplateType == "Q_COUNT": elif prop_template.TemplateType == "Q_COUNT":
@@ -144,7 +151,9 @@ class Pset(blenderbim.core.tool.Pset):
) )
@classmethod @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] enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
selected_enum_items = data.get(prop_template.Name, []) or [] 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 new.is_selected = enum in selected_enum_items
@classmethod @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 = props.properties.add()
prop.name = prop_template.Name prop.name = prop_template.Name
prop.value_type = "IfcPropertySingleValue" prop.value_type = "IfcPropertySingleValue"
@@ -207,7 +222,12 @@ class Pset(blenderbim.core.tool.Pset):
blenderbim.bim.helper.add_attribute_description(metadata, prop_template) blenderbim.bim.helper.add_attribute_description(metadata, prop_template)
@classmethod @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: if pset:
data = ifcopenshell.util.element.get_property_definition(pset) data = ifcopenshell.util.element.get_property_definition(pset)
del data["id"] del data["id"]
@@ -231,28 +251,32 @@ class Pset(blenderbim.core.tool.Pset):
pass pass
@classmethod @classmethod
def clear_blender_pset_properties(cls, props): def clear_blender_pset_properties(cls, props: bpy.types.PropertyGroup) -> None:
props.properties.clear() props.properties.clear()
@classmethod @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_id = pset.id()
props.active_pset_name = pset.Name props.active_pset_name = pset.Name
props.active_pset_has_template = has_template props.active_pset_has_template = has_template
@classmethod @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_id = 0
props.active_pset_name = pset_name or "My_Data" props.active_pset_name = pset_name or "My_Data"
props.active_pset_type = pset_type props.active_pset_type = pset_type
props.active_pset_has_template = has_template props.active_pset_has_template = has_template
@classmethod @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) return blenderbim.bim.schema.ifc.psetqto.get_by_name(name)
@classmethod @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): if props.properties.get(name):
return return
prop = props.properties.add() 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) metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
@classmethod @classmethod
def cast_string_to_primitive(cls, value: str): def cast_string_to_primitive(cls, value: str) -> Any:
value = value.strip() value = value.strip()
if value.lower() == "true": if value.lower() == "true":
return True return True
@@ -33,6 +33,8 @@ from typing import TypeVar, Union, Optional, Generator, Any, Literal, overload,
if TYPE_CHECKING: if TYPE_CHECKING:
from OCC.Core import TopoDS from OCC.Core import TopoDS
IteratorOutput = Union["ShapeElementType", "utils.shape_tuple"]
T = TypeVar("T") T = TypeVar("T")
ShapeElementType = Union[ ShapeElementType = Union[
ifcopenshell_wrapper.BRepElement, ifcopenshell_wrapper.TriangulationElement, ifcopenshell_wrapper.SerializedElement ifcopenshell_wrapper.BRepElement, ifcopenshell_wrapper.TriangulationElement, ifcopenshell_wrapper.SerializedElement
@@ -273,7 +275,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
def get(self): def get(self):
return wrap_shape_creation(self.settings, ifcopenshell_wrapper.Iterator.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(): if self.initialize():
while True: while True:
yield self.get() 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(): if it.initialize():
while True: while True:
if with_progress: if with_progress:
@@ -423,16 +437,49 @@ def consume_iterator(it, with_progress=False):
break break
@overload
def iterate( def iterate(
settings, settings: settings,
file_or_filename, file_or_filename: Union[file, str],
num_threads=1, num_threads: int = 1,
include=None, include: Optional[Union[list[entity_instance], list[str]]] = None,
exclude=None, exclude: Optional[Union[list[entity_instance], list[str]]] = None,
with_progress=False, with_progress: Literal[False] = False,
cache=None, cache: Optional[serializers.hdf5] = None,
geometry_library: GEOMETRY_LIBRARY = "opencascade", 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) it = iterator(settings, file_or_filename, num_threads, include, exclude, geometry_library)
if cache: if cache:
hdf5_cache = serializers.hdf5(cache, settings) hdf5_cache = serializers.hdf5(cache, settings)
@@ -40,6 +40,7 @@ except ImportError:
class shape_tuple(NamedTuple): class shape_tuple(NamedTuple):
"""A tuple containing IfcOpenShell serialized element/shape and pythonOCC shape."""
data: Union[ifcopenshell_wrapper.SerializedElement, ifcopenshell_wrapper.Serialization] data: Union[ifcopenshell_wrapper.SerializedElement, ifcopenshell_wrapper.Serialization]
geometry: TopoDS.TopoDS_Shape geometry: TopoDS.TopoDS_Shape
styles: tuple[tuple[float, float, float, float], ...] styles: tuple[tuple[float, float, float, float], ...]