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