From 45a16554857336d11b512eed30dc78b1719ccf9a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 29 May 2024 17:39:41 +0500 Subject: [PATCH] typing --- .../blenderbim/bim/module/cost/data.py | 5 +- .../bim/module/geometry/operator.py | 1 + .../blenderbim/bim/module/style/operator.py | 1 + src/blenderbim/blenderbim/core/aggregate.py | 38 +++++++-- src/blenderbim/blenderbim/core/material.py | 56 +++++++++---- src/blenderbim/blenderbim/core/style.py | 44 +++++++--- src/blenderbim/blenderbim/tool/aggregate.py | 11 ++- src/blenderbim/blenderbim/tool/material.py | 60 ++++++++------ src/blenderbim/blenderbim/tool/profile.py | 15 ++-- src/blenderbim/blenderbim/tool/root.py | 51 ++++++++---- src/blenderbim/blenderbim/tool/style.py | 82 ++++++++++--------- src/ifc5d/ifc5d/ifc5Dspreadsheet.py | 32 +++++--- .../ifcopenshell/util/element.py | 4 +- 13 files changed, 260 insertions(+), 140 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/cost/data.py b/src/blenderbim/blenderbim/bim/module/cost/data.py index 1c424cb1d6..09698a54be 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/data.py +++ b/src/blenderbim/blenderbim/bim/module/cost/data.py @@ -198,8 +198,11 @@ class CostSchedulesData: # data["DerivedUnitSymbol"] = "?" # print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name) + # TODO: dead code? @classmethod - def _get_object_quantities(cls, cost_item, element): + def _get_object_quantities( + cls, cost_item: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance + ) -> list[int]: if not element.is_a("IfcObject"): return [] cost_quantities = cost_item.CostQuantities diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index a26831bff1..385ca383c9 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -28,6 +28,7 @@ import ifcopenshell.util.element import ifcopenshell.util.representation import ifcopenshell.util.placement import ifcopenshell.api +import blenderbim.core.geometry import blenderbim.core.geometry as core import blenderbim.core.aggregate import blenderbim.core.style diff --git a/src/blenderbim/blenderbim/bim/module/style/operator.py b/src/blenderbim/blenderbim/bim/module/style/operator.py index e8b0479f9c..cf36eee95e 100644 --- a/src/blenderbim/blenderbim/bim/module/style/operator.py +++ b/src/blenderbim/blenderbim/bim/module/style/operator.py @@ -22,6 +22,7 @@ import blenderbim.bim.helper import blenderbim.bim.handler import blenderbim.tool as tool import blenderbim.core.style as core +import ifcopenshell.api import ifcopenshell.util.representation from blenderbim.bim.module.style.prop import switch_shading from pathlib import Path diff --git a/src/blenderbim/blenderbim/core/aggregate.py b/src/blenderbim/blenderbim/core/aggregate.py index 58d10814a7..44d4444f79 100644 --- a/src/blenderbim/blenderbim/core/aggregate.py +++ b/src/blenderbim/blenderbim/core/aggregate.py @@ -16,16 +16,30 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Union -def enable_editing_aggregate(aggregator, obj=None): +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool + + +def enable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None: aggregator.enable_editing(obj) -def disable_editing_aggregate(aggregator, obj=None): +def disable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None: aggregator.disable_editing(obj) -def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=None): +def assign_object( + ifc: tool.Ifc, + aggregator: tool.Aggregate, + collector: tool.Collector, + relating_obj: Optional[bpy.types.Object] = None, + related_obj: Optional[bpy.types.Object] = None, +) -> Union[ifcopenshell.entity_instance, None]: if not aggregator.can_aggregate(relating_obj, related_obj): return rel = ifc.run( @@ -37,7 +51,13 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non return rel -def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None): +def unassign_object( + ifc: tool.Ifc, + aggregate: tool.Aggregate, + collector: tool.Collector, + relating_obj: Optional[bpy.types.Object] = None, + related_obj: Optional[bpy.types.Object] = None, +) -> None: related_element = ifc.get_entity(related_obj) container = aggregate.get_container(related_element) if not relating_obj: @@ -52,7 +72,15 @@ def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=No collector.assign(related_obj) -def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None): +def add_part_to_object( + ifc: tool.Ifc, + aggregator: tool.Aggregate, + collector: tool.Collector, + blender: tool.Blender, + obj: bpy.types.Object, + part_class: str, + part_name: Optional[str] = None, +) -> None: part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name) assign_object(ifc, aggregator, collector, relating_obj=obj, related_obj=part_obj) blender.set_active_object(obj) diff --git a/src/blenderbim/blenderbim/core/material.py b/src/blenderbim/blenderbim/core/material.py index bd3eb87748..5a33704ead 100644 --- a/src/blenderbim/blenderbim/core/material.py +++ b/src/blenderbim/blenderbim/core/material.py @@ -16,15 +16,33 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Union -def unlink_material(ifc, obj=None): +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool + + +def unlink_material(ifc: tool.Ifc, obj: bpy.types.Material) -> None: ifc.unlink(obj=obj) -def add_material(ifc, material, style, obj=None, name=None, category=None, description=None): +def add_material( + ifc: tool.Ifc, + material: tool.Material, + style: tool.Style, + obj: Optional[bpy.types.Material] = None, + name: Optional[str] = None, + category: Optional[str] = None, + description: Optional[str] = None, +) -> ifcopenshell.entity_instance: if not obj: obj = material.add_default_material_object(name) - ifc_material = ifc.run("material.add_material", name=material.get_name(obj), category=category, description=description) + ifc_material = ifc.run( + "material.add_material", name=material.get_name(obj), category=category, description=description + ) ifc.link(ifc_material, obj) ifc_style = style.get_style(obj) if ifc_style: @@ -36,14 +54,16 @@ def add_material(ifc, material, style, obj=None, name=None, category=None, descr return ifc_material -def add_material_set(ifc, material, set_type=None): +def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> ifcopenshell.entity_instance: ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type) if material.is_editing_materials(): material.import_material_definitions(material.get_active_material_type()) return ifc_material -def remove_material(ifc, material_tool, style, material=None) -> bool: +def remove_material( + ifc: tool.Ifc, material_tool: tool.Material, style: tool.Style, material: ifcopenshell.entity_instance +) -> bool: """returns True after deleting False,\n returns False if material used in material sets and cannot be removed""" if material_tool.is_material_used_in_sets(material): @@ -58,31 +78,33 @@ def remove_material(ifc, material_tool, style, material=None) -> bool: return True -def remove_material_set(ifc, material_tool, material=None): +def remove_material_set(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None: ifc.run("material.remove_material_set", material=material) if material_tool.is_editing_materials(): material_tool.import_material_definitions(material_tool.get_active_material_type()) -def load_materials(material, material_type=None): +def load_materials(material: tool.Material, material_type: str) -> None: material.import_material_definitions(material_type) material.enable_editing_materials() -def disable_editing_materials(material): +def disable_editing_materials(material: tool.Material) -> None: material.disable_editing_materials() -def select_by_material(material_tool, spatial, material=None): +def select_by_material( + material_tool: tool.Material, spatial: tool.Spatial, material: ifcopenshell.entity_instance +) -> None: spatial.select_products(material_tool.get_elements_by_material(material)) -def enable_editing_material(material_tool, material): +def enable_editing_material(material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None: material_tool.load_material_attributes(material) material_tool.enable_editing_material(material) -def edit_material(ifc, material_tool, material): +def edit_material(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None: attributes = material_tool.get_material_attributes() ifc.run("material.edit_material", material=material, attributes=attributes) material_tool.sync_blender_material_name(material) @@ -92,11 +114,13 @@ def edit_material(ifc, material_tool, material): material_tool.enable_editing_materials() -def disable_editing_material(material_tool): +def disable_editing_material(material_tool: tool.Material) -> None: material_tool.disable_editing_material() -def assign_material(ifc, material_tool, material_type, objects): +def assign_material( + ifc: tool.Ifc, material_tool: tool.Material, material_type: Union[str, None], objects: list[bpy.types.Object] +) -> None: material_type = material_type or material_tool.get_active_object_material() material = material_tool.get_active_material() for obj in objects: @@ -109,7 +133,7 @@ def assign_material(ifc, material_tool, material_type, objects): material_tool.add_material_to_set(material_set=assigned_material, material=material) -def unassign_material(ifc, material_tool, objects): +def unassign_material(ifc: tool.Ifc, material_tool: tool.Material, objects: list[bpy.types.Object]) -> None: for obj in objects: element = ifc.get_entity(obj) if element: @@ -125,7 +149,9 @@ def unassign_material(ifc, material_tool, objects): ifc.run("material.unassign_material", products=[element]) -def patch_non_parametric_mep_segment(ifc, material_tool, profile_tool, obj): +def patch_non_parametric_mep_segment( + ifc: tool.Ifc, material_tool: tool.Material, profile_tool: tool.Profile, obj: bpy.types.Object +) -> None: element = ifc.get_entity(obj) if not element: return diff --git a/src/blenderbim/blenderbim/core/style.py b/src/blenderbim/blenderbim/core/style.py index 13576cb80f..f22b4b15d9 100644 --- a/src/blenderbim/blenderbim/core/style.py +++ b/src/blenderbim/blenderbim/core/style.py @@ -16,8 +16,16 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Any -def add_style(ifc, style, obj=None): +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool + + +def add_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> ifcopenshell.entity_instance: element = ifc.run("style.add_style", name=style.get_name(obj)) ifc.link(element, obj) if style.can_support_rendering_style(obj): @@ -33,18 +41,26 @@ def add_style(ifc, style, obj=None): return element -def add_external_style(ifc, style, obj, attributes): +def add_external_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, attributes: dict[str, Any]) -> None: element = style.get_style(obj) ifc.run( "style.add_surface_style", style=element, ifc_class="IfcExternallyDefinedSurfaceStyle", attributes=attributes ) -def update_external_style(ifc, style, external_style, attributes): +# TODO: unused `style` argument? +def update_external_style( + ifc: tool.Ifc, + style: ifcopenshell.entity_instance, + external_style: ifcopenshell.entity_instance, + attributes: dict[str, Any], +) -> None: ifc.run("style.edit_surface_style", style=external_style, attributes=attributes) -def remove_style(ifc, material, style_tool, style=None): +def remove_style( + ifc: tool.Ifc, material: tool.Material, style_tool: tool.Style, style: ifcopenshell.entity_instance +) -> None: obj = ifc.get_object(style) ifc.unlink(obj=obj, element=style) ifc.run("style.remove_style", style=style) @@ -54,7 +70,7 @@ def remove_style(ifc, material, style_tool, style=None): style_tool.import_presentation_styles(style_tool.get_active_style_type()) -def update_style_colours(ifc, style, obj=None, verbose=False): +def update_style_colours(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, verbose: bool = False) -> None: element = style.get_style(obj) if style.can_support_rendering_style(obj): @@ -91,7 +107,9 @@ def update_style_colours(ifc, style, obj=None, verbose=False): style.record_shading(obj) -def update_style_textures(ifc, style, obj=None, representation=None): +def update_style_textures( + ifc: tool.Ifc, style: tool.Style, obj: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance +) -> None: element = style.get_style(obj) uv_maps = style.get_uv_maps(representation) @@ -111,34 +129,34 @@ def update_style_textures(ifc, style, obj=None, representation=None): ifc.run("style.remove_surface_style", style=texture_style) -def unlink_style(ifc, style=None): +def unlink_style(ifc: tool.Ifc, style: ifcopenshell.entity_instance) -> None: obj = ifc.get_object(style) ifc.unlink(obj=obj, element=style) -def enable_editing_style(style, obj=None): +def enable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None: style.enable_editing(obj) style.import_surface_attributes(style.get_style(obj), obj) -def disable_editing_style(style, obj=None): +def disable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None: style.disable_editing(obj) -def edit_style(ifc, style, obj=None): +def edit_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> None: attributes = style.export_surface_attributes(obj) ifc.run("style.edit_presentation_style", style=style.get_style(obj), attributes=attributes) style.disable_editing(obj) -def load_styles(style, style_type=None): +def load_styles(style: tool.Style, style_type: str) -> None: style.import_presentation_styles(style_type) style.enable_editing_styles() -def disable_editing_styles(style): +def disable_editing_styles(style: tool.Style) -> None: style.disable_editing_styles() -def select_by_style(style_tool, spatial, style=None): +def select_by_style(style_tool: tool.Style, spatial: tool.Spatial, style: ifcopenshell.entity_instance) -> None: spatial.select_products(style_tool.get_elements_by_style(style)) diff --git a/src/blenderbim/blenderbim/tool/aggregate.py b/src/blenderbim/blenderbim/tool/aggregate.py index 66e549f67d..9188e12a32 100644 --- a/src/blenderbim/blenderbim/tool/aggregate.py +++ b/src/blenderbim/blenderbim/tool/aggregate.py @@ -20,6 +20,7 @@ import bpy import blenderbim.core.tool import blenderbim.tool as tool import ifcopenshell.util.element +from typing import Union class Aggregate(blenderbim.core.tool.Aggregate): @@ -46,19 +47,21 @@ class Aggregate(blenderbim.core.tool.Aggregate): return False @classmethod - def disable_editing(cls, obj): + def disable_editing(cls, obj: bpy.types.Object) -> None: obj.BIMObjectAggregateProperties.is_editing = False @classmethod - def enable_editing(cls, obj): + def enable_editing(cls, obj: bpy.types.Object) -> None: obj.BIMObjectAggregateProperties.is_editing = True @classmethod - def get_container(cls, element): + def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: return ifcopenshell.util.element.get_container(element) @classmethod - def get_relating_object(cls, related_element): + def get_relating_object( + cls, related_element: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: for rel in related_element.Decomposes: if rel.is_a("IfcRelAggregates"): return rel.RelatingObject diff --git a/src/blenderbim/blenderbim/tool/material.py b/src/blenderbim/blenderbim/tool/material.py index a1bd4a51b8..4a72301f2f 100644 --- a/src/blenderbim/blenderbim/tool/material.py +++ b/src/blenderbim/blenderbim/tool/material.py @@ -19,43 +19,45 @@ import bpy import ifcopenshell import blenderbim.core.tool +import blenderbim.core.material import blenderbim.tool as tool import blenderbim.bim.helper import ifcopenshell.util.unit import ifcopenshell.util.element +from typing import Union, Any class Material(blenderbim.core.tool.Material): @classmethod - def add_default_material_object(cls, name): + def add_default_material_object(cls, name: Union[str, None]) -> bpy.types.Material: return bpy.data.materials.new(name or "Default") @classmethod - def delete_object(cls, obj): + def delete_object(cls, obj: bpy.types.Material) -> None: bpy.data.materials.remove(obj) @classmethod - def disable_editing_materials(cls): + def disable_editing_materials(cls) -> None: bpy.context.scene.BIMMaterialProperties.is_editing = False @classmethod - def enable_editing_materials(cls): + def enable_editing_materials(cls) -> None: bpy.context.scene.BIMMaterialProperties.is_editing = True @classmethod - def get_active_material_type(cls): + def get_active_material_type(cls) -> str: return bpy.context.scene.BIMMaterialProperties.material_type @classmethod - def get_elements_by_material(cls, material): + def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material) @classmethod - def get_name(cls, obj): + def get_name(cls, obj: bpy.types.Material) -> str: return obj.name @classmethod - def import_material_definitions(cls, material_type): + def import_material_definitions(cls, material_type: str) -> None: props = bpy.context.scene.BIMMaterialProperties expanded_categories = {m.name for m in props.materials if m.is_expanded} props.materials.clear() @@ -89,11 +91,11 @@ class Material(blenderbim.core.tool.Material): new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)) @classmethod - def is_editing_materials(cls): + def is_editing_materials(cls) -> bool: return bpy.context.scene.BIMMaterialProperties.is_editing @classmethod - def is_material_used_in_sets(cls, material): + def is_material_used_in_sets(cls, material: ifcopenshell.entity_instance) -> bool: for inverse in tool.Ifc.get().get_inverse(material): if inverse.is_a() in [ "IfcMaterialProfile", @@ -105,48 +107,50 @@ class Material(blenderbim.core.tool.Material): return False @classmethod - def load_material_attributes(cls, material): + def load_material_attributes(cls, material: ifcopenshell.entity_instance) -> None: props = bpy.context.scene.BIMMaterialProperties props.material_attributes.clear() blenderbim.bim.helper.import_attributes2(material, props.material_attributes) @classmethod - def enable_editing_material(cls, material): + def enable_editing_material(cls, material: ifcopenshell.entity_instance) -> None: props = bpy.context.scene.BIMMaterialProperties props.active_material_id = material.id() props.editing_material_type = "ATTRIBUTES" @classmethod - def get_material_attributes(cls): + def get_material_attributes(cls) -> dict[str, Any]: return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMMaterialProperties.material_attributes) @classmethod - def disable_editing_material(cls): + def disable_editing_material(cls) -> None: props = bpy.context.scene.BIMMaterialProperties props.active_material_id = 0 props.editing_material_type = "" @classmethod - def get_type(cls, element): + def get_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: return ifcopenshell.util.element.get_type(element) @classmethod - def get_active_object_material(cls): + def get_active_object_material(cls) -> Union[str, None]: active_obj = bpy.context.active_object if not active_obj: return return active_obj.BIMObjectMaterialProperties.material_type @classmethod - def get_active_material(cls): + def get_active_material(cls) -> ifcopenshell.entity_instance: return tool.Ifc.get().by_id(int(bpy.context.active_object.BIMObjectMaterialProperties.material)) @classmethod - def get_material(cls, element, should_inherit=False): + def get_material( + cls, element: ifcopenshell.entity_instance, should_inherit: bool = False + ) -> Union[ifcopenshell.entity_instance, None]: return ifcopenshell.util.element.get_material(element, should_inherit=should_inherit) @classmethod - def is_a_material_set(cls, material): + def is_a_material_set(cls, material: ifcopenshell.entity_instance) -> bool: return material.is_a() in [ "IfcMaterialConstituentSet", "IfcMaterialLayerSet", @@ -154,7 +158,9 @@ class Material(blenderbim.core.tool.Material): ] @classmethod - def add_material_to_set(cls, material_set, material): + def add_material_to_set( + cls, material_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance + ) -> None: if material_set.is_a("IfcMaterialConstituentSet"): if not material_set.MaterialConstituents: tool.Ifc.run( @@ -195,7 +201,7 @@ class Material(blenderbim.core.tool.Material): ) @classmethod - def has_material_profile(cls, element): + def has_material_profile(cls, element: ifcopenshell.entity_instance) -> bool: material = cls.get_material(element, should_inherit=False) inherited_material = cls.get_material(element, should_inherit=True) if material and "Profile" in material.is_a(): @@ -205,11 +211,13 @@ class Material(blenderbim.core.tool.Material): return False @classmethod - def is_a_flow_segment(cls, element): + def is_a_flow_segment(cls, element: ifcopenshell.entity_instance) -> bool: return element.is_a("IfcFlowSegment") @classmethod - def replace_material_with_material_profile(cls, element): + def replace_material_with_material_profile( + cls, element: ifcopenshell.entity_instance + ) -> ifcopenshell.entity_instance: old_material = cls.get_material(element, should_inherit=False) old_inherited_material = cls.get_material(element, should_inherit=True) material = old_material if old_material and old_material.is_a("IfcMaterial") else None @@ -225,14 +233,14 @@ class Material(blenderbim.core.tool.Material): return material_profile @classmethod - def update_elements_using_material(cls, material): + def update_elements_using_material(cls, material: ifcopenshell.entity_instance) -> None: # update elements that are using this material elements = ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material) objects = [tool.Ifc.get_object(e) for e in elements] tool.Geometry.reload_representation(objects) @classmethod - def sync_blender_material_name(cls, material): + def sync_blender_material_name(cls, material: ifcopenshell.entity_instance) -> None: name = material.Name or "Unnamed" obj = tool.Ifc.get_object(material) if obj: @@ -245,7 +253,7 @@ class Material(blenderbim.core.tool.Material): obj.name = name @classmethod - def get_style(cls, material): + def get_style(cls, material: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: for material_representation in material.HasRepresentation: for representation in material_representation.Representations: for item in representation.Items: diff --git a/src/blenderbim/blenderbim/tool/profile.py b/src/blenderbim/blenderbim/tool/profile.py index c0295be0c7..10e8613694 100644 --- a/src/blenderbim/blenderbim/tool/profile.py +++ b/src/blenderbim/blenderbim/tool/profile.py @@ -17,18 +17,23 @@ # along with BlenderBIM Add-on. If not, see . import ifcopenshell +import ifcopenshell.geom import ifcopenshell.util.element import ifcopenshell.util.unit import ifcopenshell.util.placement import ifcopenshell.util.representation import blenderbim.core.tool import blenderbim.tool as tool +import PIL.ImageDraw from blenderbim.bim.module.model.decorator import ProfileDecorator +from typing import Union class Profile(blenderbim.core.tool.Profile): @classmethod - def draw_image_for_ifc_profile(cls, draw, profile, size): + def draw_image_for_ifc_profile( + cls, draw: PIL.ImageDraw.ImageDraw, profile: ifcopenshell.entity_instance, size: float + ) -> None: """generates image based on `profile` using `PIL.ImageDraw`""" settings = ifcopenshell.geom.settings() settings.set(settings.INCLUDE_CURVES, True) @@ -57,11 +62,11 @@ class Profile(blenderbim.core.tool.Profile): draw.line((tuple(grouped_verts[e[0]]), tuple(grouped_verts[e[1]])), fill="white", width=2) @classmethod - def is_editing_profile(cls): - return ProfileDecorator.installed + def is_editing_profile(cls) -> bool: + return bool(ProfileDecorator.installed) @classmethod - def get_profile(cls, element): + def get_profile(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: representations = element.Representation for representation in representations.Representations: if not representation.is_a("IfcShapeRepresentation"): @@ -74,7 +79,7 @@ class Profile(blenderbim.core.tool.Profile): return None @classmethod - def get_model_profiles(cls): + def get_model_profiles(cls) -> list[ifcopenshell.entity_instance]: return tool.Ifc.get().by_type("IfcProfileDef") @classmethod diff --git a/src/blenderbim/blenderbim/tool/root.py b/src/blenderbim/blenderbim/tool/root.py index f1f36d6933..057644e9f5 100644 --- a/src/blenderbim/blenderbim/tool/root.py +++ b/src/blenderbim/blenderbim/tool/root.py @@ -21,6 +21,7 @@ import ifcopenshell import ifcopenshell.api import ifcopenshell.util.representation import ifcopenshell.util.element +import ifcopenshell.util.placement import blenderbim.core.tool import blenderbim.core.aggregate import blenderbim.core.geometry @@ -29,17 +30,17 @@ import blenderbim.core.style import blenderbim.tool as tool from mathutils import Vector from blenderbim.bim.module.model.opening import FilledOpeningGenerator -from typing import Union, Optional +from typing import Union, Optional, Any class Root(blenderbim.core.tool.Root): @classmethod - def add_tracked_opening(cls, obj): + def add_tracked_opening(cls, obj: bpy.types.Object) -> None: new = bpy.context.scene.BIMModelProperties.openings.add() new.obj = obj @classmethod - def assign_body_styles(cls, element, obj): + def assign_body_styles(cls, element: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None: # Should this even be here? Should it be in the geometry tool? body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if body: @@ -56,7 +57,7 @@ class Root(blenderbim.core.tool.Root): ) @classmethod - def copy_representation(cls, source, dest): + def copy_representation(cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance) -> None: def exclude_callback(attribute): return attribute.is_a("IfcProfileDef") and attribute.ProfileName @@ -80,11 +81,13 @@ class Root(blenderbim.core.tool.Root): ] @classmethod - def does_type_have_representations(cls, element): + def does_type_have_representations(cls, element: ifcopenshell.entity_instance) -> bool: return bool(element.RepresentationMaps) @classmethod - def get_decomposition_relationships(cls, objs): + def get_decomposition_relationships( + cls, objs: list[bpy.types.Object] + ) -> dict[ifcopenshell.entity_instance, dict[str, Any]]: relationships = {} for obj in objs: element = tool.Ifc.get_entity(obj) @@ -96,7 +99,9 @@ class Root(blenderbim.core.tool.Root): return relationships @classmethod - def get_connection_relationships(cls, objs): + def get_connection_relationships( + cls, objs: list[bpy.types.Object] + ) -> dict[ifcopenshell.entity_instance, dict[str, Any]]: relationships = {} for obj in objs: element = tool.Ifc.get_entity(obj) @@ -119,7 +124,9 @@ class Root(blenderbim.core.tool.Root): return relationships @classmethod - def get_element_representation(cls, element, context): + def get_element_representation( + cls, element: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: if context.is_a("IfcGeometricRepresentationSubContext"): return ifcopenshell.util.representation.get_representation( element, @@ -134,13 +141,13 @@ class Root(blenderbim.core.tool.Root): return ifcopenshell.util.element.get_type(element) @classmethod - def get_object_name(cls, obj): + def get_object_name(cls, obj: bpy.types.Object) -> None: if "." in obj.name and obj.name.split(".")[-1].isnumeric(): return ".".join(obj.name.split(".")[:-1]) return obj.name @classmethod - def get_object_representation(cls, obj): + def get_object_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]: if obj.data and obj.data.BIMMeshProperties.ifc_definition_id: return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) element = tool.Ifc.get_entity(obj) @@ -152,19 +159,21 @@ class Root(blenderbim.core.tool.Root): return element.Representation.Representations[0] @classmethod - def get_representation_context(cls, representation): + def get_representation_context(cls, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: return representation.ContextOfItems @classmethod - def is_element_a(cls, element, ifc_class): + def is_element_a(cls, element: ifcopenshell.entity_instance, ifc_class: str) -> bool: return element.is_a(ifc_class) @classmethod - def link_object_data(cls, source_obj, destination_obj): + def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None: destination_obj.data = source_obj.data @classmethod - def recreate_decompositions(cls, relationships, old_to_new): + def recreate_decompositions( + cls, relationships, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]] + ) -> None: for subelement, data in relationships.items(): new_subelements = old_to_new.get(subelement) new_elements = old_to_new.get(data["element"]) @@ -227,7 +236,11 @@ class Root(blenderbim.core.tool.Root): ) @classmethod - def recreate_connections(cls, relationship, old_to_new): + def recreate_connections( + cls, + relationship: dict[ifcopenshell.entity_instance, dict[str, Any]], + old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]], + ) -> None: for element, data in relationship.items(): try: new_relating_element = old_to_new.get(data["relating_element"])[0] @@ -244,7 +257,9 @@ class Root(blenderbim.core.tool.Root): ) @classmethod - def recreate_aggregate(cls, old_to_new): + def recreate_aggregate( + cls, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]] + ) -> None: for old, new in old_to_new.items(): old_aggregate = ifcopenshell.util.element.get_aggregate(old) if old_aggregate: @@ -301,7 +316,7 @@ class Root(blenderbim.core.tool.Root): ) @classmethod - def set_object_name(cls, obj, element): + def set_object_name(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None: # This disables the Blender name event handler obj.BIMObjectProperties.is_renaming = True name = getattr(element, "Name", getattr(element, "AxisTag", None)) @@ -309,7 +324,7 @@ class Root(blenderbim.core.tool.Root): obj.BIMObjectProperties.is_renaming = False @classmethod - def unlink_object(cls, obj): + def unlink_object(cls, obj: bpy.types.Object) -> None: tool.Ifc.unlink(obj=obj) if hasattr(obj.data, "BIMMeshProperties"): obj.data.BIMMeshProperties.ifc_definition_id = 0 diff --git a/src/blenderbim/blenderbim/tool/style.py b/src/blenderbim/blenderbim/tool/style.py index 9ff8c9aa4c..ae99214599 100644 --- a/src/blenderbim/blenderbim/tool/style.py +++ b/src/blenderbim/blenderbim/tool/style.py @@ -20,11 +20,12 @@ import bpy import numpy as np import ifcopenshell import ifcopenshell.util.element +import ifcopenshell.util.representation import blenderbim.core.tool import blenderbim.tool as tool import blenderbim.bim.helper from mathutils import Color -from typing import Union +from typing import Union, Any, Optional # fmt: off TEXTURE_MAPS_BY_METHODS = { @@ -45,19 +46,19 @@ STYLE_PROPS_MAP = { class Style(blenderbim.core.tool.Style): @classmethod - def can_support_rendering_style(cls, obj): + def can_support_rendering_style(cls, obj: bpy.types.Material) -> bool: return obj.use_nodes and hasattr(obj.node_tree, "nodes") @classmethod - def disable_editing(cls, obj): + def disable_editing(cls, obj: bpy.types.Material) -> None: obj.BIMStyleProperties.is_editing = False @classmethod - def disable_editing_external_style(cls, obj): + def disable_editing_external_style(cls, obj: bpy.types.Material) -> None: obj.BIMStyleProperties.is_editing_external_style = False @classmethod - def disable_editing_styles(cls): + def disable_editing_styles(cls) -> None: bpy.context.scene.BIMStylesProperties.is_editing = False @classmethod @@ -67,39 +68,40 @@ class Style(blenderbim.core.tool.Style): return new_style @classmethod - def enable_editing(cls, obj): + def enable_editing(cls, obj: bpy.types.Material) -> None: obj.BIMStyleProperties.is_editing = True @classmethod - def enable_editing_external_style(cls, obj): + def enable_editing_external_style(cls, obj: bpy.types.Material) -> None: obj.BIMStyleProperties.is_editing_external_style = True @classmethod - def enable_editing_styles(cls): + def enable_editing_styles(cls) -> None: bpy.context.scene.BIMStylesProperties.is_editing = True @classmethod - def export_surface_attributes(cls, obj): + def export_surface_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]: return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.attributes) @classmethod - def export_external_style_attributes(cls, obj): + def export_external_style_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]: return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.external_style_attributes) @classmethod - def get_active_style_type(cls): + def get_active_style_type(cls) -> str: return bpy.context.scene.BIMStylesProperties.style_type + # TODO: `obj` argument is unused? @classmethod - def get_context(cls, obj): + def get_context(cls, obj) -> Union[ifcopenshell.entity_instance, None]: return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") @classmethod - def get_elements_by_style(cls, style): + def get_elements_by_style(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: return ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style) @classmethod - def get_name(cls, obj): + def get_name(cls, obj: bpy.types.Material) -> str: return obj.name @classmethod @@ -126,7 +128,7 @@ class Style(blenderbim.core.tool.Style): return style_elements @classmethod - def get_shading_style_data_from_props(cls) -> dict: + def get_shading_style_data_from_props(cls) -> dict[str, Any]: """returns style data from blender props in similar way to `Loader.surface_style_to_dict` to be compatible with `Loader.create_surface_style_rendering`""" surface_style_data = dict() @@ -154,7 +156,7 @@ class Style(blenderbim.core.tool.Style): return surface_style_data @classmethod - def get_texture_style_data_from_props(cls) -> list[dict]: + def get_texture_style_data_from_props(cls) -> list[dict[str, Any]]: """returns style data from blender props in similar way to `Loader.surface_texture_to_dict` to be compatible with `Loader.create_surface_style_with_textures`""" props = bpy.context.scene.BIMStylesProperties @@ -174,7 +176,7 @@ class Style(blenderbim.core.tool.Style): return textures @classmethod - def set_surface_style_props(cls): + def set_surface_style_props(cls) -> None: """set blender style props based on currently edited IfcSurfaceStyle, reset unrelated props to default values""" @@ -241,7 +243,7 @@ class Style(blenderbim.core.tool.Style): props["update_graph"] = prev_update_graph_value @classmethod - def get_surface_rendering_attributes(cls, obj, verbose=False): + def get_surface_rendering_attributes(cls, obj: bpy.types.Material, verbose: bool = False) -> dict[str, Any]: report = (lambda *x: print(*x)) if verbose else (lambda *x: None) def color_to_ifc_format(color): @@ -403,22 +405,22 @@ class Style(blenderbim.core.tool.Style): return attributes @classmethod - def get_surface_rendering_style(cls, obj): + def get_surface_rendering_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: style_elements = cls.get_style_elements(obj) return style_elements.get("IfcSurfaceStyleRendering", None) @classmethod - def get_texture_style(cls, obj): + def get_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: style_elements = cls.get_style_elements(obj) return style_elements.get("IfcSurfaceStyleWithTextures", None) @classmethod - def get_external_style(cls, obj): + def get_external_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: style_elements = cls.get_style_elements(obj) return style_elements.get("IfcExternallyDefinedSurfaceStyle", None) @classmethod - def get_surface_shading_attributes(cls, obj): + def get_surface_shading_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]: data = { "SurfaceColour": { "Name": None, @@ -433,7 +435,7 @@ class Style(blenderbim.core.tool.Style): return data @classmethod - def get_surface_shading_style(cls, obj): + def get_surface_shading_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: if obj.BIMMaterialProperties.ifc_style_id: style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id) items = [s for s in style.Styles if s.is_a() == "IfcSurfaceStyleShading"] @@ -441,7 +443,7 @@ class Style(blenderbim.core.tool.Style): return items[0] @classmethod - def get_surface_texture_style(cls, obj): + def get_surface_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: if obj.BIMMaterialProperties.ifc_style_id: style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id) items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleWithTextures")] @@ -449,7 +451,7 @@ class Style(blenderbim.core.tool.Style): return items[0] @classmethod - def get_uv_maps(cls, representation): + def get_uv_maps(cls, representation: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: items = [] for item in representation.Items: if item.is_a("IfcMappedItem"): @@ -464,7 +466,7 @@ class Style(blenderbim.core.tool.Style): return results @classmethod - def get_style_ui_props_attributes(cls, style_type): + def get_style_ui_props_attributes(cls, style_type: str) -> Union[bpy.types.PropertyGroup, None]: props = bpy.context.scene.BIMStylesProperties if style_type == "IfcExternallyDefinedSurfaceStyle": return props.external_style_attributes @@ -474,7 +476,7 @@ class Style(blenderbim.core.tool.Style): return props.lighting_style_colours @classmethod - def import_presentation_styles(cls, style_type): + def import_presentation_styles(cls, style_type: str) -> None: color_to_tuple = lambda x: (x.Red, x.Green, x.Blue) props = bpy.context.scene.BIMStylesProperties props.styles.clear() @@ -497,13 +499,13 @@ class Style(blenderbim.core.tool.Style): new.total_elements = len(ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style)) @classmethod - def import_surface_attributes(cls, style, obj): + def import_surface_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None: attributes = obj.BIMStyleProperties.attributes attributes.clear() blenderbim.bim.helper.import_attributes2(style, attributes) @classmethod - def import_external_style_attributes(cls, style, obj): + def import_external_style_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None: attributes = obj.BIMStyleProperties.external_style_attributes attributes.clear() blenderbim.bim.helper.import_attributes2(style, attributes) @@ -514,26 +516,26 @@ class Style(blenderbim.core.tool.Style): return bool(external_style and external_style.Location and external_style.Location.endswith(".blend")) @classmethod - def is_editing_styles(cls): + def is_editing_styles(cls) -> bool: return bpy.context.scene.BIMStylesProperties.is_editing @classmethod - def record_shading(cls, obj): + def record_shading(cls, obj: bpy.types.Material) -> None: obj.BIMMaterialProperties.shading_checksum = repr(np.array(obj.diffuse_color).tobytes()) @classmethod - def select_elements(cls, elements): + def select_elements(cls, elements: list[ifcopenshell.entity_instance]) -> None: for element in elements: obj = tool.Ifc.get_object(element) if obj: obj.select_set(True) @classmethod - def change_current_style_type(cls, blender_material, style_type): + def change_current_style_type(cls, blender_material: bpy.types.Material, style_type: str) -> None: blender_material.BIMStyleProperties.active_style_type = style_type @classmethod - def get_styled_items(cls, style): + def get_styled_items(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: ifc_file = tool.Ifc.get() inverses = list(ifc_file.get_inverse(style)) @@ -554,13 +556,15 @@ class Style(blenderbim.core.tool.Style): return items @classmethod - def assign_style_to_object(cls, style, obj): + def assign_style_to_object(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None: """assigns `style` to `object` current representation""" representation = tool.Geometry.get_active_representation(obj) tool.Ifc.run("style.assign_representation_styles", shape_representation=representation, styles=[style]) @classmethod - def assign_style_to_representation_item(cls, representation_item, style=None): + def assign_style_to_representation_item( + cls, representation_item: ifcopenshell.entity_instance, style: Optional[ifcopenshell.entity_instance] = None + ) -> None: ifc_file = tool.Ifc.get() if not representation_item.StyledByItem: if style is None: @@ -574,12 +578,14 @@ class Style(blenderbim.core.tool.Style): styled_item.Styles = (style,) @classmethod - def get_representation_item_style(cls, representation_item): + def get_representation_item_style( + cls, representation_item: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: for inverse in tool.Ifc.get().get_inverse(representation_item): if inverse.is_a("IfcStyledItem"): for style in inverse.Styles: return style @classmethod - def reload_material_from_ifc(cls, blender_material): + def reload_material_from_ifc(cls, blender_material: bpy.types.Material) -> None: blender_material.BIMStyleProperties.active_style_type = blender_material.BIMStyleProperties.active_style_type diff --git a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py index b870329d20..31dd3e2d7a 100644 --- a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py +++ b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py @@ -26,7 +26,7 @@ import ifcopenshell import ifcopenshell.util.element import ifcopenshell.util.cost import ifcopenshell.util.date -from typing import Union, Optional +from typing import Union, Optional, Any class IfcDataGetter: @@ -41,17 +41,17 @@ class IfcDataGetter: ] @staticmethod - def canonicalise_time(time): + def canonicalise_time(time: Union[datetime.datetime, None]) -> str: if not time: return "-" return time.strftime("%d/%m/%y") @staticmethod - def get_root_costs(cost_schedule): + def get_root_costs(cost_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: return [obj for rel in cost_schedule.Controls or [] for obj in rel.RelatedObjects or []] @staticmethod - def get_cost_item_values(cost_item=None): + def get_cost_item_values(cost_item: Union[ifcopenshell.entity_instance, None]) -> Union[list[dict[str, Any]], None]: if not cost_item: return None values = [] @@ -71,14 +71,14 @@ class IfcDataGetter: return values @staticmethod - def process_categories(cost_item, categories): + def process_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]: for cost_value in cost_item.CostValues or []: if cost_value.Category: categories.add("{}{}".format(cost_value.Category, " Cost")) return categories @staticmethod - def process_cost_item_categories(cost_item, categories): + def process_cost_item_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]: IfcDataGetter.process_categories(cost_item, categories) for rel in cost_item.IsNestedBy or []: for child in rel.RelatedObjects or []: @@ -86,14 +86,20 @@ class IfcDataGetter: return categories @staticmethod - def get_cost_rates_categories(schedule): + def get_cost_rates_categories(schedule: ifcopenshell.entity_instance) -> set[str]: categories = set() for cost_item in IfcDataGetter.get_root_costs(schedule): IfcDataGetter.process_cost_item_categories(cost_item, categories) return categories @staticmethod - def process_cost_data(file, cost_item, cost_items_data, index, hierarchy="1"): + def process_cost_data( + file: ifcopenshell.file, + cost_item: ifcopenshell.entity_instance, + cost_items_data: list[dict[str, Any]], + index: int, + hierarchy: str = "1", + ) -> None: def listToString(s): return ", ".join([str(i) for i in s]) @@ -134,7 +140,7 @@ class IfcDataGetter: ) @staticmethod - def get_cost_items_data(file, schedule): + def get_cost_items_data(file: ifcopenshell.file, schedule: ifcopenshell.entity_instance) -> list[dict[str, Any]]: cost_items_data = [] index = 0 for cost_item in IfcDataGetter.get_root_costs(schedule): @@ -142,7 +148,7 @@ class IfcDataGetter: return cost_items_data @staticmethod - def format_unit(unit): + def format_unit(unit: ifcopenshell.entity_instance) -> str: if unit.is_a("IfcContextDependentUnit"): return f"{unit.UnitType} / {unit.Name}" else: @@ -152,7 +158,7 @@ class IfcDataGetter: return f"{unit.UnitType} / {name}" @staticmethod - def get_cost_value_unit(cost_value=None): + def get_cost_value_unit(cost_value: Optional[ifcopenshell.entity_instance] = None) -> Union[str, None]: if not cost_value: return None unit = cost_value.UnitBasis @@ -161,9 +167,9 @@ class IfcDataGetter: return IfcDataGetter.format_unit(unit.UnitComponent) @staticmethod - def get_cost_item_quantity(file, cost_item=None): + def get_cost_item_quantity(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> dict[str, Any]: # TODO: handle multiple quantities, THOSE WHHICH ARE JUYST ASSIGNED TO THE COST ITEM DIRECTLY, NOT THROUGH OBJECTS. - def add_quantity(quantity, take_off_name): + def add_quantity(quantity: ifcopenshell.entity_instance, take_off_name: str) -> float: accounted_for.append(quantity) if take_off_name == "": take_off_name = quantity[0] diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 76366c2f0e..89999deb5b 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -833,7 +833,7 @@ def get_layers( def get_container( element: ifcopenshell.entity_instance, should_get_direct: bool = False, ifc_class: Optional[str] = None -) -> ifcopenshell.entity_instance: +) -> Union[ifcopenshell.entity_instance, None]: """ Retrieves the spatial structure container of an element. @@ -849,7 +849,7 @@ def get_container( example, you may be after the storey, not a space. :type ifc_class: str, optional :return: The direct or indirect container of the element or None. - :rtype: ifcopenshell.entity_instance + :rtype: Union[ifcopenshell.entity_instance, None] Example: