From 062bf8e60b5f84907ef802ef2b7e5214a1c09f0a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 5 Sep 2025 10:50:34 +0500 Subject: [PATCH] typing --- .../bonsai/bim/module/classification/ui.py | 9 +++-- src/bonsai/bonsai/bim/module/root/operator.py | 1 + src/ifcopenshell-python/ifcopenshell/sql.py | 34 ++++++++++++++++--- src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py | 4 +-- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/classification/ui.py b/src/bonsai/bonsai/bim/module/classification/ui.py index 5c4b3576b7..7cbfb0ed93 100644 --- a/src/bonsai/bonsai/bim/module/classification/ui.py +++ b/src/bonsai/bonsai/bim/module/classification/ui.py @@ -73,6 +73,7 @@ class BIM_PT_classifications(Panel): self.draw_ui(classification) def draw_add_manual_ui(self, context): + assert self.layout if self.props.is_adding: bonsai.bim.helper.draw_attributes(self.props.classification_attributes, self.layout) row = self.layout.row(align=True) @@ -83,10 +84,12 @@ class BIM_PT_classifications(Panel): row.operator("bim.enable_adding_manual_classification", text="Add Classification", icon="ADD") def draw_add_bsdd_ui(self, context): + assert self.layout row = self.layout.row() row.operator("bim.add_classification_from_bsdd", icon="ADD") def draw_add_file_ui(self, context): + assert self.layout if ClassificationsData.data["has_classification_file"]: row = self.layout.row(align=True) row.prop(self.props, "available_classifications", text="") @@ -97,13 +100,15 @@ class BIM_PT_classifications(Panel): row.label(text="No Active Classification Library") row.operator("bim.load_classification_library", text="", icon="IMPORT") - def draw_editable_ui(self): + def draw_editable_ui(self) -> None: + assert self.layout row = self.layout.row(align=True) row.operator("bim.edit_classification", text="Save changes", icon="CHECKMARK") row.operator("bim.disable_editing_classification", text="", icon="CANCEL") bonsai.bim.helper.draw_attributes(self.props.classification_attributes, self.layout) - def draw_ui(self, classification): + def draw_ui(self, classification: dict[str, Any]) -> None: + assert self.layout row = self.layout.row(align=True) row.label(text=classification["Name"], icon="ASSET_MANAGER") if not self.props.active_classification_id: diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index e9ace873e2..8eb2119993 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -203,6 +203,7 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator): predefined_type: str userdefined_type: str context_id: int + props_to_pset: bool should_add_representation: bool ifc_representation_class: str diff --git a/src/ifcopenshell-python/ifcopenshell/sql.py b/src/ifcopenshell-python/ifcopenshell/sql.py index c2f8185a8e..d05af04d56 100644 --- a/src/ifcopenshell-python/ifcopenshell/sql.py +++ b/src/ifcopenshell-python/ifcopenshell/sql.py @@ -18,11 +18,13 @@ from __future__ import annotations import re import json +import numpy as np +import numpy.typing as npt import ifcopenshell import ifcopenshell.util.attribute import ifcopenshell.util.schema from pathlib import Path -from typing import Any, NoReturn, Union, Optional, TYPE_CHECKING +from typing import Any, NoReturn, Union, Optional, TYPE_CHECKING, TypedDict from . import ifcopenshell_wrapper from .file import file from .entity_instance import entity_instance @@ -36,6 +38,30 @@ except ImportError as e: print(f"No SQL support: {e}") +class GeometryCache(TypedDict): + shapes: dict[int, GeometryCacheShape] + geometry: dict[str, GeometryCacheGeometry] + + +class GeometryCacheShape(TypedDict): + co: list[float] + """Object location.""" + matrix: npt.NDArray[np.float64] + geometry: Union[str, None] + """Element's geometry id (same value as in ``Representation.id``). + + Is set to ``None` when no geometry is available for the element. + """ + + +class GeometryCacheGeometry(TypedDict): + verts: npt.NDArray[np.float64] + edges: npt.NDArray[np.int32] + faces: npt.NDArray[np.int32] + material_ids: npt.NDArray[np.int32] + materials: list[int] + + class sqlite(file): mvd_str: str """As in `header.file_description.description`.""" @@ -235,15 +261,15 @@ class sqlite(file): return True return False - def get_geometry(self, ids: list[int]) -> dict[str, dict]: + def get_geometry(self, ids: list[int]) -> GeometryCache: import numpy as np ids_csv = ",".join(map(str, ids)) query = f"SELECT ifc_id, x, y, z, matrix, geometry, verts, edges, faces, material_ids, materials FROM shape LEFT JOIN geometry ON shape.geometry = geometry.id WHERE `ifc_id` IN ({ids_csv})" self.cursor.execute(query) rows = self.cursor.fetchall() - shapes = {} - geometry = {} + shapes: dict[int, GeometryCacheShape] = {} + geometry: dict[str, GeometryCacheGeometry] = {} for row in rows: if row["geometry"] and row["geometry"] not in geometry: # Same data types as in ifcopenshell.util.shape. diff --git a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py index 4d71dbd96d..d683d7ca7e 100644 --- a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py +++ b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py @@ -140,7 +140,7 @@ class Patcher(ifcpatch.BasePatcher): self.should_skip_geometry_data = should_skip_geometry_data geometry_rows: dict[str, tuple[str, bytes, bytes, bytes, bytes, str]] - shape_rows: dict[int, tuple[int, list[float], list[float], list[float], bytes, str]] + shape_rows: dict[int, tuple[int, float, float, float, bytes, Union[str, None]]] def get_output(self) -> Union[str, None]: """Return resulting database filepath for sqlite and ``None`` for mysql.""" @@ -576,7 +576,7 @@ class Patcher(ifcpatch.BasePatcher): if element.id() not in self.shape_rows and (placement := getattr(element, "ObjectPlacement", None)): m = ifcopenshell.util.placement.get_local_placement(placement) x, y, z = m[:, 3][0:3].tolist() - self.shape_rows[element.id()] = [element.id(), x, y, z, m.tobytes(), None] + self.shape_rows[element.id()] = (element.id(), x, y, z, m.tobytes(), None) if self.sql_type == "sqlite": if rows: