diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 6df397b30f..8c05315643 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -232,19 +232,19 @@ class IfcImporter: self.material_creator = MaterialCreator(ifc_import_settings, self) - def profile_code(self, message): + def profile_code(self, message: str) -> None: if not self.time: self.time = time.time() print("{} :: {:.2f}".format(message, time.time() - self.time)) self.time = time.time() self.update_progress(self.progress + 1) - def update_progress(self, progress): + def update_progress(self, progress: float) -> None: if progress <= 100: self.progress = progress bpy.context.window_manager.progress_update(self.progress) - def execute(self): + def execute(self) -> None: bpy.context.window_manager.progress_begin(0, 100) self.profile_code("Starting import process") self.load_file() @@ -324,7 +324,7 @@ class IfcImporter: coords = getattr(point, "Coordinates", point) return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit - def process_context_filter(self): + def process_context_filter(self) -> None: # Annotation ContextType is to accommodate broken Revit files # See https://github.com/Autodesk/revit-ifc/issues/187 type_priority = ["Model", "Plan", "Annotation"] @@ -412,7 +412,7 @@ class IfcImporter: settings.set_context_ids([context.id()]) self.gross_context_settings.append(settings) - def process_element_filter(self): + def process_element_filter(self) -> None: offset = self.ifc_import_settings.element_offset offset_limit = offset + self.ifc_import_settings.element_limit @@ -479,7 +479,7 @@ class IfcImporter: break return results - def parse_native_elements(self): + def parse_native_elements(self) -> None: if not self.ifc_import_settings.should_load_geometry: return for element in self.elements: @@ -487,13 +487,13 @@ class IfcImporter: self.native_elements.add(element) self.elements -= self.native_elements - def is_native(self, element): + def is_native(self, element: ifcopenshell.entity_instance) -> bool: if ( not element.Representation or not element.Representation.Representations or getattr(element, "HasOpenings", None) ): - return + return False representation = None representation_priority = None @@ -508,7 +508,7 @@ class IfcImporter: context = rep.ContextOfItems if not representation: - return + return False matrix = np.eye(4) representation_id = None @@ -566,8 +566,11 @@ class IfcImporter: "type": "IfcFaceBasedSurfaceModel", } return True + return False - def is_native_swept_disk_solid(self, element, representation): + def is_native_swept_disk_solid( + self, element: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance + ) -> bool: items = [i["item"] for i in ifcopenshell.util.representation.resolve_items(representation)] if len(items) == 1 and items[0].is_a("IfcSweptDiskSolid"): if tool.Blender.Modifier.is_railing(element): @@ -583,20 +586,20 @@ class IfcImporter: return True return False - def is_native_faceted_brep(self, representation): + def is_native_faceted_brep(self, representation: ifcopenshell.entity_instance) -> bool: # TODO handle mapped items for i in representation.Items: if i.is_a() != "IfcFacetedBrep": return False return True - def is_native_face_based_surface_model(self, representation): + def is_native_face_based_surface_model(self, representation: ifcopenshell.entity_instance) -> bool: for i in representation.Items: if i.is_a() != "IfcFaceBasedSurfaceModel": return False return True - def get_products_from_shape_representation(self, element): + def get_products_from_shape_representation(self, element: ifcopenshell.entity_instance) -> None: products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation] for rep_map in element.RepresentationMap: for usage in rep_map.MapUsage: @@ -605,7 +608,7 @@ class IfcImporter: products.extend(self.get_products_from_shape_representation(inverse_element)) return products - def predict_dense_mesh(self): + def predict_dense_mesh(self) -> None: if self.ifc_import_settings.should_use_native_meshes: return @@ -632,7 +635,7 @@ class IfcImporter: if faces and max(faces) > threshold: self.ifc_import_settings.should_use_native_meshes = True - def calculate_model_offset(self): + def calculate_model_offset(self) -> None: props = bpy.context.scene.BIMGeoreferenceProperties if props.has_blender_offset: return @@ -650,14 +653,14 @@ class IfcImporter: return self.guess_false_origin_and_project_north(building) return self.guess_false_origin() - def set_manual_blender_offset(self): + def set_manual_blender_offset(self) -> None: props = bpy.context.scene.BIMGeoreferenceProperties props.blender_eastings = str(self.ifc_import_settings.false_origin[0]) props.blender_northings = str(self.ifc_import_settings.false_origin[1]) props.blender_orthogonal_height = str(self.ifc_import_settings.false_origin[2]) props.has_blender_offset = True - def guess_false_origin_and_project_north(self, element): + def guess_false_origin_and_project_north(self, element: ifcopenshell.entity_instance) -> None: if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"): return placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) @@ -672,7 +675,7 @@ class IfcImporter: props.blender_x_axis_ordinate = str(placement[1][0]) props.has_blender_offset = True - def guess_false_origin(self): + def guess_false_origin(self) -> None: # Civil BIM applications like to work in absolute coordinates, where the # ObjectPlacement is usually 0,0,0 (but not always, so we'll need to # check for the actual transformation) but each individual coordinate of @@ -1909,13 +1912,17 @@ class IfcImporter: polyline.points[-1].co = mathutils.Vector(v2) return curve - def create_mesh(self, element: ifcopenshell.entity_instance, shape) -> bpy.types.Mesh: + def create_mesh( + self, + element: ifcopenshell.entity_instance, + shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType], + ) -> bpy.types.Mesh: try: if hasattr(shape, "geometry"): # shape is ifcopenshell_wrapper.TriangulationElement - geometry: ifcopenshell_wrapper.Triangulation = shape.geometry + geometry = shape.geometry else: - geometry: ifcopenshell_wrapper.Triangulation = shape + geometry = shape mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name(geometry)) diff --git a/src/blenderbim/blenderbim/tool/loader.py b/src/blenderbim/blenderbim/tool/loader.py index aac640d4da..b6a507a668 100644 --- a/src/blenderbim/blenderbim/tool/loader.py +++ b/src/blenderbim/blenderbim/tool/loader.py @@ -19,6 +19,7 @@ import re import bpy import bmesh +import ifcopenshell.geom import ifcopenshell.util.element import blenderbim.core.tool import blenderbim.tool as tool @@ -55,9 +56,12 @@ class Loader(blenderbim.core.tool.Loader): return collection @classmethod - def get_mesh_name(cls, geometry) -> str: + def get_mesh_name(cls, geometry: ifcopenshell.geom.ShapeType) -> str: representation_id = geometry.id if "-" in representation_id: + # Example: 2432-openings-2468, where + # 2432 is mapped representation id + # and 2468 is IFCRELVOIDSELEMENT representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0])) else: representation_id = int(re.sub(r"\D", "", representation_id)) diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 5de590b9f5..8125fa573f 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -169,7 +169,7 @@ class entity_instance: return file.from_pointer(self.wrapped_data.file_pointer()) - def __getattr__(self, name): + def __getattr__(self, name: str) -> Any: INVALID, FORWARD, INVERSE = range(3) attr_cat = self.wrapped_data.get_attribute_category(name) if attr_cat == FORWARD: diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 3cd1cb3c5c..dad21c9c88 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -27,9 +27,13 @@ from ..entity_instance import entity_instance from . import has_occ -from typing import TypeVar, Union, Optional +from typing import TypeVar, Union, Optional, Generator T = TypeVar("T") +ShapeElementType = Union[ + ifcopenshell_wrapper.BRepElement, ifcopenshell_wrapper.TriangulationElement, ifcopenshell_wrapper.SerializedElement +] +ShapeType = Union[ifcopenshell_wrapper.BRep, ifcopenshell_wrapper.Triangulation, ifcopenshell_wrapper.Serialization] def wrap_shape_creation(settings, shape): @@ -122,7 +126,7 @@ class iterator(ifcopenshell_wrapper.Iterator): def get(self): return wrap_shape_creation(self.settings, ifcopenshell_wrapper.Iterator.get(self)) - def __iter__(self): + def __iter__(self) -> Generator[ShapeElementType, None, None]: if self.initialize(): while True: yield self.get() diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index e6c1cf750e..3932f4fd95 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -738,7 +738,7 @@ def get_elements_by_style( def get_elements_by_representation( ifc_file: ifcopenshell.file, representation: ifcopenshell.entity_instance -) -> list[ifcopenshell.entity_instance]: +) -> set[ifcopenshell.entity_instance]: """Gets all elements using a geometric representation :param ifc_file: The IFC file @@ -746,7 +746,7 @@ def get_elements_by_representation( :param representation: The IfcShapeRepresentation representation :type representation: ifcopenshell.entity_instance :return: The elements using the geometric representation - :rtype: list[ifcopenshell.entity_instance] + :rtype: set[ifcopenshell.entity_instance] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py index 81ec583419..fac2f12bf1 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py @@ -26,7 +26,7 @@ import ifcopenshell.util.element import ifcopenshell.util.representation import ifcopenshell.util.unit from math import cos, sin, pi, tan, radians, degrees, atan, sqrt, ceil -from typing import Union, Optional, Literal, Any +from typing import Union, Optional, Literal, Any, Sequence from itertools import chain from mathutils import Vector, Matrix @@ -304,9 +304,9 @@ class ShapeBuilder: x_axis_radius: float, y_axis_radius: float, position=Vector((0.0, 0.0)).freeze(), - trim_points: list[Vector] = (), + trim_points: Sequence[Vector] = (), ref_x_direction: Vector = Vector((1.0, 0.0)), - trim_points_mask: list[int] = (), + trim_points_mask: Sequence[int] = (), ) -> ifcopenshell.entity_instance: """ Ellipse trimming points should be specified in counter clockwise order. @@ -345,7 +345,7 @@ class ShapeBuilder: self, outer_curve: ifcopenshell.entity_instance, name: Optional[str] = None, - inner_curves: list[ifcopenshell.entity_instance] = (), + inner_curves: Sequence[ifcopenshell.entity_instance] = (), profile_type: str = "AREA", ) -> ifcopenshell.entity_instance: # > inner_curves - list of IfcCurve; @@ -882,8 +882,8 @@ class ShapeBuilder: def get_simple_2dcurve_data( self, coords: list[Vector], - fillets: list[int] = (), - fillet_radius: list[float] = (), + fillets: Sequence[int] = (), + fillet_radius: Sequence[float] = (), closed: bool = True, create_ifc_curve: bool = False, ) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]: