diff --git a/src/bonsai/bonsai/bim/module/covering/workspace.py b/src/bonsai/bonsai/bim/module/covering/workspace.py index 08c496b756..44c6f36721 100644 --- a/src/bonsai/bonsai/bim/module/covering/workspace.py +++ b/src/bonsai/bonsai/bim/module/covering/workspace.py @@ -45,8 +45,8 @@ class CoveringTool(WorkSpaceTool): CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) -def add_layout_hotkey(layout, text, hotkey, description): - args = ["covering", layout, text, hotkey, description] +def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: + args = ("covering", layout, text, hotkey, description) tool.Blender.add_layout_hotkey_operator(*args) diff --git a/src/bonsai/bonsai/bim/module/misc/operator.py b/src/bonsai/bonsai/bim/module/misc/operator.py index 962df15068..3e68f62890 100644 --- a/src/bonsai/bonsai/bim/module/misc/operator.py +++ b/src/bonsai/bonsai/bim/module/misc/operator.py @@ -19,6 +19,9 @@ import bpy import numpy as np import ifcopenshell +import ifcopenshell.util.geolocation +import ifcopenshell.util.placement +import ifcopenshell.util.unit import bonsai.tool as tool import bonsai.core.misc as core import bonsai.core.geometry as core_geometry diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index b9646bce63..249acf9614 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -519,7 +519,7 @@ class AlignProduct(bpy.types.Operator): obj.matrix_world = Matrix.Translation(active_y_axis * -y_distances[i]) @ obj.matrix_world return {"FINISHED"} - def get_axis_distances(self, point, axis, context): + def get_axis_distances(self, point: Vector, axis: Vector, context: bpy.types.Context) -> list[float]: results = [] for obj in context.selected_objects: if self.align_type == "CENTERLINE": diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index cb4fb47454..35425e01d4 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -412,11 +412,11 @@ class DumbWallAligner: # An alignment shifts the origin of all walls to the closest point on the # local X axis of the reference wall. In addition, the Z rotation is copied. # Z translations are ignored for alignment. - def __init__(self, wall, reference_wall): + def __init__(self, wall: bpy.types.Object, reference_wall: bpy.types.Object): self.wall = wall self.reference_wall = reference_wall - def align_centerline(self): + def align_centerline(self) -> None: self.align_rotation() l_start = Vector(self.reference_wall.bound_box[0]).lerp(Vector(self.reference_wall.bound_box[3]), 0.5) @@ -434,7 +434,7 @@ class DumbWallAligner: new_origin = point - offset self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy - def align_last_layer(self): + def align_last_layer(self) -> None: self.align_rotation() if self.is_rotation_flipped(): @@ -457,7 +457,7 @@ class DumbWallAligner: new_origin = point - offset self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy - def align_first_layer(self): + def align_first_layer(self) -> None: self.align_rotation() if self.is_rotation_flipped(): @@ -480,7 +480,7 @@ class DumbWallAligner: new_origin = point - offset self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy - def align_rotation(self): + def align_rotation(self) -> None: reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d() wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d() angle = reference.angle_signed(wall) @@ -492,7 +492,7 @@ class DumbWallAligner: self.wall.rotation_euler[2] += angle bpy.context.view_layer.update() - def is_rotation_flipped(self): + def is_rotation_flipped(self) -> bool: reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d() wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d() angle = reference.angle_signed(wall) @@ -835,7 +835,7 @@ class DumbWallJoiner: body = copy.deepcopy(axis1["reference"]) self.recreate_wall(element1, wall1, axis, body) - def split(self, wall1, target): + def split(self, wall1: bpy.types.Object, target: Vector) -> None: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) element1 = tool.Ifc.get_entity(wall1) @@ -895,7 +895,7 @@ class DumbWallJoiner: self.recreate_wall(element1, wall1, axis1["reference"], axis1["reference"]) self.recreate_wall(element2, wall2, axis2["reference"], axis2["reference"]) - def flip(self, wall1): + def flip(self, wall1: bpy.types.Object) -> None: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) if tool.Ifc.is_moved(wall1): diff --git a/src/bonsai/bonsai/bim/module/spatial/workspace.py b/src/bonsai/bonsai/bim/module/spatial/workspace.py index 2eb0f8f05e..bfd7c9d3f6 100644 --- a/src/bonsai/bonsai/bim/module/spatial/workspace.py +++ b/src/bonsai/bonsai/bim/module/spatial/workspace.py @@ -49,8 +49,8 @@ class SpatialTool(WorkSpaceTool): SpatialToolUI.draw(context, layout) -def add_layout_hotkey(layout, text, hotkey, description): - args = ["spatial", layout, text, hotkey, description] +def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: + args = ("spatial", layout, text, hotkey, description) tool.Blender.add_layout_hotkey_operator(*args) diff --git a/src/bonsai/bonsai/bim/module/structural/workspace.py b/src/bonsai/bonsai/bim/module/structural/workspace.py index 1f4717b9d4..cec8115a2d 100644 --- a/src/bonsai/bonsai/bim/module/structural/workspace.py +++ b/src/bonsai/bonsai/bim/module/structural/workspace.py @@ -41,8 +41,8 @@ class StructuralTool(WorkSpaceTool): StructuralToolUI.draw(context, layout) -def add_layout_hotkey(layout, text, hotkey, description): - args = ["structural", layout, text, hotkey, description] +def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: + args = ("structural", layout, text, hotkey, description) tool.Blender.add_layout_hotkey_operator(*args) diff --git a/src/bonsai/bonsai/core/misc.py b/src/bonsai/bonsai/core/misc.py index 033e7ab76f..46bbcb1675 100644 --- a/src/bonsai/bonsai/core/misc.py +++ b/src/bonsai/bonsai/core/misc.py @@ -16,8 +16,16 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +from __future__ import annotations +from typing import TYPE_CHECKING, Optional -def resize_to_storey(misc, obj=None, total_storeys=None): +if TYPE_CHECKING: + import bpy + import ifcopenshell + import bonsai.tool as tool + + +def resize_to_storey(misc: tool.Misc, obj: bpy.types.Object, total_storeys: int) -> None: storey = misc.get_object_storey(obj) if not storey: return diff --git a/src/bonsai/bonsai/tool/misc.py b/src/bonsai/bonsai/tool/misc.py index 8a94436954..562033ece7 100644 --- a/src/bonsai/bonsai/tool/misc.py +++ b/src/bonsai/bonsai/tool/misc.py @@ -19,28 +19,33 @@ import bpy import bmesh import ifcopenshell +import ifcopenshell.util.element +import ifcopenshell.util.placement +import ifcopenshell.util.unit import bonsai.core.tool import bonsai.core.root import bonsai.tool as tool from mathutils import Vector, Matrix -from bonsai.bim.ifc import IfcStore +from typing import Union class Misc(bonsai.core.tool.Misc): @classmethod - def get_object_storey(cls, obj): - storey = ifcopenshell.util.element.get_container(tool.Ifc.get_entity(obj)) + def get_object_storey(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]: + element = tool.Ifc.get_entity(obj) + assert element + storey = ifcopenshell.util.element.get_container(element) if storey and storey.is_a("IfcBuildingStorey"): return storey @classmethod - def get_storey_elevation_in_si(cls, storey): + def get_storey_elevation_in_si(cls, storey: ifcopenshell.entity_instance) -> float: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) elevation = ifcopenshell.util.placement.get_storey_elevation(storey) return elevation * unit_scale @classmethod - def get_storey_height_in_si(cls, storey, total_storeys): + def get_storey_height_in_si(cls, storey: ifcopenshell.entity_instance, total_storeys: int) -> Union[float, None]: building = ifcopenshell.util.element.get_aggregate(storey) related_objects = [] for rel in building.IsDecomposedBy: @@ -60,11 +65,12 @@ class Misc(bonsai.core.tool.Misc): return (next_storey_elevation - storey_elevation) * unit_scale @classmethod - def set_object_origin_to_bottom(cls, obj): + def set_object_origin_to_bottom(cls, obj: bpy.types.Object) -> None: absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box] min_z = min([c[2] for c in absolute_bound_box]) new_origin = obj.matrix_world.translation.copy() new_origin[2] = min_z + assert isinstance(obj.data, bpy.types.Mesh) obj.data.transform( Matrix.Translation( (obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin)) @@ -73,15 +79,15 @@ class Misc(bonsai.core.tool.Misc): obj.matrix_world.translation = new_origin @classmethod - def move_object_to_elevation(cls, obj, elevation): + def move_object_to_elevation(cls, obj: bpy.types.Object, elevation: float) -> None: obj.matrix_world.translation[2] = elevation @classmethod - def run_root_copy_class(cls, obj=None): + def run_root_copy_class(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance: return bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj) @classmethod - def scale_object_to_height(cls, obj, height): + def scale_object_to_height(cls, obj: bpy.types.Object, height: float) -> None: absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box] max_z = max([c[2] for c in absolute_bound_box]) min_z = min([c[2] for c in absolute_bound_box]) @@ -101,6 +107,7 @@ class Misc(bonsai.core.tool.Misc): cls, objs: list[bpy.types.Object], cutter: bpy.types.Object ) -> list[bpy.types.Object]: cutter_mesh = cutter.data + assert isinstance(cutter_mesh, bpy.types.Mesh) bm = bmesh.new() bm.from_mesh(cutter_mesh) @@ -119,6 +126,7 @@ class Misc(bonsai.core.tool.Misc): collection.objects.link(new_obj) mod = new_obj.modifiers.new(type="BOOLEAN", name="Boolean") + assert isinstance(mod, bpy.types.BooleanModifier) mod.object = cutter with bpy.context.temp_override(object=new_obj): bpy.ops.object.modifier_apply(modifier="Boolean") @@ -126,6 +134,7 @@ class Misc(bonsai.core.tool.Misc): bm_flipped.to_mesh(cutter_mesh) mod = obj.modifiers.new(type="BOOLEAN", name="Boolean") + assert isinstance(mod, bpy.types.BooleanModifier) mod.object = cutter with bpy.context.temp_override(object=obj): bpy.ops.object.modifier_apply(modifier="Boolean") diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index 4d301f7068..0c012977b3 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -762,6 +762,12 @@ class Spatial(bonsai.core.tool.Spatial): @classmethod def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]: + """ + `x`, `y` - object's center XY in world space;\n + `z` - object's local Z- in world space;\n + `h` - object's Z dimension;\n + `mat` - object's matrix + """ mat = obj.matrix_world local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector()) global_bbox_center = mat @ local_bbox_center @@ -774,6 +780,12 @@ class Spatial(bonsai.core.tool.Spatial): @classmethod def get_x_y_z_h_mat_from_cursor(cls) -> tuple[float, float, float, float, Matrix]: + """ + `x`, `y` - from cursor;\n + `z` - from default container Z location (if set);\n + `h` - default value of 3;\n + `mat` - identity matrix + """ x, y, z = bpy.context.scene.cursor.location.xyz if container := tool.Root.get_default_container(): if container_obj := tool.Ifc.get_object(container): @@ -940,6 +952,7 @@ class Spatial(bonsai.core.tool.Spatial): @classmethod def edit_active_space_obj_from_mesh(cls, mesh: bpy.types.Mesh) -> None: active_obj = bpy.context.active_object + assert active_obj and isinstance(active_obj.data, bpy.types.Mesh) mesh.name = active_obj.data.name mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id tool.Geometry.change_object_data(active_obj, mesh, is_global=True) diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 45869dd600..2a7777d8cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -50,8 +50,8 @@ HEADER_FIELDS = { class Transaction: - def __init__(self, ifc_file): - self.file = ifc_file + def __init__(self, ifc_file: file): + self.file: file = ifc_file self.operations = [] self.is_batched = False self.batch_delete_index = 0