diff --git a/src/blenderbim/blenderbim/bim/module/void/operator.py b/src/blenderbim/blenderbim/bim/module/void/operator.py index 80e768201a..2f6e684f0f 100644 --- a/src/blenderbim/blenderbim/bim/module/void/operator.py +++ b/src/blenderbim/blenderbim/bim/module/void/operator.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell.api +import ifcopenshell.util.element import ifcopenshell.util.representation import blenderbim.tool as tool import blenderbim.core.geometry diff --git a/src/blenderbim/blenderbim/core/type.py b/src/blenderbim/blenderbim/core/type.py index 8660e52cfb..51f52d9c7e 100644 --- a/src/blenderbim/blenderbim/core/type.py +++ b/src/blenderbim/blenderbim/core/type.py @@ -16,14 +16,23 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations import blenderbim.core.geometry +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool -def assign_type(ifc, type_tool, element=None, type=None): +def assign_type( + ifc: tool.Ifc, type_tool: tool.Type, element: ifcopenshell.entity_instance, type: ifcopenshell.entity_instance +) -> None: ifc.run("type.assign_type", related_objects=[element], relating_type=type) obj = ifc.get_object(element) if type_tool.has_material_usage(element): - pass # for now, representation regeneration handled by API listeners + pass # for now, representation regeneration handled by API listeners else: type_data = type_tool.get_object_data(ifc.get_object(type)) if type_data: @@ -31,7 +40,8 @@ def assign_type(ifc, type_tool, element=None, type=None): type_tool.disable_editing(obj) -def purge_unused_types(ifc, type): +def purge_unused_types(ifc: tool.Ifc, type: tool.Type) -> int: + """Remove all types without occurrences, return an amount of the removed types.""" purged_types = 0 for element_type in type.get_model_types(): if not type.get_type_occurrences(element_type): diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index 3d4d4fc052..fdd1133e04 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -45,14 +45,14 @@ from typing import Union, Iterable, Optional class Geometry(blenderbim.core.tool.Geometry): @classmethod - def change_object_data(cls, obj, data, is_global=False): + def change_object_data(cls, obj: bpy.types.Object, data: bpy.types.ID, is_global: bool = False) -> None: if is_global: cls.replace_object_data_globally(obj.data, data) else: obj.data = data @classmethod - def replace_object_data_globally(cls, old_data, new_data): + def replace_object_data_globally(cls, old_data: bpy.types.ID, new_data: bpy.types.ID) -> None: if getattr(old_data, "is_editmode", None): raise Exception("user_remap is not supported for meshes in EDIT mode") old_data.user_remap(new_data) @@ -653,7 +653,14 @@ class Geometry(blenderbim.core.tool.Geometry): obj.name = name @classmethod - def replace_object_with_empty(cls, obj): + def replace_object_with_empty(cls, obj: bpy.types.Object) -> None: + """Recreate a Blender object as an empty object. + + This method is useful when an object should no longer have associated + data (in Blender, you cannot simply assign .data to None). Note that the + object's original data is not handled by this method and should be + processed separately to avoid leaving orphan data. + """ element = tool.Ifc.get_entity(obj) name = obj.name if element: diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 77b02b9581..9f2490bec9 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -23,6 +23,7 @@ import collections import collections.abc import numpy as np import ifcopenshell +import ifcopenshell.api import ifcopenshell.util.element import ifcopenshell.util.unit import ifcopenshell.util.placement @@ -460,7 +461,7 @@ class Model(blenderbim.core.tool.Model): return ifc_importer.added_data.values() @classmethod - def clear_scene_openings(cls): + def clear_scene_openings(cls) -> None: props = bpy.context.scene.BIMModelProperties has_deleted_opening = True while has_deleted_opening: @@ -525,7 +526,9 @@ class Model(blenderbim.core.tool.Model): return booleans @classmethod - def mark_manual_booleans(cls, element, booleans): + def mark_manual_booleans( + cls, element: ifcopenshell.entity_instance, booleans: list[ifcopenshell.entity_instance] + ) -> None: pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") boolean_ids = [b.id() for b in booleans] if pset_data: diff --git a/src/blenderbim/blenderbim/tool/type.py b/src/blenderbim/blenderbim/tool/type.py index dc74f49d25..c9de381afb 100644 --- a/src/blenderbim/blenderbim/tool/type.py +++ b/src/blenderbim/blenderbim/tool/type.py @@ -18,27 +18,32 @@ import bpy import ifcopenshell +import ifcopenshell.util.element +import ifcopenshell.util.representation import blenderbim.core.tool import blenderbim.core.geometry import blenderbim.tool as tool import blenderbim.bim.helper +from typing import Union class Type(blenderbim.core.tool.Type): @classmethod - def change_object_data(cls, obj, data, is_global=False): + def change_object_data(cls, obj: bpy.types.Object, data: bpy.types.ID, is_global: bool = False) -> None: tool.Geometry.change_object_data(obj, data, is_global) @classmethod - def disable_editing(cls, obj): + def disable_editing(cls, obj: bpy.types.Object) -> None: obj.BIMTypeProperties.is_editing_type = False @classmethod - def get_body_context(cls): + def get_body_context(cls) -> ifcopenshell.entity_instance: return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") @classmethod - def get_body_representation(cls, element): + def get_body_representation( + cls, element: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: if element.is_a("IfcProduct") and element.Representation and element.Representation.Representations: for representation in element.Representation.Representations: if representation.ContextOfItems.ContextIdentifier == "Body": @@ -49,7 +54,7 @@ class Type(blenderbim.core.tool.Type): return representation_map.MappedRepresentation @classmethod - def get_ifc_representation_class(cls, element): + def get_ifc_representation_class(cls, element: ifcopenshell.entity_instance) -> Union[str, None]: material = ifcopenshell.util.element.get_material(element) if material: if material.is_a("IfcMaterialProfileSetUsage"): @@ -58,7 +63,7 @@ class Type(blenderbim.core.tool.Type): return "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids" @classmethod - def get_model_types(cls): + def get_model_types(cls) -> list[ifcopenshell.entity_instance]: ifc_file = tool.Ifc.get() types = ifc_file.by_type("IfcElementType") # exclude IfcSpatialElementType @@ -69,39 +74,43 @@ class Type(blenderbim.core.tool.Type): return types @classmethod - def get_object_data(cls, obj): + def get_object_data(cls, obj: bpy.types.Object) -> Union[bpy.types.ID, None]: return obj.data @classmethod - def get_profile_set_usage(cls, element): + def get_profile_set_usage(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: material = ifcopenshell.util.element.get_material(element) if material: if material.is_a("IfcMaterialProfileSetUsage"): return material @classmethod - def get_representation_context(cls, representation): + def get_representation_context(cls, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: return representation.ContextOfItems @classmethod - def get_type_occurrences(cls, element_type): + def get_type_occurrences(cls, element_type: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: return ifcopenshell.util.element.get_types(element_type) @classmethod - def has_material_usage(cls, element): + def has_material_usage(cls, element: ifcopenshell.entity_instance) -> bool: material = ifcopenshell.util.element.get_material(element) if material: return "Usage" in material.is_a() return False @classmethod - def remove_object(cls, obj): + def remove_object(cls, obj: bpy.types.Object) -> None: bpy.data.objects.remove(obj) @classmethod def run_geometry_add_representation( - cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None - ): + cls, + obj: bpy.types.Object, + context: ifcopenshell.entity_instance, + ifc_representation_class: Union[str, None] = None, + profile_set_usage: Union[ifcopenshell.entity_instance, None] = None, + ) -> ifcopenshell.entity_instance: return blenderbim.core.geometry.add_representation( tool.Ifc, tool.Geometry, @@ -115,8 +124,12 @@ class Type(blenderbim.core.tool.Type): @classmethod def run_geometry_switch_representation( - cls, obj=None, representation=None, should_reload=None, is_global=None - ): + cls, + obj: bpy.types.Object, + representation: ifcopenshell.entity_instance, + should_reload: bool = False, + is_global: bool = False, + ) -> None: return blenderbim.core.geometry.switch_representation( tool.Ifc, tool.Geometry, diff --git a/src/blenderbim/test/tool/test_material.py b/src/blenderbim/test/tool/test_material.py index 6fd0a8ac68..e4f9805048 100644 --- a/src/blenderbim/test/tool/test_material.py +++ b/src/blenderbim/test/tool/test_material.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell +import ifcopenshell.api import blenderbim.core.tool import blenderbim.tool as tool from test.bim.bootstrap import NewFile diff --git a/src/blenderbim/test/tool/test_style.py b/src/blenderbim/test/tool/test_style.py index ab1f24a949..66e874420a 100644 --- a/src/blenderbim/test/tool/test_style.py +++ b/src/blenderbim/test/tool/test_style.py @@ -19,6 +19,8 @@ import os import bpy import ifcopenshell +import ifcopenshell.api +import ifcopenshell.util.representation import blenderbim.core.tool import blenderbim.tool as tool from test.bim.bootstrap import NewFile