diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py
index 0251b19c94..b33b0127d5 100644
--- a/src/bonsai/bonsai/bim/import_ifc.py
+++ b/src/bonsai/bonsai/bim/import_ifc.py
@@ -29,6 +29,7 @@ import numpy.typing as npt
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
+import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.geolocation
@@ -1032,9 +1033,9 @@ class IfcImporter:
def create_curve(
self,
element: ifcopenshell.entity_instance,
- shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
+ shape: Union[W.Triangulation, W.TriangulationElement],
) -> bpy.types.Curve:
- if isinstance(shape, ifcopenshell.geom.ShapeElementType):
+ if isinstance(shape, W.TriangulationElement):
geometry = shape.geometry
else:
geometry = shape
diff --git a/src/bonsai/bonsai/bim/module/bsdd/operator.py b/src/bonsai/bonsai/bim/module/bsdd/operator.py
index b9161b3d40..337b242147 100644
--- a/src/bonsai/bonsai/bim/module/bsdd/operator.py
+++ b/src/bonsai/bonsai/bim/module/bsdd/operator.py
@@ -41,7 +41,7 @@ class SearchBSDDClassifications(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- total = core.search_bsdd_class(tool.Bsdd, context.scene.BIMBSDDProperties.keyword)
+ total = core.search_bsdd_class(tool.Bsdd, tool.Bsdd.get_bsdd_props().keyword)
self.report({"INFO"}, f"{total} bSDD classes found.")
return {"FINISHED"}
@@ -71,7 +71,7 @@ class SearchBSDDProperties(bpy.types.Operator):
def execute(self, context):
props = tool.Bsdd.get_bsdd_props()
- core.search_bsdd_properties(tool.Bsdd, context.scene.BIMBSDDProperties.keyword, self.obj, self.obj_type)
+ core.search_bsdd_properties(tool.Bsdd, tool.Bsdd.get_bsdd_props().keyword, self.obj, self.obj_type)
self.report({"INFO"}, f"{len(props.properties)} bSDD properties found.")
return {"FINISHED"}
diff --git a/src/bonsai/bonsai/bim/module/bsdd/prop.py b/src/bonsai/bonsai/bim/module/bsdd/prop.py
index bdf84fe8d9..1895750387 100644
--- a/src/bonsai/bonsai/bim/module/bsdd/prop.py
+++ b/src/bonsai/bonsai/bim/module/bsdd/prop.py
@@ -31,7 +31,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
-from typing import Union
+from typing import Union, TYPE_CHECKING, Literal
def get_active_dictionary(self, context):
@@ -54,27 +54,41 @@ def update_active_class_index(self: "BIMBSDDProperties", context: bpy.types.Cont
class BSDDDictionary(PropertyGroup):
- name: StringProperty(name="Name")
uri: StringProperty(name="URI")
default_language_code: StringProperty(name="Language")
organization_name_owner: StringProperty(name="Organization")
status: StringProperty(name="Status")
version: StringProperty(name="Version")
is_active: BoolProperty(
- name="Is Active", description="Enable to search with this dictionary", default=False, update=update_is_active
+ name="Is Active",
+ description="Enable to search with this dictionary",
+ default=False,
+ update=update_is_active,
)
+ if TYPE_CHECKING:
+ uri: str
+ default_language_code: str
+ organization_name_owner: str
+ status: str
+ version: str
+ is_active: bool
+
class BSDDClassification(PropertyGroup):
- name: StringProperty(name="Name")
reference_code: StringProperty(name="Reference Code")
uri: StringProperty(name="URI")
dictionary_name: StringProperty(name="Dictionary Name")
dictionary_namespace_uri: StringProperty(name="Dictionary Namespace URI")
+ if TYPE_CHECKING:
+ reference_code: str
+ uri: str
+ dictionary_name: str
+ dictionary_namespace_uri: str
+
class BSDDProperty(PropertyGroup):
- name: StringProperty(name="Name")
code: StringProperty(name="Code")
uri: StringProperty(name="URI")
pset: StringProperty(name="Pset")
@@ -82,11 +96,19 @@ class BSDDProperty(PropertyGroup):
name="Is Selected", description="Select to add or edit this property", default=False, update=update_is_selected
)
+ if TYPE_CHECKING:
+ code: str
+ uri: str
+ pset: str
+ is_selected: bool
+
class BSDDPset(PropertyGroup):
- name: StringProperty(name="Name")
properties: CollectionProperty(name="Properties", type=Attribute)
+ if TYPE_CHECKING:
+ properties: bpy.types.bpy_prop_collection_idprop[Attribute]
+
class BIMBSDDProperties(PropertyGroup):
active_dictionary: StringProperty(name="Active Dictionary")
@@ -131,6 +153,28 @@ class BIMBSDDProperties(PropertyGroup):
)
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
+ if TYPE_CHECKING:
+ active_dictionary: str
+ active_dictionary: str
+ active_uri: str
+ dictionaries: bpy.types.bpy_prop_collection_idprop[BSDDDictionary]
+ active_dictionary_index: int
+ classifications: bpy.types.bpy_prop_collection_idprop[BSDDClassification]
+ active_classification_index: int
+ property_filter_mode: Literal["CLASS", "KEYWORD"]
+ classes: bpy.types.bpy_prop_collection_idprop[BSDDClassification]
+ active_class_index: int
+ properties: bpy.types.bpy_prop_collection_idprop[BSDDProperty]
+ active_property_index: int
+ selected_properties: bpy.types.bpy_prop_collection_idprop[Attribute]
+ keyword: str
+ should_filter_ifc_class: bool
+ use_only_ifc_properties: bool
+ load_preview_dictionaries: bool
+ load_inactive_dictionaries: bool
+ load_test_dictionaries: bool
+ classification_psets: bpy.types.bpy_prop_collection_idprop[BSDDPset]
+
@property
def active_class(self) -> Union[BSDDClassification, None]:
return tool.Blender.get_active_uilist_element(self.classes, self.active_class_index)
diff --git a/src/bonsai/bonsai/bim/module/bsdd/ui.py b/src/bonsai/bonsai/bim/module/bsdd/ui.py
index 7a8eb7a8c7..638d4d1d52 100644
--- a/src/bonsai/bonsai/bim/module/bsdd/ui.py
+++ b/src/bonsai/bonsai/bim/module/bsdd/ui.py
@@ -16,9 +16,15 @@
# 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.tool as tool
from bonsai.bim.module.bsdd.data import BSDDData
from bpy.types import Panel, UIList
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDDictionary, BSDDClassification, BSDDProperty
class BIM_PT_bsdd(Panel):
@@ -33,7 +39,8 @@ class BIM_PT_bsdd(Panel):
def draw(self, context):
if not BSDDData.is_loaded:
BSDDData.load()
- props = context.scene.BIMBSDDProperties
+ props = tool.Bsdd.get_bsdd_props()
+ assert self.layout
layout = self.layout
if len(props.dictionaries):
row = self.layout.row()
@@ -70,9 +77,17 @@ class BIM_PT_bsdd(Panel):
class BIM_UL_bsdd_dictionaries(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMBSDDProperties,
+ item: BSDDDictionary,
+ icon,
+ active_data,
+ active_propname,
+ ) -> None:
if item:
- props = context.scene.BIMBSDDProperties
row = layout.row(align=True)
if item.status != "Active":
row.label(
@@ -86,7 +101,16 @@ class BIM_UL_bsdd_dictionaries(UIList):
class BIM_UL_bsdd_classifications(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMBSDDProperties,
+ item: BSDDClassification,
+ icon,
+ active_data,
+ active_propname,
+ ) -> None:
if item:
row = layout.row(align=True)
row.label(text=item.reference_code)
@@ -95,7 +119,16 @@ class BIM_UL_bsdd_classifications(UIList):
class BIM_UL_bsdd_classes(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMBSDDProperties,
+ item: BSDDClassification,
+ icon,
+ active_data,
+ active_propname,
+ ) -> None:
if item:
row = layout.row(align=True)
row.label(text=item.name)
@@ -103,7 +136,16 @@ class BIM_UL_bsdd_classes(UIList):
class BIM_UL_bsdd_properties(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMBSDDProperties,
+ item: BSDDProperty,
+ icon,
+ active_data,
+ active_propname,
+ ) -> None:
if item:
row = layout.row(align=True)
name = item.name
diff --git a/src/bonsai/bonsai/bim/module/classification/data.py b/src/bonsai/bonsai/bim/module/classification/data.py
index d47c64881e..86c7d27443 100644
--- a/src/bonsai/bonsai/bim/module/classification/data.py
+++ b/src/bonsai/bonsai/bim/module/classification/data.py
@@ -83,7 +83,7 @@ class ReferencesData:
def active_classification_library(cls):
if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"):
return False
- props = bpy.context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
return name
diff --git a/src/bonsai/bonsai/bim/module/classification/operator.py b/src/bonsai/bonsai/bim/module/classification/operator.py
index bead1b8d6a..2d7327abb5 100644
--- a/src/bonsai/bonsai/bim/module/classification/operator.py
+++ b/src/bonsai/bonsai/bim/module/classification/operator.py
@@ -47,7 +47,7 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
ifcopenshell.api.classification.add_classification(
tool.Ifc.get(),
classification=IfcStore.classification_file.by_id(int(props.available_classifications)),
@@ -60,7 +60,7 @@ class AddManualClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
attributes = bonsai.bim.helper.export_attributes(props.classification_attributes)
classification = ifcopenshell.api.classification.add_classification(tool.Ifc.get(), classification="Unnamed")
ifcopenshell.api.classification.edit_classification(
@@ -84,7 +84,7 @@ class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
- props = context.scene.BIMClassificationReferenceProperties
+ props = tool.Classification.get_classification_reference_props()
attributes = bonsai.bim.helper.export_attributes(props.reference_attributes)
products = [
tool.Ifc.get().by_id(ifc_definition_id)
@@ -110,7 +110,7 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- cprops = context.scene.BIMClassificationProperties
+ cprops = tool.Classification.get_classification_props()
bprops = tool.Bsdd.get_bsdd_props()
if cprops.classification_source == "BSDD":
dictionaries = [d.uri for d in bprops.dictionaries if d.is_active]
@@ -142,7 +142,7 @@ class EnableAddingManualClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
props.is_adding = True
props.active_classification_id = 0
props.classification_attributes.clear()
@@ -156,7 +156,7 @@ class DisableAddingManualClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
props.is_adding = False
return {"FINISHED"}
@@ -167,7 +167,7 @@ class EnableAddingManualClassificationReference(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMClassificationReferenceProperties
+ props = tool.Classification.get_classification_reference_props()
props.is_adding = True
props.reference_attributes.clear()
bonsai.bim.helper.import_attributes2("IfcClassificationReference", props.reference_attributes)
@@ -180,7 +180,7 @@ class DisableAddingManualClassificationReference(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMClassificationReferenceProperties
+ props = tool.Classification.get_classification_reference_props()
props.is_adding = False
return {"FINISHED"}
@@ -203,7 +203,7 @@ class EnableEditingClassification(bpy.types.Operator):
new.string_value = "" if new.is_null else json.dumps(data[name])
return True
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
props.classification_attributes.clear()
bonsai.bim.helper.import_attributes2(
tool.Ifc.get().by_id(self.classification), props.classification_attributes, callback
@@ -218,7 +218,8 @@ class DisableEditingClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMClassificationProperties.active_classification_id = 0
+ props = tool.Classification.get_classification_props()
+ props.active_classification_id = 0
return {"FINISHED"}
@@ -246,7 +247,7 @@ class EditClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
def callback(attributes, prop):
if prop.name == "ReferenceTokens":
@@ -271,7 +272,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
- props = context.scene.BIMClassificationReferenceProperties
+ props = tool.Classification.get_classification_reference_props()
props.reference_attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.reference), props.reference_attributes)
props.active_reference_id = self.reference
@@ -285,7 +286,7 @@ class DisableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
- context.scene.BIMClassificationReferenceProperties.active_reference_id = 0
+ tool.Classification.get_classification_reference_props().active_reference_id = 0
return {"FINISHED"}
@@ -337,7 +338,7 @@ class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
obj: bpy.props.StringProperty()
def _execute(self, context):
- props = context.scene.BIMClassificationReferenceProperties
+ props = tool.Classification.get_classification_reference_props()
attributes = bonsai.bim.helper.export_attributes(props.reference_attributes)
ifc_file = tool.Ifc.get()
ifcopenshell.api.classification.edit_reference(
@@ -364,7 +365,7 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
classification = None
classification_name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
for element in tool.Ifc.get().by_type("IfcClassification"):
@@ -405,8 +406,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
- props = context.scene.BIMClassificationProperties
- bprops = context.scene.BIMBSDDProperties
+ bprops = tool.Bsdd.get_bsdd_props()
use_only_ifc_properties: bool = bprops.use_only_ifc_properties
bsdd_classification = bprops.classifications[bprops.active_classification_index]
@@ -487,7 +487,7 @@ class ChangeClassificationLevel(bpy.types.Operator):
parent_id: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
props.available_library_references.clear()
for reference in IfcStore.classification_file.by_id(self.parent_id).HasReferences:
new = props.available_library_references.add()
@@ -509,6 +509,6 @@ class DisableEditingClassificationReferences(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMClassificationProperties
+ props = tool.Classification.get_classification_props()
props.available_library_references.clear()
return {"FINISHED"}
diff --git a/src/bonsai/bonsai/bim/module/classification/prop.py b/src/bonsai/bonsai/bim/module/classification/prop.py
index 0c09799285..c6b12d9893 100644
--- a/src/bonsai/bonsai/bim/module/classification/prop.py
+++ b/src/bonsai/bonsai/bim/module/classification/prop.py
@@ -17,6 +17,7 @@
# along with Bonsai. If not, see .
import bpy
+import bonsai.tool as tool
from bonsai.bim.prop import Attribute
from bonsai.bim.module.classification.data import ClassificationsData, ClassificationReferencesData
from bpy.types import PropertyGroup
@@ -30,9 +31,12 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
+from typing import TYPE_CHECKING
-def get_available_classifications(self, context):
+def get_available_classifications(
+ self: "BIMClassificationProperties", context: bpy.types.Context
+) -> tool.Blender.BLENDER_ENUM_ITEMS:
if not ClassificationsData.is_loaded:
ClassificationsData.load()
return ClassificationsData.data["available_classifications"]
@@ -44,19 +48,26 @@ def get_classifications(self, context):
return ClassificationReferencesData.data["classifications"]
-def get_classification_source(self, context):
+def get_classification_source(
+ self: "BIMClassificationProperties", context: bpy.types.Context
+) -> tool.Blender.BLENDER_ENUM_ITEMS:
if not ClassificationsData.is_loaded:
ClassificationsData.load()
return ClassificationsData.data["classification_source"]
class ClassificationReference(PropertyGroup):
- name: StringProperty(name="Name")
identification: StringProperty(name="Identification")
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_references: BoolProperty(name="Has References")
referenced_source: IntProperty(name="IFC Definition ID")
+ if TYPE_CHECKING:
+ identification: str
+ ifc_definition_id: int
+ has_references: bool
+ referenced_source: int
+
class BIMClassificationProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
@@ -68,9 +79,25 @@ class BIMClassificationProperties(PropertyGroup):
active_library_referenced_source: IntProperty(name="Active Library Referenced Source")
active_library_reference_index: IntProperty(name="Active Library Reference Index")
+ if TYPE_CHECKING:
+ is_adding: bool
+ classification_source: str
+ available_classifications: str
+ classification_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ active_classification_id: int
+ available_library_references: bpy.types.bpy_prop_collection_idprop[ClassificationReference]
+ active_library_referenced_source: int
+ active_library_reference_index: int
+
class BIMClassificationReferenceProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classifications: EnumProperty(items=get_classifications, name="Classifications")
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
active_reference_id: IntProperty(name="Active Reference Id")
+
+ if TYPE_CHECKING:
+ is_adding: bool
+ classifications: str
+ reference_attributes: bpy.types.bpy_prop_collection_idprop
+ active_reference_id: int
diff --git a/src/bonsai/bonsai/bim/module/classification/ui.py b/src/bonsai/bonsai/bim/module/classification/ui.py
index fc808a206b..e2a27edbfe 100644
--- a/src/bonsai/bonsai/bim/module/classification/ui.py
+++ b/src/bonsai/bonsai/bim/module/classification/ui.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 bonsai.bim.helper
import bonsai.tool as tool
@@ -27,6 +28,10 @@ from bonsai.bim.module.classification.data import (
MaterialClassificationsData,
CostClassificationsData,
)
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.classification.prop import BIMClassificationProperties, ClassificationReference
class BIM_PT_classifications(Panel):
@@ -46,7 +51,8 @@ class BIM_PT_classifications(Panel):
if not ClassificationsData.is_loaded:
ClassificationsData.load()
- self.props = context.scene.BIMClassificationProperties
+ self.props = tool.Classification.get_classification_props()
+ assert self.layout
row = self.layout.row(align=True)
row.label(text="Source", icon="OUTLINER")
@@ -110,9 +116,9 @@ class ReferenceUI:
def draw_ui(self, context):
obj = context.active_object
- self.sprops = context.scene.BIMClassificationProperties
- self.bprops = context.scene.BIMBSDDProperties
- self.props = context.scene.BIMClassificationReferenceProperties
+ self.sprops = tool.Classification.get_classification_props()
+ self.bprops = tool.Bsdd.get_bsdd_props()
+ self.props = tool.Classification.get_classification_reference_props()
self.file = tool.Ifc.get()
self.draw_add_ui(context)
@@ -315,7 +321,16 @@ class BIM_PT_cost_classifications(Panel, ReferenceUI):
class BIM_UL_classifications(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMClassificationProperties,
+ item: ClassificationReference,
+ icon,
+ active_data,
+ active_propname,
+ ):
if item:
if item.has_references:
op = layout.operator("bim.change_classification_level", text="", icon="DISCLOSURE_TRI_RIGHT")
diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py
index 9f559b4556..b119a40749 100644
--- a/src/bonsai/bonsai/bim/module/pset/ui.py
+++ b/src/bonsai/bonsai/bim/module/pset/ui.py
@@ -255,7 +255,7 @@ class BIM_PT_object_psets(Panel):
ObjectPsetsData.load()
props = context.active_object.PsetProperties
- self.bprops = context.scene.BIMBSDDProperties
+ self.bprops = tool.Bsdd.get_bsdd_props()
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
if props.pset_name != "BBIM_BSDD" and not props.pset_name.startswith(tool.Bsdd.identifier_url):
diff --git a/src/bonsai/bonsai/core/bsdd.py b/src/bonsai/bonsai/core/bsdd.py
index 5c16b7e9ec..34ff455091 100644
--- a/src/bonsai/bonsai/core/bsdd.py
+++ b/src/bonsai/bonsai/core/bsdd.py
@@ -26,20 +26,20 @@ if TYPE_CHECKING:
import bonsai.tool as tool
-def import_bsdd_classes(bsdd: tool.Bsdd, obj, obj_type) -> int:
+def import_bsdd_classes(bsdd: type[tool.Bsdd], obj, obj_type) -> int:
return bsdd.import_classes(obj, obj_type)
-def search_bsdd_properties(bsdd: tool.Bsdd, keyword: str, obj, obj_type) -> int:
+def search_bsdd_properties(bsdd: type[tool.Bsdd], keyword: str, obj, obj_type) -> int:
return bsdd.import_properties(obj, obj_type, keyword)
-def load_bsdd(bsdd: tool.Bsdd) -> None:
+def load_bsdd(bsdd: type[tool.Bsdd]) -> None:
bsdd.clear_dictionaries()
bsdd.create_dictionaries(bsdd.get_dictionaries())
-def search_bsdd_class(bsdd: tool.Bsdd, keyword: str) -> int:
+def search_bsdd_class(bsdd: type[tool.Bsdd], keyword: str) -> int:
bsdd.clear_classes()
related_entities = bsdd.get_related_ifc_entities()
return bsdd.search_class(keyword, related_entities)
diff --git a/src/bonsai/bonsai/core/model.py b/src/bonsai/bonsai/core/model.py
index 5372e662b2..c82de50e0e 100644
--- a/src/bonsai/bonsai/core/model.py
+++ b/src/bonsai/bonsai/core/model.py
@@ -32,7 +32,11 @@ if TYPE_CHECKING:
def unjoin_walls(
- ifc: tool.Ifc, blender: tool.Blender, geometry: tool.Geometry, joiner: DumbWallJoiner, model: tool.Model
+ ifc: type[tool.Ifc],
+ blender: type[tool.Blender],
+ geometry: type[tool.Geometry],
+ joiner: DumbWallJoiner,
+ model: type[tool.Model],
) -> None:
"""Unjoin selected walls."""
for obj in blender.get_selected_objects():
@@ -45,11 +49,11 @@ def unjoin_walls(
def extend_walls(
- ifc: tool.Ifc,
- blender: tool.Blender,
- geometry: tool.Geometry,
+ ifc: type[tool.Ifc],
+ blender: type[tool.Blender],
+ geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
- model: tool.Model,
+ model: type[tool.Model],
target: Vector,
connection: Optional[str] = None,
) -> None:
@@ -62,11 +66,11 @@ def extend_walls(
def join_walls_LV(
- ifc: tool.Ifc,
- blender: tool.Blender,
- geometry: tool.Geometry,
+ ifc: type[tool.Ifc],
+ blender: type[tool.Blender],
+ geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
- model: tool.Model,
+ model: type[tool.Model],
join_type: Literal["L", "V"] = "L",
) -> None:
selected_objs = [
@@ -86,7 +90,7 @@ def join_walls_LV(
joiner.connect(another_selected_object, active_obj)
-def offset_walls(ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, offset_type: OffsetType):
+def offset_walls(ifc: type[tool.Ifc], blender: type[tool.Blender], model: type[tool.Model], offset_type: OffsetType):
objs = [
obj
for obj in blender.get_selected_objects()
@@ -98,7 +102,11 @@ def offset_walls(ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, offset
def align_walls(
- ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, aligner: DumbWallAligner, align_type: AlignType
+ ifc: type[tool.Ifc],
+ blender: type[tool.Blender],
+ model: type[tool.Model],
+ aligner: DumbWallAligner,
+ align_type: AlignType,
):
reference_obj = blender.get_active_object(is_selected=True)
if not (e := ifc.get_entity(reference_obj) or not model.get_usage_type(e) == "LAYER2"):
@@ -122,7 +130,9 @@ def align_walls(
aligner.align_last_layer(obj)
-def align_objects(blender: tool.Blender, model: tool.Model, align_type: Literal["CENTER", "POSITIVE", "NEGATIVE"]):
+def align_objects(
+ blender: type[tool.Blender], model: type[tool.Model], align_type: Literal["CENTER", "POSITIVE", "NEGATIVE"]
+):
reference_obj = blender.get_active_object(is_selected=True)
objs = [o for o in blender.get_selected_objects() if o != reference_obj]
if not reference_obj or not objs:
@@ -131,9 +141,9 @@ def align_objects(blender: tool.Blender, model: tool.Model, align_type: Literal[
def extend_wall_to_slab(
- ifc: tool.Ifc,
- geometry: tool.Geometry,
- model: tool.Model,
+ ifc: type[tool.Ifc],
+ geometry: type[tool.Geometry],
+ model: type[tool.Model],
slab_obj: bpy.types.Object,
wall_objs: list[bpy.types.Object],
) -> None:
@@ -150,7 +160,11 @@ def extend_wall_to_slab(
def join_walls_TZ(
- ifc: tool.Ifc, blender: tool.Blender, geometry: tool.Geometry, joiner: DumbWallJoiner, model: tool.Model
+ ifc: type[tool.Ifc],
+ blender: type[tool.Blender],
+ geometry: type[tool.Geometry],
+ joiner: DumbWallJoiner,
+ model: type[tool.Model],
) -> None:
selected_objs = [
o
diff --git a/src/bonsai/bonsai/tool/bsdd.py b/src/bonsai/bonsai/tool/bsdd.py
index 5ee004327d..7f090546b5 100644
--- a/src/bonsai/bonsai/tool/bsdd.py
+++ b/src/bonsai/bonsai/tool/bsdd.py
@@ -1,3 +1,22 @@
+# Bonsai - OpenBIM Blender Add-on
+# Copyright (C) 2021 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 bonsai.core.tool
import bonsai.tool as tool
import bpy
@@ -8,6 +27,9 @@ import ifcopenshell.util.element
import ifcopenshell.util.classification
from typing import Any, Union, Optional, TYPE_CHECKING
+if TYPE_CHECKING:
+ from bonsai.bim.module.bsdd.prop import BIMBSDDProperties
+
class Bsdd(bonsai.core.tool.Bsdd):
identifier_url = "https://identifier.buildingsmart.org"
@@ -16,28 +38,29 @@ class Bsdd(bonsai.core.tool.Bsdd):
bsdd_properties: dict[str, dict] = {}
@classmethod
- def get_bsdd_props(cls):
- return bpy.context.scene.BIMBSDDProperties
+ def get_bsdd_props(cls) -> BIMBSDDProperties:
+ assert (scene := bpy.context.scene)
+ return scene.BIMBSDDProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def clear_class_psets(cls) -> None:
- bpy.context.scene.BIMBSDDProperties.classification_psets.clear()
+ cls.get_bsdd_props().classification_psets.clear()
@classmethod
def clear_classes(cls) -> None:
- bpy.context.scene.BIMBSDDProperties.classifications.clear()
+ cls.get_bsdd_props().classifications.clear()
@classmethod
def clear_properties(cls) -> None:
- bpy.context.scene.BIMBSDDProperties.properties.clear()
+ cls.get_bsdd_props().properties.clear()
@classmethod
def clear_dictionaries(cls) -> None:
- bpy.context.scene.BIMBSDDProperties.dictionaries.clear()
+ cls.get_bsdd_props().dictionaries.clear()
@classmethod
def create_class_psets(cls, pset_dict: dict[str, dict[str, Any]]) -> None:
- props = bpy.context.scene.BIMBSDDProperties
+ props = cls.get_bsdd_props()
data_type_map = {
"String": "string",
"Real": "float",
@@ -60,7 +83,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def create_dictionaries(cls, dictionaries: list[bsdd.DictionaryContractV1]) -> None:
- props = bpy.context.scene.BIMBSDDProperties
+ props = cls.get_bsdd_props()
for dictionary in sorted(dictionaries, key=lambda d: d["name"]):
new = props.dictionaries.add()
new.name = dictionary["name"]
@@ -72,7 +95,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def get_active_class_data(cls) -> Union[bsdd.ClassContractV1, dict]:
- prop = bpy.context.scene.BIMBSDDProperties
+ prop = cls.get_bsdd_props()
bsdd_classification = prop.classifications[prop.active_classification_index]
if not bsdd_classification:
return {}
@@ -80,18 +103,18 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def get_active_dictionary_uri(cls) -> str:
- return bpy.context.scene.BIMBSDDProperties.active_uri
+ return cls.get_bsdd_props().active_uri
@classmethod
def get_dictionary(cls, uri: str) -> bsdd.DictionaryContractV1:
- props = bpy.context.scene.BIMBSDDProperties
+ props = cls.get_bsdd_props()
response = cls.client.get_dictionary(dictionary_uri=uri, include_test_dictionaries=props.load_test_dictionaries)
if dicts := response.get("dictionaries"):
return dicts[0]
@classmethod
def get_dictionaries(cls) -> list[bsdd.DictionaryContractV1]:
- props = bpy.context.scene.BIMBSDDProperties
+ props = cls.get_bsdd_props()
response = cls.client.get_dictionary(include_test_dictionaries=props.load_test_dictionaries)
dicts = response.get("dictionaries") or []
statuses = ["Active"]
@@ -165,7 +188,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
limit: int = 100,
should_paginate: bool = True,
):
- cprops = bpy.context.scene.BIMClassificationProperties
+ cprops = tool.Classification.get_classification_props()
bprops = cls.get_bsdd_props()
dictionary_uris = (
[d.uri for d in bprops.dictionaries if d.is_active]
@@ -202,13 +225,13 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def set_active_bsdd(cls, name: str, uri: str) -> None:
- props = bpy.context.scene.BIMBSDDProperties
+ props = cls.get_bsdd_props()
props.active_dictionary = name
props.active_uri = uri
@classmethod
def should_filter_ifc_class(cls) -> bool:
- return bpy.context.scene.BIMBSDDProperties.should_filter_ifc_class
+ return cls.get_bsdd_props().should_filter_ifc_class
@classmethod
def get_bsdd_class(cls, uri: str) -> dict:
diff --git a/src/bonsai/bonsai/tool/classification.py b/src/bonsai/bonsai/tool/classification.py
index 113c69b480..3535e0a12f 100644
--- a/src/bonsai/bonsai/tool/classification.py
+++ b/src/bonsai/bonsai/tool/classification.py
@@ -22,10 +22,23 @@ import ifcopenshell.api
import ifcopenshell.util.classification
import bonsai.core.tool
import bonsai.tool as tool
-from typing import Union, assert_never
+from typing import Union, assert_never, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.classification.prop import BIMClassificationReferenceProperties, BIMClassificationProperties
class Classification(bonsai.core.tool.Classification):
+ @classmethod
+ def get_classification_props(cls) -> BIMClassificationProperties:
+ assert (scene := bpy.context.scene)
+ return scene.BIMClassificationProperties # pyright: ignore[reportAttributeAccessIssue]
+
+ @classmethod
+ def get_classification_reference_props(cls) -> BIMClassificationReferenceProperties:
+ assert (scene := bpy.context.scene)
+ return scene.BIMClassificationReferenceProperties # pyright: ignore[reportAttributeAccessIssue]
+
@classmethod
def get_location(cls, classification: ifcopenshell.entity_instance) -> Union[str, None]:
schema = classification.file.schema
diff --git a/src/bonsai/test/tool/test_classification.py b/src/bonsai/test/tool/test_classification.py
index c2f299a059..7e9bd3d5de 100644
--- a/src/bonsai/test/tool/test_classification.py
+++ b/src/bonsai/test/tool/test_classification.py
@@ -46,8 +46,8 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0"
bpy.ops.bim.set_active_bsdd_domain(name="CCI Construction", uri=uri)
- props = context.scene.BIMBSDDProperties
- bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
+ props = tool.Bsdd.get_bsdd_props()
+ tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = True
props.keyword = "Room"
bpy.ops.bim.search_bsdd_classifications()
@@ -70,8 +70,8 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0"
bpy.ops.bim.set_active_bsdd_domain(name="CCI Construction", uri=uri)
- props = context.scene.BIMBSDDProperties
- bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
+ props = tool.Bsdd.get_bsdd_props()
+ tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = True
props.keyword = "Room"
bpy.ops.bim.search_bsdd_classifications()
@@ -103,12 +103,12 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
element = tool.Ifc.get_entity(obj)
assert element
- props = context.scene.BIMBSDDProperties
+ props = tool.Bsdd.get_bsdd_props()
props.load_preview_domains = True
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/ifcairport/ifcairport/0.9"
bpy.ops.bim.set_active_bsdd_domain(name="IFC Airport", uri=uri)
- bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
+ tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = False # Important due to class mismatch.
props.keyword = "check-in conveyor"
bpy.ops.bim.search_bsdd_classifications()
diff --git a/src/bonsai/test/tool/test_loader.py b/src/bonsai/test/tool/test_loader.py
index 9845f50342..e0c5766421 100644
--- a/src/bonsai/test/tool/test_loader.py
+++ b/src/bonsai/test/tool/test_loader.py
@@ -540,7 +540,7 @@ class TestSetupActiveBsddClassification(NewFile):
filepath = "test/files/temp/test.ifc"
ifc_file.write(filepath)
bpy.ops.bim.load_project(filepath=filepath)
- props = bpy.context.scene.BIMBSDDProperties
+ props = tool.Bsdd.get_bsdd_props()
assert props.active_domain == name
assert props.active_uri == base_uri
diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py
index 330bf0b226..2eb29c431a 100644
--- a/src/ifcopenshell-python/ifcopenshell/validate.py
+++ b/src/ifcopenshell-python/ifcopenshell/validate.py
@@ -351,19 +351,18 @@ def log_internal_cpp_errors(f: ifcopenshell.file, filename: str, logger: Logger)
logger.error(m)
-entity_attribute_map: dict[tuple[str, str], tuple[entity_type, tuple[attribute]]] = {}
+entity_attribute_map: dict[tuple[str, str], tuple[entity_type, tuple[attribute, ...]]] = {}
-def get_entity_attributes(schema: schema_definition, entity: str) -> tuple[entity_type, tuple[attribute]]:
+def get_entity_attributes(schema: schema_definition, entity: str) -> tuple[entity_type, tuple[attribute, ...]]:
cache_key = schema.name(), entity
from_cache = entity_attribute_map.get(cache_key)
if from_cache:
return from_cache
- entity_attrs = (
- ent := schema.declaration_by_name(entity),
- ent.all_attributes(),
- )
+ ent = schema.declaration_by_name(entity).as_entity()
+ assert ent
+ entity_attrs = (ent, ent.all_attributes())
entity_attribute_map[cache_key] = entity_attrs
return entity_attrs
diff --git a/src/ifcpatch/ifcpatch/recipes/MergeStyles.py b/src/ifcpatch/ifcpatch/recipes/MergeStyles.py
index cb490e77c7..cb2780a360 100644
--- a/src/ifcpatch/ifcpatch/recipes/MergeStyles.py
+++ b/src/ifcpatch/ifcpatch/recipes/MergeStyles.py
@@ -17,12 +17,14 @@
# along with IfcPatch. If not, see .
import ifcopenshell
-import ifcopenshell.guid
import ifcopenshell.util.element
+import ifcpatch
+import logging
+from typing import Union
-class Patcher:
- def __init__(self, file, logger):
+class Patcher(ifcpatch.BasePatcher):
+ def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None):
"""Merge identical styles together
Some software may create an obscene number of styles instead of reusing
@@ -35,8 +37,7 @@ class Patcher:
ifcpatch.execute({"file": model, "recipe": "MergeStyles", "arguments": []})
"""
- self.file = file
- self.logger = logger
+ super().__init__(file, logger)
def patch(self):
for ifc_class in ("IfcColourRgb", "IfcSurfaceStyleShading", "IfcPresentationStyle"):