From def9352adec319576e59edf240205c82916d9907 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 8 May 2025 15:56:15 +0500 Subject: [PATCH] typing --- src/bonsai/bonsai/bim/module/drawing/data.py | 2 +- .../bonsai/bim/module/drawing/operator.py | 3 +- src/bonsai/bonsai/bim/module/drawing/prop.py | 2 +- src/bonsai/bonsai/core/drawing.py | 108 ++++++++++-------- src/bonsai/bonsai/tool/drawing.py | 44 ++++--- 5 files changed, 93 insertions(+), 66 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/data.py b/src/bonsai/bonsai/bim/module/drawing/data.py index fb03c98576..d89c6bd38c 100644 --- a/src/bonsai/bonsai/bim/module/drawing/data.py +++ b/src/bonsai/bonsai/bim/module/drawing/data.py @@ -118,7 +118,7 @@ class DrawingsData: return len([e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]) @classmethod - def location_hint(cls): + def location_hint(cls) -> list[tuple[tool.Drawing.LocationHintType, str, str]]: props = tool.Drawing.get_document_props() if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]: results = [("0", "Origin", "")] diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index cf3af011d7..d10d9d34b6 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -143,6 +143,8 @@ class AddDrawing(bpy.types.Operator, tool.Ifc.Operator): hint = props.location_hint if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]: hint = int(hint) + else: + assert hint in tool.Drawing.LOCATION_HINT_LITERALS core.add_drawing( tool.Ifc, tool.Collector, @@ -167,7 +169,6 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator): @classmethod def poll(cls, context): - props = tool.Drawing.get_document_props() if not tool.Drawing.get_active_drawing_item(): cls.poll_message_set("No drawing selected.") return False diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py index b574326105..48d7709b3d 100644 --- a/src/bonsai/bonsai/bim/module/drawing/prop.py +++ b/src/bonsai/bonsai/bim/module/drawing/prop.py @@ -465,7 +465,7 @@ class DocProperties(PropertyGroup): is_editing_schedules: bool is_editing_references: bool target_view: Literal["PLAN_VIEW", "ELEVATION_VIEW", "SECTION_VIEW", "REFLECTED_PLAN_VIEW", "MODEL_VIEW"] - location_hint: str + location_hint: tool.Drawing.LocationHintType drawings: bpy.types.bpy_prop_collection_idprop[Drawing] active_drawing_id: int active_drawing_index: int diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index eaed3d66c6..5ea86f39ad 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -27,16 +27,16 @@ if TYPE_CHECKING: import bonsai.tool as tool -def enable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: +def enable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.enable_editing_text(obj) drawing.import_text_attributes(obj) -def disable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: +def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.disable_editing_text(obj) -def edit_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: +def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.synchronise_ifc_and_text_attributes(obj) drawing.update_text_size_pset(obj) drawing.update_newline_at(obj) @@ -44,17 +44,20 @@ def edit_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.disable_editing_text(obj) -def enable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None: +def enable_editing_assigned_product(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.enable_editing_assigned_product(obj) drawing.import_assigned_product(obj) -def disable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None: +def disable_editing_assigned_product(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: drawing.disable_editing_assigned_product(obj) def edit_assigned_product( - ifc: tool.Ifc, drawing: tool.Drawing, obj: bpy.types.Object, product: Optional[ifcopenshell.entity_instance] = None + ifc: type[tool.Ifc], + drawing: type[tool.Drawing], + obj: bpy.types.Object, + product: Optional[ifcopenshell.entity_instance] = None, ) -> None: element = ifc.get_entity(obj) assert element @@ -70,16 +73,16 @@ def edit_assigned_product( drawing.disable_editing_assigned_product(obj) -def load_sheets(drawing: tool.Drawing) -> None: +def load_sheets(drawing: type[tool.Drawing]) -> None: drawing.import_sheets() drawing.enable_editing_sheets() -def disable_editing_sheets(drawing: tool.Drawing) -> None: +def disable_editing_sheets(drawing: type[tool.Drawing]) -> None: drawing.disable_editing_sheets() -def add_sheet(ifc: tool.Ifc, drawing: tool.Drawing, titleblock: ifcopenshell.entity_instance) -> None: +def add_sheet(ifc: type[tool.Ifc], drawing: type[tool.Drawing], titleblock: ifcopenshell.entity_instance) -> None: sheet = ifc.run("document.add_information") layout = ifc.run("document.add_reference", information=sheet) titleblock_reference = ifc.run("document.add_reference", information=sheet) @@ -105,7 +108,7 @@ def add_sheet(ifc: tool.Ifc, drawing: tool.Drawing, titleblock: ifcopenshell.ent drawing.import_sheets() -def regenerate_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: +def regenerate_sheet(drawing: type[tool.Drawing], sheet: ifcopenshell.entity_instance) -> None: titleblock_uri = drawing.get_document_uri(sheet, "TITLEBLOCK") drawing.create_svg_sheet(sheet, drawing.sanitise_filename(Path(titleblock_uri).stem)) try: @@ -116,11 +119,11 @@ def regenerate_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) drawing.delete_file(path_layout) -def open_layout(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: +def open_layout(drawing: type[tool.Drawing], sheet: ifcopenshell.entity_instance) -> None: drawing.open_layout_svg(drawing.get_document_uri(sheet, "LAYOUT")) -def remove_sheet(ifc: tool.Ifc, drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: +def remove_sheet(ifc: type[tool.Ifc], drawing: type[tool.Drawing], sheet: ifcopenshell.entity_instance) -> None: for reference in drawing.get_document_references(sheet): if drawing.get_reference_description(reference) in ("LAYOUT", "SHEET", "REVISION", "RASTER"): uri = ifc.resolve_uri(drawing.get_document_uri(reference)) @@ -131,7 +134,11 @@ def remove_sheet(ifc: tool.Ifc, drawing: tool.Drawing, sheet: ifcopenshell.entit def rename_sheet( - ifc: tool.Ifc, drawing: tool.Drawing, sheet: ifcopenshell.entity_instance, identification: str, name: str + ifc: type[tool.Ifc], + drawing: type[tool.Drawing], + sheet: ifcopenshell.entity_instance, + identification: str, + name: str, ) -> None: if ifc.get_schema() == "IFC2X3": attributes = {"DocumentId": identification, "Name": name} @@ -159,31 +166,33 @@ def rename_sheet( def rename_reference( - ifc: tool.Ifc, drawing: tool.Drawing, reference: ifcopenshell.entity_instance, identification: str + ifc: type[tool.Ifc], drawing: type[tool.Drawing], reference: ifcopenshell.entity_instance, identification: str ) -> None: attributes = drawing.generate_reference_attributes(reference, Identification=identification) ifc.run("document.edit_reference", reference=reference, attributes=attributes) -def load_schedules(drawing: tool.Drawing) -> None: +def load_schedules(drawing: type[tool.Drawing]) -> None: drawing.import_documents("SCHEDULE") drawing.enable_editing_schedules() -def load_references(drawing: tool.Drawing) -> None: +def load_references(drawing: type[tool.Drawing]) -> None: drawing.import_documents("REFERENCE") drawing.enable_editing_references() -def disable_editing_schedules(drawing: tool.Drawing) -> None: +def disable_editing_schedules(drawing: type[tool.Drawing]) -> None: drawing.disable_editing_schedules() -def disable_editing_references(drawing: tool.Drawing) -> None: +def disable_editing_references(drawing: type[tool.Drawing]) -> None: drawing.disable_editing_references() -def add_document(ifc: tool.Ifc, drawing: tool.Drawing, document_type: tool.Drawing.DOCUMENT_TYPE, uri: str) -> None: +def add_document( + ifc: type[tool.Ifc], drawing: type[tool.Drawing], document_type: type[tool.Drawing].DOCUMENT_TYPE, uri: str +) -> None: document = ifc.run("document.add_information") reference = ifc.run("document.add_reference", information=document) name = drawing.get_path_filename(uri) @@ -197,46 +206,47 @@ def add_document(ifc: tool.Ifc, drawing: tool.Drawing, document_type: tool.Drawi def remove_document( - ifc: tool.Ifc, - drawing: tool.Drawing, - document_type: tool.Drawing.DOCUMENT_TYPE, + ifc: type[tool.Ifc], + drawing: type[tool.Drawing], + document_type: type[tool.Drawing].DOCUMENT_TYPE, document: ifcopenshell.entity_instance, ) -> None: ifc.run("document.remove_information", information=document) drawing.import_documents(document_type) -def open_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None: +def open_schedule(drawing: type[tool.Drawing], schedule: ifcopenshell.entity_instance) -> None: drawing.open_spreadsheet(drawing.get_document_uri(schedule)) -def open_reference(drawing: tool.Drawing, reference: ifcopenshell.entity_instance) -> None: +def open_reference(drawing: type[tool.Drawing], reference: ifcopenshell.entity_instance) -> None: drawing.open_svg(drawing.get_document_uri(reference)) def update_document_name( - ifc: tool.Ifc, drawing: tool.Drawing, document: ifcopenshell.entity_instance, name=None + ifc: type[tool.Ifc], drawing: type[tool.Drawing], document: ifcopenshell.entity_instance, name=None ) -> None: if drawing.get_name(document) != name: ifc.run("document.edit_information", information=document, attributes={"Name": name}) -def load_drawings(drawing: tool.Drawing) -> None: +def load_drawings(drawing: type[tool.Drawing]) -> None: drawing.import_drawings() drawing.enable_editing_drawings() -def disable_editing_drawings(drawing: tool.Drawing) -> None: +def disable_editing_drawings(drawing: type[tool.Drawing]) -> None: drawing.disable_editing_drawings() def add_drawing( - ifc: tool.Ifc, - collector: tool.Collector, - drawing: tool.Drawing, - target_view: Union[ifcopenshell.util.representation.TARGET_VIEW, None] = None, - location_hint: Union[str, None] = None, + ifc: type[tool.Ifc], + collector: type[tool.Collector], + drawing: type[tool.Drawing], + target_view: ifcopenshell.util.representation.TARGET_VIEW, + location_hint: Union[tool.Drawing.LocationHintLiteral, int], ) -> None: + assert location_hint is not None drawing_name = drawing.ensure_unique_drawing_name(drawing.generate_drawing_name(target_view, location_hint)) drawing_matrix = drawing.generate_drawing_matrix(target_view, location_hint) camera = drawing.create_camera(drawing_name, drawing_matrix, location_hint) @@ -295,10 +305,10 @@ def add_drawing( def duplicate_drawing( - ifc: tool.Ifc, - blender: tool.Blender, - drawing_tool: tool.Drawing, - geometry: tool.Geometry, + ifc: type[tool.Ifc], + blender: type[tool.Blender], + drawing_tool: type[tool.Drawing], + geometry: type[tool.Geometry], drawing: ifcopenshell.entity_instance, should_duplicate_annotations: bool = False, ) -> ifcopenshell.entity_instance: @@ -341,7 +351,9 @@ def duplicate_drawing( return new_drawing -def remove_drawing(ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance) -> None: +def remove_drawing( + ifc: type[tool.Ifc], drawing_tool: type[tool.Drawing], drawing: ifcopenshell.entity_instance +) -> None: if drawing_tool.is_active_drawing(drawing): drawing_tool.run_drawing_activate_model() @@ -370,7 +382,7 @@ def remove_drawing(ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopensh def update_drawing_name( - ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance, name: str + ifc: type[tool.Ifc], drawing_tool: type[tool.Drawing], drawing: ifcopenshell.entity_instance, name: str ) -> None: if drawing_tool.get_name(drawing) != name: ifc.run("attribute.edit_attributes", product=drawing, attributes={"Name": name}) @@ -406,9 +418,9 @@ def update_drawing_name( def add_annotation( - ifc: tool.Ifc, - collector: tool.Collector, - drawing_tool: tool.Drawing, + ifc: type[tool.Ifc], + collector: type[tool.Collector], + drawing_tool: type[tool.Drawing], drawing: ifcopenshell.entity_instance, object_type: str, relating_type: ifcopenshell.entity_instance, @@ -444,13 +456,16 @@ def add_annotation( return obj -def build_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None: +def build_schedule(drawing: type[tool.Drawing], schedule: ifcopenshell.entity_instance) -> None: drawing.create_svg_schedule(schedule) drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg")) def sync_references( - ifc: tool.Ifc, collector: tool.Collector, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + collector: type[tool.Collector], + drawing_tool: type[tool.Drawing], + drawing: ifcopenshell.entity_instance, ) -> None: if not drawing_tool.has_annotation(drawing): return @@ -496,12 +511,15 @@ def sync_references( drawing_tool.sync_object_representation(reference_obj) -def select_assigned_product(drawing: tool.Drawing, context: bpy.types.Context) -> None: +def select_assigned_product(drawing: type[tool.Drawing], context: bpy.types.Context) -> None: drawing.select_assigned_product(context) def activate_drawing_view( - ifc: tool.Ifc, blender: tool.Blender, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + blender: type[tool.Blender], + drawing_tool: type[tool.Drawing], + drawing: ifcopenshell.entity_instance, ) -> None: camera = ifc.get_object(drawing) if not camera: diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 7e7f0b4c31..6d15f1803a 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -51,7 +51,7 @@ from shapely.ops import unary_union from lxml import etree from mathutils import Vector, Matrix from fractions import Fraction -from typing import Optional, Union, Iterable, Any, Literal, Sequence, TYPE_CHECKING, NamedTuple +from typing import Optional, Union, Iterable, Any, Literal, Sequence, TYPE_CHECKING, NamedTuple, get_args from pathlib import Path if TYPE_CHECKING: @@ -68,6 +68,9 @@ if TYPE_CHECKING: class Drawing(bonsai.core.tool.Drawing): ANNOTATION_DATA_TYPE = Literal["empty", "curve", "mesh"] DOCUMENT_TYPE = Literal["SCHEDULE", "REFERENCE"] + LocationHintLiteral = Literal["PERSPECTIVE", "ORTHOGRAPHIC", "NORTH", "SOUTH", "EAST", "WEST"] + LOCATION_HINT_LITERALS = ("PERSPECTIVE", "ORTHOGRAPHIC", "NORTH", "SOUTH", "EAST", "WEST") + LocationHintType = Union[LocationHintLiteral, str] # ObjectType: annotation_name, description, icon, data_type # fmt: off @@ -296,7 +299,7 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def create_camera( - cls, name: str, matrix: Matrix, location_hint: Literal["PERSPECTIVE", "ORTHOGRAPHIC"] + cls, name: str, matrix: Matrix, location_hint: Union[LocationHintLiteral, int] ) -> bpy.types.Object: camera = bpy.data.objects.new(name, (camera_data := bpy.data.cameras.new(name))) props = cls.get_camera_props(camera_data) @@ -590,20 +593,21 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def generate_drawing_matrix( - cls, target_view: ifcopenshell.util.representation.TARGET_VIEW, location_hint: str + cls, + target_view: ifcopenshell.util.representation.TARGET_VIEW, + location_hint: Union[LocationHintLiteral, int], ) -> Matrix: x, y, z = (0, 0, 0) if location_hint == 0 else bpy.context.scene.cursor.matrix.translation - if target_view == "PLAN_VIEW": + if isinstance(location_hint, int): if location_hint: z = tool.Ifc.get_object(tool.Ifc.get().by_id(location_hint)).matrix_world.translation.z - return mathutils.Matrix(((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z + 1.6), (0, 0, 0, 1))) - elif target_view == "REFLECTED_PLAN_VIEW": - if location_hint: - z = tool.Ifc.get_object(tool.Ifc.get().by_id(location_hint)).matrix_world.translation.z - m = mathutils.Matrix() - m[2][2] = -1 - m.translation = (x, y, z + 1.6) - return m + if target_view == "PLAN_VIEW": + return mathutils.Matrix(((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z + 1.6), (0, 0, 0, 1))) + elif target_view == "REFLECTED_PLAN_VIEW": + m = mathutils.Matrix() + m[2][2] = -1 + m.translation = (x, y, z + 1.6) + return m return mathutils.Matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, -1, 0), (0, 0, 0, 1))) elif target_view == "ELEVATION_VIEW": if location_hint == "NORTH": @@ -1174,13 +1178,17 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def generate_drawing_name( - cls, target_view: ifcopenshell.util.representation.TARGET_VIEW, location_hint: str + cls, + target_view: ifcopenshell.util.representation.TARGET_VIEW, + location_hint: Union[LocationHintLiteral, int], ) -> str: - if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") and location_hint: - location = tool.Ifc.get().by_id(location_hint) - if target_view == "REFLECTED_PLAN_VIEW": - target_view = "RCP_VIEW" - return (location.Name or "UNNAMED").upper() + " " + target_view.split("_")[0] + if isinstance(location_hint, int): + if location_hint: + location = tool.Ifc.get().by_id(location_hint) + target_view_ = target_view + if target_view == "REFLECTED_PLAN_VIEW": + target_view_ = "RCP_VIEW" + return (location.Name or "UNNAMED").upper() + " " + target_view_.split("_")[0] elif target_view in ("SECTION_VIEW", "ELEVATION_VIEW") and location_hint: return location_hint + " " + target_view.split("_")[0] elif target_view == "MODEL_VIEW" and location_hint: