From eca1d47d06f92bf3df351daa1a5385bba9744b4c Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 8 Aug 2024 12:11:36 +0500 Subject: [PATCH] typing --- .../blenderbim/core/georeference.py | 36 ++++++++----- src/blenderbim/blenderbim/tool/blender.py | 2 +- .../blenderbim/tool/georeference.py | 51 ++++++++++--------- .../ifcopenshell/api/project/append_asset.py | 13 +++-- .../ifcopenshell/util/geolocation.py | 25 +++++---- 5 files changed, 73 insertions(+), 54 deletions(-) diff --git a/src/blenderbim/blenderbim/core/georeference.py b/src/blenderbim/blenderbim/core/georeference.py index 54413d9239..ce49dadf78 100644 --- a/src/blenderbim/blenderbim/core/georeference.py +++ b/src/blenderbim/blenderbim/core/georeference.py @@ -16,26 +16,34 @@ # 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 -def add_georeferencing(georeference): +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool + + +def add_georeferencing(georeference: tool.Georeference) -> None: georeference.add_georeferencing() -def enable_editing_georeferencing(georeference): +def enable_editing_georeferencing(georeference: tool.Georeference) -> None: georeference.import_projected_crs() georeference.import_coordinate_operation() georeference.enable_editing() -def remove_georeferencing(ifc): +def remove_georeferencing(ifc: tool.Ifc) -> None: ifc.run("georeference.remove_georeferencing") -def disable_editing_georeferencing(georeference): +def disable_editing_georeferencing(georeference: tool.Georeference) -> None: georeference.disable_editing() -def edit_georeferencing(ifc, georeference): +def edit_georeferencing(ifc: tool.Ifc, georeference: tool.Georeference) -> None: ifc.run( "georeference.edit_georeferencing", projected_crs=georeference.export_projected_crs(), @@ -45,7 +53,7 @@ def edit_georeferencing(ifc, georeference): georeference.set_model_origin() -def get_cursor_location(georeference): +def get_cursor_location(georeference: tool.Georeference) -> None: location = georeference.get_cursor_location() if georeference.has_blender_offset(): georeference.set_coordinates("blender", location) @@ -53,39 +61,39 @@ def get_cursor_location(georeference): georeference.set_coordinates("local", location) -def import_plot(georeference, filepath): +def import_plot(georeference: tool.Georeference, filepath: str) -> None: georeference.import_plot(filepath) -def enable_editing_wcs(georeference): +def enable_editing_wcs(georeference: tool.Georeference) -> None: georeference.import_wcs() georeference.enable_editing_wcs() -def disable_editing_wcs(georeference): +def disable_editing_wcs(georeference: tool.Georeference) -> None: georeference.disable_editing_wcs() -def edit_wcs(georeference): +def edit_wcs(georeference: tool.Georeference) -> None: wcs = georeference.export_wcs() georeference.set_wcs(wcs) georeference.disable_editing_wcs() georeference.set_model_origin() -def enable_editing_true_north(georeference): +def enable_editing_true_north(georeference: tool.Georeference) -> None: georeference.import_true_north() georeference.enable_editing_true_north() -def disable_editing_true_north(georeference): +def disable_editing_true_north(georeference: tool.Georeference) -> None: georeference.disable_editing_true_north() -def edit_true_north(ifc, georeference): +def edit_true_north(ifc: tool.Ifc, georeference: tool.Georeference) -> None: ifc.run("georeference.edit_true_north", true_north=georeference.get_true_north_attributes()) georeference.disable_editing_true_north() -def remove_true_north(ifc): +def remove_true_north(ifc: tool.Ifc) -> None: ifc.run("georeference.edit_true_north", true_north=None) diff --git a/src/blenderbim/blenderbim/tool/blender.py b/src/blenderbim/blenderbim/tool/blender.py index ef7ba5287d..76a4893770 100644 --- a/src/blenderbim/blenderbim/tool/blender.py +++ b/src/blenderbim/blenderbim/tool/blender.py @@ -690,7 +690,7 @@ class Blender(blenderbim.core.tool.Blender): return False, None @classmethod - def toggle_edit_mode(cls, context: bpy.types.Context) -> set: + def toggle_edit_mode(cls, context: bpy.types.Context) -> set[str]: ao = context.active_object if not ao: return {"CANCELLED"} diff --git a/src/blenderbim/blenderbim/tool/georeference.py b/src/blenderbim/blenderbim/tool/georeference.py index aa0867cb25..df73810248 100644 --- a/src/blenderbim/blenderbim/tool/georeference.py +++ b/src/blenderbim/blenderbim/tool/georeference.py @@ -27,18 +27,21 @@ import ifcopenshell.util.unit import blenderbim.core.tool import blenderbim.tool as tool import blenderbim.bim.helper +from typing import Any, Union, Literal class Georeference(blenderbim.core.tool.Georeference): + COORDINATE_TYPE = Literal["blender", "local", "map"] + @classmethod - def add_georeferencing(cls): + def add_georeferencing(cls) -> None: tool.Ifc.run( "georeference.add_georeferencing", ifc_class=bpy.context.scene.BIMGeoreferenceProperties.coordinate_operation_class, ) @classmethod - def import_projected_crs(cls): + def import_projected_crs(cls) -> None: def callback(name, prop, data): if name == "MapUnit": new = bpy.context.scene.BIMGeoreferenceProperties.projected_crs.add() @@ -70,7 +73,7 @@ class Georeference(blenderbim.core.tool.Georeference): return @classmethod - def import_coordinate_operation(cls): + def import_coordinate_operation(cls) -> None: def callback(name, prop, data): if name in ("FirstCoordinate", "SecondCoordinate"): props = bpy.context.scene.BIMGeoreferenceProperties @@ -128,7 +131,7 @@ class Georeference(blenderbim.core.tool.Georeference): return @classmethod - def import_true_north(cls): + def import_true_north(cls) -> None: if tool.Ifc.get_schema() == "IFC2X3": return @@ -150,7 +153,7 @@ class Georeference(blenderbim.core.tool.Georeference): return @classmethod - def export_projected_crs(cls): + def export_projected_crs(cls) -> dict[str, Any]: def callback(attributes, prop): if not prop.is_null and prop.name == "MapUnit": attributes[prop.name] = tool.Ifc.get().by_id(int(prop.enum_value)) @@ -160,7 +163,7 @@ class Georeference(blenderbim.core.tool.Georeference): return blenderbim.bim.helper.export_attributes(props.projected_crs, callback=callback) @classmethod - def export_coordinate_operation(cls): + def export_coordinate_operation(cls) -> dict[str, Any]: measure_type = None def callback(attributes, prop): @@ -191,7 +194,7 @@ class Georeference(blenderbim.core.tool.Georeference): return blenderbim.bim.helper.export_attributes(props.coordinate_operation, callback=callback) @classmethod - def get_true_north_attributes(cls): + def get_true_north_attributes(cls) -> Union[list[float], None]: props = bpy.context.scene.BIMGeoreferenceProperties try: return [float(props.true_north_abscissa), float(props.true_north_ordinate)] @@ -199,46 +202,48 @@ class Georeference(blenderbim.core.tool.Georeference): print("ERROR, True North Abscissa and Ordinate expect a number") @classmethod - def enable_editing(cls): + def enable_editing(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing = True @classmethod - def disable_editing(cls): + def disable_editing(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing = False @classmethod - def enable_editing_wcs(cls): + def enable_editing_wcs(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing_wcs = True @classmethod - def disable_editing_wcs(cls): + def disable_editing_wcs(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing_wcs = False @classmethod - def enable_editing_true_north(cls): + def enable_editing_true_north(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing_true_north = True @classmethod - def disable_editing_true_north(cls): + def disable_editing_true_north(cls) -> None: bpy.context.scene.BIMGeoreferenceProperties.is_editing_true_north = False @classmethod - def set_coordinates(cls, io, coordinates): + def set_coordinates(cls, io: COORDINATE_TYPE, coordinates: list[float]) -> None: props = bpy.context.scene.BIMGeoreferenceProperties setattr(props, f"{io}_coordinates", ",".join([str(o) for o in coordinates])) @classmethod - def get_coordinates(cls, io): + def get_coordinates(cls, io: COORDINATE_TYPE) -> list[float]: props = bpy.context.scene.BIMGeoreferenceProperties return [float(co) for co in getattr(props, f"{io}_coordinates").split(",")] @classmethod - def get_cursor_location(cls): + def get_cursor_location(cls) -> list[float]: scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) return [o / scale for o in bpy.context.scene.cursor.location] @classmethod - def xyz2enh(cls, coordinates, should_return_in_map_units=True): + def xyz2enh( + cls, coordinates: tuple[float, float, float], should_return_in_map_units: bool = True + ) -> tuple[float, float, float]: props = bpy.context.scene.BIMGeoreferenceProperties if props.has_blender_offset: coordinates = ifcopenshell.util.geolocation.xyz2enh( @@ -256,7 +261,7 @@ class Georeference(blenderbim.core.tool.Georeference): ) @classmethod - def enh2xyz(cls, coordinates): + def enh2xyz(cls, coordinates: tuple[float, float, float]) -> tuple[float, float, float]: coordinates = ifcopenshell.util.geolocation.auto_enh2xyz(tool.Ifc.get(), *coordinates) props = bpy.context.scene.BIMGeoreferenceProperties if props.has_blender_offset: @@ -273,10 +278,10 @@ class Georeference(blenderbim.core.tool.Georeference): return coordinates @classmethod - def import_plot(cls, filepath): + def import_plot(cls, filepath: str) -> None: import bmesh - def parse_csv(file_path): + def parse_csv(file_path: str): import csv with open(file_path, "r") as f: @@ -307,7 +312,7 @@ class Georeference(blenderbim.core.tool.Georeference): bm.free() @classmethod - def import_wcs(cls): + def import_wcs(cls) -> None: props = bpy.context.scene.BIMGeoreferenceProperties wcs = None for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False): @@ -324,7 +329,7 @@ class Georeference(blenderbim.core.tool.Georeference): props.wcs_x, props.wcs_y, props.wcs_z = map(str, placement[:, 3][:3]) @classmethod - def export_wcs(cls): + def export_wcs(cls) -> dict[str, float]: props = bpy.context.scene.BIMGeoreferenceProperties return { "x": float(props.wcs_x), @@ -334,7 +339,7 @@ class Georeference(blenderbim.core.tool.Georeference): } @classmethod - def set_wcs(cls, wcs): + def set_wcs(cls, wcs: dict[str, float]) -> None: ifcopenshell.api.georeference.edit_wcs(tool.Ifc.get(), **wcs, is_si=False) @classmethod diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index b62a677905..90d19ba0c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -259,7 +259,7 @@ class Usecase: subelement_queue.extend(self.settings["library"].traverse(subelement, max_levels=1)[1:]) return new - def has_whitelisted_inverses(self, element): + def has_whitelisted_inverses(self, element: ifcopenshell.entity_instance) -> bool: for source_class, attributes in self.whitelisted_inverse_attributes.items(): if not element.is_a(source_class): continue @@ -274,6 +274,7 @@ class Usecase: return True elif value: return True + return False def check_inverses(self, element: ifcopenshell.entity_instance) -> None: for source_class, attributes in self.whitelisted_inverse_attributes.items(): @@ -318,7 +319,7 @@ class Usecase: if new_attribute is not None: new[i] = new_attribute - def is_another_asset(self, element): + def is_another_asset(self, element: ifcopenshell.entity_instance) -> bool: if element == self.settings["element"]: return False elif element.is_a("IfcFeatureElement"): @@ -332,7 +333,7 @@ class Usecase: return True return False - def reuse_existing_contexts(self): + def reuse_existing_contexts(self) -> None: added_contexts = set([e for e in self.added_elements.values() if e.is_a("IfcGeometricRepresentationContext")]) added_contexts -= set(self.existing_contexts) for added_context in added_contexts: @@ -344,7 +345,9 @@ class Usecase: for added_context in added_contexts: ifcopenshell.util.element.remove_deep2(self.file, added_context) - def get_equivalent_existing_context(self, added_context): + def get_equivalent_existing_context( + self, added_context: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: for context in self.existing_contexts: if context.is_a() != added_context.is_a(): continue @@ -361,7 +364,7 @@ class Usecase: ): return context - def create_equivalent_context(self, added_context): + def create_equivalent_context(self, added_context: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: if added_context.is_a("IfcGeometricRepresentationSubContext"): parent = self.get_equivalent_existing_context(added_context.ParentContext) if not parent: diff --git a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py index a74f75fc0c..2855bfde5c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py @@ -18,13 +18,14 @@ import math import numpy as np -import numpy.typing as npt import ifcopenshell import ifcopenshell.util.unit import ifcopenshell.util.element import ifcopenshell.util.placement from typing import NamedTuple, Optional, Union +MatrixType = ifcopenshell.util.placement.MatrixType + class HelmertTransformation(NamedTuple): e: float @@ -159,7 +160,9 @@ def auto_xyz2enh( return enh[0] / parameters.scale, enh[1] / parameters.scale, enh[2] / parameters.scale -def auto_enh2xyz(ifc_file, easting, northing, height, is_specified_in_map_units: bool = True): +def auto_enh2xyz( + ifc_file: ifcopenshell.file, easting: float, northing: float, height: float, is_specified_in_map_units: bool = True +) -> tuple[float, float, float]: """Convert from global map coordinate eastings, northings, and heights to local XYZ coordinates The necessary georeferencing map conversion is automatically detected from @@ -351,7 +354,7 @@ def enh2xyz( def local2global( - matrix: npt.NDArray[np.float64], + matrix: MatrixType, eastings: float = 0.0, northings: float = 0.0, orthogonal_height: float = 0.0, @@ -361,7 +364,7 @@ def local2global( factor_x: float = 1.0, factor_y: float = 1.0, factor_z: float = 1.0, -) -> npt.NDArray[np.float64]: +) -> MatrixType: """Manually convert a 4x4 matrix from local to global coordinates This function is for advanced users as it allows you to specify your own @@ -413,8 +416,8 @@ def local2global( def auto_local2global( - ifc_file: ifcopenshell.file, matrix: npt.NDArray[np.float64], should_return_in_map_units: bool = True -) -> npt.NDArray[np.float64]: + ifc_file: ifcopenshell.file, matrix: MatrixType, should_return_in_map_units: bool = True +) -> MatrixType: """Convert a local matrix to a global map matrix The necessary georeferencing map conversion is automatically detected from @@ -443,7 +446,7 @@ def auto_local2global( def global2local( - matrix: npt.NDArray[np.float64], + matrix: MatrixType, eastings: float = 0.0, northings: float = 0.0, orthogonal_height: float = 0.0, @@ -453,7 +456,7 @@ def global2local( factor_x: float = 1.0, factor_y: float = 1.0, factor_z: float = 1.0, -) -> npt.NDArray[np.float64]: +) -> MatrixType: """Manually convert a 4x4 matrix from global to local coordinates This function is for advanced users as it allows you to specify your own @@ -504,8 +507,8 @@ def global2local( def auto_global2local( - ifc_file: ifcopenshell.file, matrix: npt.NDArray[np.float64], is_specified_in_map_units: bool = True -) -> npt.NDArray[np.float64]: + ifc_file: ifcopenshell.file, matrix: MatrixType, is_specified_in_map_units: bool = True +) -> MatrixType: """Convert a global map matrix to a local matrix The necessary georeferencing map conversion is automatically detected from @@ -651,7 +654,7 @@ def angle2yaxis(angle: float) -> tuple[float, float]: return x, y -def get_wcs(ifc_file: ifcopenshell.file) -> Optional[npt.NDArray[np.float64]]: +def get_wcs(ifc_file: ifcopenshell.file) -> Optional[MatrixType]: """Gets the WCS (prioritising 3D contexts) as a matrix :param: The IFC file