diff --git a/src/bonsai/bonsai/bim/module/layer/prop.py b/src/bonsai/bonsai/bim/module/layer/prop.py index acdeb79163..6f0dbd7c9c 100644 --- a/src/bonsai/bonsai/bim/module/layer/prop.py +++ b/src/bonsai/bonsai/bim/module/layer/prop.py @@ -30,6 +30,7 @@ from bpy.props import ( FloatVectorProperty, CollectionProperty, ) +from typing import TYPE_CHECKING def update_layer_property(self: "Layer", context: bpy.types.Context, *, property: str) -> None: @@ -74,3 +75,11 @@ class BIMLayerProperties(PropertyGroup): ), default="IfcPresentationLayerAssignment", ) + + if TYPE_CHECKING: + layer_attributes: bpy.types.bpy_prop_collection_idprop[Attribute] + active_layer_id: int + layers: bpy.types.bpy_prop_collection_idprop[Layer] + active_layer_index: int + is_editing: bool + layer_type: str diff --git a/src/bonsai/bonsai/bim/module/profile/prop.py b/src/bonsai/bonsai/bim/module/profile/prop.py index 391ddc75c5..543312b484 100644 --- a/src/bonsai/bonsai/bim/module/profile/prop.py +++ b/src/bonsai/bonsai/bim/module/profile/prop.py @@ -35,6 +35,7 @@ from bpy.props import ( FloatVectorProperty, CollectionProperty, ) +from typing import TYPE_CHECKING def get_profile_classes(self, context): @@ -53,6 +54,11 @@ class Profile(PropertyGroup): ifc_class: StringProperty(name="IFC Class") ifc_definition_id: IntProperty(name="IFC Definition ID") + if TYPE_CHECKING: + name: str + ifc_class: str + ifc_definition_id: int + def update_active_profile_index(self, context): ProfileData.data["active_profile_users"] = ProfileData.active_profile_users() diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index 66fda4a09b..da97493653 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -257,7 +257,7 @@ class BIM_PT_styles(Panel): class BIM_UL_styles(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + def draw_item(self, context, layout: bpy.types.UILayout, data, item, icon, active_data, active_property): if item: row = layout.row(align=True) row.prop(item, "name", text="", emboss=False) diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index cbb867eebc..388e361fae 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -488,6 +488,11 @@ class Ifc: def get_all_element_occurrences(cls, element): pass +@interface +class Layer: + pass + + @interface class Library: def clear_editing_mode(cls): pass diff --git a/src/bonsai/bonsai/tool/__init__.py b/src/bonsai/bonsai/tool/__init__.py index 2b52464841..6e3fc32944 100644 --- a/src/bonsai/bonsai/tool/__init__.py +++ b/src/bonsai/bonsai/tool/__init__.py @@ -39,6 +39,7 @@ from bonsai.tool.georeference import Georeference from bonsai.tool.ifc import Ifc from bonsai.tool.ifcgit import IfcGit from bonsai.tool.ifcgit import IfcGitRepo +from bonsai.tool.layer import Layer from bonsai.tool.library import Library from bonsai.tool.loader import Loader from bonsai.tool.material import Material diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 2056af0fc2..a07e4b203c 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +from __future__ import annotations import bpy import bmesh import struct @@ -56,9 +57,14 @@ from typing_extensions import TypeIs if TYPE_CHECKING: from bonsai.bim.prop import Attribute + from bonsai.bim.module.geometry.prop import BIMObjectGeometryProperties class Geometry(bonsai.core.tool.Geometry): + @classmethod + def get_object_geometry_props(cls, object: bpy.types.Object) -> BIMObjectGeometryProperties: + return object.BIMGeometryProperties + @classmethod def change_object_data(cls, obj: bpy.types.Object, data: bpy.types.ID, is_global: bool = False) -> None: if is_global: diff --git a/src/bonsai/bonsai/tool/layer.py b/src/bonsai/bonsai/tool/layer.py new file mode 100644 index 0000000000..7b5775d20d --- /dev/null +++ b/src/bonsai/bonsai/tool/layer.py @@ -0,0 +1,32 @@ +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . + +from __future__ import annotations +import bpy +import bonsai.core.tool +import bonsai.tool as tool +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from bonsai.bim.module.layer.prop import BIMLayerProperties + + +class Layer(bonsai.core.tool.Layer): + @classmethod + def get_layer_props(cls) -> BIMLayerProperties: + return bpy.context.scene.BIMLayerProperties diff --git a/src/bonsai/bonsai/tool/profile.py b/src/bonsai/bonsai/tool/profile.py index 18add8c0bc..06c1f58faf 100644 --- a/src/bonsai/bonsai/tool/profile.py +++ b/src/bonsai/bonsai/tool/profile.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +from __future__ import annotations import bpy import ifcopenshell import ifcopenshell.api.profile @@ -28,7 +29,10 @@ import bonsai.core.tool import bonsai.tool as tool import PIL.ImageDraw from bonsai.bim.module.model.decorator import ProfileDecorator -from typing import Union +from typing import Union, TYPE_CHECKING + +if TYPE_CHECKING: + import bonsai.bim.module.profile.prop class Profile(bonsai.core.tool.Profile): @@ -111,7 +115,7 @@ class Profile(bonsai.core.tool.Profile): return new_profile @classmethod - def get_active_profile_ui(cls) -> Union[bpy.types.PropertyGroup, None]: + def get_active_profile_ui(cls) -> Union[bonsai.bim.module.profile.prop.Profile, None]: props = bpy.context.scene.BIMProfileProperties index = props.active_profile_index if len(props.profiles) > index >= 0: diff --git a/src/bonsai/bonsai/tool/sequence.py b/src/bonsai/bonsai/tool/sequence.py index 0ffed5fc4c..343a0338f2 100644 --- a/src/bonsai/bonsai/tool/sequence.py +++ b/src/bonsai/bonsai/tool/sequence.py @@ -38,7 +38,7 @@ import bonsai.bim.helper from dateutil import parser from datetime import datetime from datetime import time as datetime_time -from typing import Optional, Any, Union, Literal, TYPE_CHECKING +from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable if TYPE_CHECKING: import bonsai.bim.prop @@ -705,14 +705,14 @@ class Sequence(bonsai.core.tool.Sequence): return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes) @classmethod - def select_products(cls, products): + def select_products(cls, products: Iterable[ifcopenshell.entity_instance]) -> None: [obj.select_set(False) for obj in bpy.context.selected_objects] for product in products: obj = tool.Ifc.get_object(product) obj.select_set(True) if obj else None @classmethod - def add_task_column(cls, column_type: str, name: str, data_type: str): + def add_task_column(cls, column_type: str, name: str, data_type: str) -> None: props = bpy.context.scene.BIMWorkScheduleProperties new = props.columns.add() new.name = f"{column_type}.{name}" diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py index fcf8799707..e26d051eef 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py @@ -18,7 +18,7 @@ import ifcopenshell import ifcopenshell.util.representation -from typing import Optional +from typing import Optional, Any def add_context( @@ -191,6 +191,9 @@ def add_context( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): if not self.settings["parent"]: if self.settings["context_type"] == "Plan": diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py index c4e574ca1f..37bdac03bc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.util.unit def add_layer( diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 3407cacfd1..e051ebf07c 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -24,12 +24,13 @@ import zipfile import functools import ifcopenshell from pathlib import Path -from typing import Any from typing import Callable from typing import Generator from typing import Optional from typing import TYPE_CHECKING from typing import Union +from typing import overload +from typing import Literal from . import ifcopenshell_wrapper from .entity_instance import entity_instance @@ -558,9 +559,33 @@ class file: return [entity_instance(e, self) for e in fn(inst.wrapped_data, max_levels)] + @overload def get_inverse( - self, inst: ifcopenshell.entity_instance, allow_duplicate: bool = False, with_attribute_indices: bool = False - ) -> list[ifcopenshell.entity_instance]: + self, + inst: ifcopenshell.entity_instance, + allow_duplicate: Literal[False] = False, + with_attribute_indices: bool = False, + ) -> set[ifcopenshell.entity_instance]: ... + @overload + def get_inverse( + self, + inst: ifcopenshell.entity_instance, + allow_duplicate: Literal[True], + with_attribute_indices: bool = False, + ) -> list[ifcopenshell.entity_instance]: ... + @overload + def get_inverse( + self, + inst: ifcopenshell.entity_instance, + allow_duplicate: bool, + with_attribute_indices: bool = False, + ) -> Union[list[ifcopenshell.entity_instance], set[ifcopenshell.entity_instance]]: ... + def get_inverse( + self, + inst: ifcopenshell.entity_instance, + allow_duplicate: bool = False, + with_attribute_indices: bool = False, + ) -> Union[list[ifcopenshell.entity_instance], set[ifcopenshell.entity_instance]]: """Return a list of entities that reference this entity Warning: this is a slow function, especially when there is a large @@ -569,12 +594,10 @@ class file: consider using :func:`get_total_inverses`. :param inst: The entity instance to get inverse relationships - :type inst: ifcopenshell.entity_instance :param allow_duplicate: Returns a `list` when True, `set` when False :param with_attribute_indices: Returns pairs of where i[idx] is inst or contains inst. Requires allow_duplicate=True - :returns: A list of ifcopenshell.entity_instance objects - :rtype: list[ifcopenshell.entity_instance] + :returns: A list or set of ifcopenshell.entity_instance objects. """ if with_attribute_indices and not allow_duplicate: raise ValueError("with_attribute_indices requires allow_duplicate to be True") @@ -584,6 +607,7 @@ class file: if allow_duplicate: if with_attribute_indices: idxs = self.wrapped_data.get_inverse_indices(inst.wrapped_data) + # TODO: include in typing. return list(zip(inverses, idxs)) else: return inverses