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
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
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")
@@ -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
+30 -4
View File
@@ -17,7 +17,24 @@
# 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)
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)
+9 -1
View File
@@ -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:
+14 -1
View File
@@ -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)
+41 -17
View File
@@ -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
@@ -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)
@@ -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], ...]