diff --git a/src/bonsai/bonsai/bim/module/drawing/__init__.py b/src/bonsai/bonsai/bim/module/drawing/__init__.py
index 83dd36f5ce..a98d5038e8 100644
--- a/src/bonsai/bonsai/bim/module/drawing/__init__.py
+++ b/src/bonsai/bonsai/bim/module/drawing/__init__.py
@@ -100,7 +100,7 @@ classes = (
prop.Sheet,
prop.DocProperties,
prop.BIMCameraProperties,
- prop.Literal,
+ prop.LiteralProps,
prop.BIMTextProperties,
prop.BIMAssignedProductProperties,
prop.BIMAnnotationProperties,
diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py
index 27e4f5d332..534e8c4ab1 100644
--- a/src/bonsai/bonsai/bim/module/drawing/operator.py
+++ b/src/bonsai/bonsai/bim/module/drawing/operator.py
@@ -1686,7 +1686,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
return False
if active_item.reference_type == "TITLEBLOCK":
- cls.poll_message_set("No effect deleting this.")
+ cls.poll_message_set("No effect deleting titleblock reference.")
return False
return True
diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py
index 594d34757c..5fc5769c30 100644
--- a/src/bonsai/bonsai/bim/module/drawing/prop.py
+++ b/src/bonsai/bonsai/bim/module/drawing/prop.py
@@ -44,6 +44,7 @@ from bpy.props import (
CollectionProperty,
BoolVectorProperty,
)
+from typing import TYPE_CHECKING, Literal
diagram_scales_enum = []
@@ -294,12 +295,25 @@ class Drawing(PropertyGroup):
is_drawing: BoolProperty(name="Is Drawing", default=False)
is_expanded: BoolProperty(name="Is Expanded", default=True)
+ if TYPE_CHECKING:
+ ifc_definition_id: int
+ name: str
+ target_view: str
+ is_selected: bool
+ is_drawing: bool
+ is_expanded: bool
+
class Document(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
name: StringProperty(name="Name", update=update_document_name)
identification: StringProperty(name="Identification")
+ if TYPE_CHECKING:
+ ifc_definition_id: int
+ name: str
+ identification: str
+
class Sheet(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
@@ -310,6 +324,15 @@ class Sheet(PropertyGroup):
reference_type: StringProperty(name="Reference Type")
is_expanded: BoolProperty(name="Is Expanded", default=False)
+ if TYPE_CHECKING:
+ ifc_definition_id: int
+ identification: str
+ name: str
+ is_sheet: bool
+ is_selected: bool
+ reference_type: str
+ is_expanded: bool
+
class DrawingStyle(PropertyGroup):
name: StringProperty(name="Name", get=get_drawing_style_name, set=set_drawing_style_name)
@@ -408,6 +431,47 @@ class DocProperties(PropertyGroup):
description="Upon import, these classes will display as wireframe.\nEx: IfcVirtualelement, IfcSpace",
)
+ if TYPE_CHECKING:
+ should_use_underlay_cache: bool
+ should_use_linework_cache: bool
+ should_use_annotation_cache: bool
+ is_editing_drawings: bool
+ 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
+ drawings: bpy.types.bpy_prop_collection_idprop[Drawing]
+ active_drawing_id: int
+ active_drawing_index: int
+ current_drawing_index: int
+ schedules: bpy.types.bpy_prop_collection_idprop[Document]
+ active_schedule_index: int
+ references: bpy.types.bpy_prop_collection_idprop[Document]
+ active_reference_index: int
+ titleblock: str
+ is_editing_sheets: bool
+ sheets: bpy.types.bpy_prop_collection_idprop[Sheet]
+ active_sheet_index: int
+ ifc_files: bpy.types.bpy_prop_collection_idprop[StrProperty]
+ drawing_styles: bpy.types.bpy_prop_collection_idprop[DrawingStyle]
+ should_draw_decorations: bool
+ sheets_dir: str
+ layouts_dir: str
+ titleblocks_dir: str
+ drawings_dir: str
+ stylesheet_path: str
+ schedules_stylesheet_path: str
+ markers_path: str
+ symbols_path: str
+ patterns_path: str
+ shadingstyles_path: str
+ shadingstyle_default: str
+ drawing_font: str
+ magic_font_scale: float
+ imperial_precision: str
+ tolerance: float
+ classes_to_wireframe: str
+
class BIMCameraProperties(PropertyGroup):
linework_mode: EnumProperty(
@@ -490,7 +554,7 @@ BOX_ALIGNMENT_POSITIONS = [
]
-class Literal(PropertyGroup):
+class LiteralProps(PropertyGroup):
def set_box_alignment(self, new_value):
markers = new_value.count(True)
if not markers:
@@ -535,7 +599,7 @@ class Literal(PropertyGroup):
class BIMTextProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
- literals: CollectionProperty(name="Literals", type=Literal)
+ literals: CollectionProperty(name="Literals", type=LiteralProps)
font_size: EnumProperty(
items=[
("1.8", "1.8 - Small", ""),
diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py
index c1380fced9..ff12939e0c 100644
--- a/src/bonsai/bonsai/bim/module/geometry/operator.py
+++ b/src/bonsai/bonsai/bim/module/geometry/operator.py
@@ -1075,7 +1075,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
@staticmethod
def duplicate_item(obj: bpy.types.Object) -> None:
- props = bpy.context.scene.BIMGeometryProperties
+ props = tool.Geometry.get_geometry_props()
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
new_item = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), item)
new_obj = obj.copy()
@@ -2750,17 +2750,17 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
assert obj
- props = context.scene.BIMGeometryProperties
+ props = tool.Geometry.get_geometry_props()
if previous_obj := props.representation_obj:
previous_obj.hide_set(False)
props.representation_obj = obj
obj.hide_set(True)
tool.Geometry.lock_object(obj)
- context.scene.BIMGeometryProperties.is_changing_mode = True
- if context.scene.BIMGeometryProperties.mode != "ITEM":
- context.scene.BIMGeometryProperties.mode = "ITEM"
- context.scene.BIMGeometryProperties.is_changing_mode = False
+ props.is_changing_mode = True
+ if props.mode != "ITEM":
+ props.mode = "ITEM"
+ props.is_changing_mode = False
tool.Loader.load_settings()
diff --git a/src/bonsai/bonsai/bim/module/geometry/prop.py b/src/bonsai/bonsai/bim/module/geometry/prop.py
index 12d84ebb20..2ab4ec5efe 100644
--- a/src/bonsai/bonsai/bim/module/geometry/prop.py
+++ b/src/bonsai/bonsai/bim/module/geometry/prop.py
@@ -32,7 +32,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
-from typing import Optional, TYPE_CHECKING
+from typing import Optional, TYPE_CHECKING, Union
def get_contexts(self, context):
@@ -302,3 +302,13 @@ class BIMGeometryProperties(PropertyGroup):
type=bpy.types.Object,
poll=is_object_valid_for_representation_copy,
)
+
+ if TYPE_CHECKING:
+ should_use_presentation_style_assignment: bool
+ should_force_faceted_brep: bool
+ should_force_triangulation: bool
+ is_changing_mode: bool
+ mode: str
+ representation_obj: Union[bpy.types.Object, None]
+ item_objs: bpy.types.bpy_prop_collection_idprop[RepresentationItemObject]
+ representation_from_object: Union[bpy.types.Object, None]
diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py
index 20eb4a1f5d..5c22da6d8c 100644
--- a/src/bonsai/bonsai/bim/module/project/operator.py
+++ b/src/bonsai/bonsai/bim/module/project/operator.py
@@ -1243,7 +1243,7 @@ class LoadLink(bpy.types.Operator):
os.remove(blend_filepath)
if not blend_filepath.exists():
- pprops = bpy.context.scene.BIMProjectProperties
+ pprops = tool.Project.get_project_props()
gprops = bpy.context.scene.BIMGeoreferenceProperties
code = f"""
diff --git a/src/bonsai/bonsai/bim/module/system/operator.py b/src/bonsai/bonsai/bim/module/system/operator.py
index ab93f43c4f..4ef3c255f7 100644
--- a/src/bonsai/bonsai/bim/module/system/operator.py
+++ b/src/bonsai/bonsai/bim/module/system/operator.py
@@ -337,7 +337,7 @@ class LoadZones(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = bpy.context.scene.BIMZoneProperties
+ props = tool.System.get_zone_props()
props.zones.clear()
for zone in tool.Ifc.get().by_type("IfcZone"):
new = props.zones.add()
@@ -354,7 +354,7 @@ class UnloadZones(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = bpy.context.scene.BIMZoneProperties
+ props = tool.System.get_zone_props()
props.is_loaded = False
return {"FINISHED"}
@@ -386,7 +386,7 @@ class EnableEditingZone(bpy.types.Operator):
zone: bpy.props.IntProperty()
def execute(self, context):
- props = bpy.context.scene.BIMZoneProperties
+ props = tool.System.get_zone_props()
props.attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.zone), props.attributes)
props.is_editing = self.zone
@@ -399,7 +399,7 @@ class DisableEditingZone(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = bpy.context.scene.BIMZoneProperties
+ props = tool.System.get_zone_props()
props.is_editing = 0
return {"FINISHED"}
@@ -410,7 +410,7 @@ class EditZone(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- props = bpy.context.scene.BIMZoneProperties
+ props = tool.System.get_zone_props()
zone = tool.Ifc.get().by_id(props.is_editing)
attributes = bonsai.bim.helper.export_attributes(props.attributes)
ifcopenshell.api.run("system.edit_system", tool.Ifc.get(), system=zone, attributes=attributes)
diff --git a/src/bonsai/bonsai/bim/module/system/prop.py b/src/bonsai/bonsai/bim/module/system/prop.py
index 91586a2224..3e14bf74e5 100644
--- a/src/bonsai/bonsai/bim/module/system/prop.py
+++ b/src/bonsai/bonsai/bim/module/system/prop.py
@@ -31,6 +31,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
+from typing import TYPE_CHECKING
def get_system_class(self, context):
@@ -71,6 +72,17 @@ class BIMSystemProperties(PropertyGroup):
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
)
+ if TYPE_CHECKING:
+ system_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ is_editing: bool
+ is_adding: bool
+ systems: bpy.types.bpy_prop_collection_idprop[System]
+ active_system_index: int
+ active_system_id: int
+ edited_system_id: int
+ system_class: str
+ should_draw_decorations: bool
+
class BIMZoneProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
@@ -78,3 +90,10 @@ class BIMZoneProperties(PropertyGroup):
is_editing: IntProperty(name="Is Editing", default=0)
zones: CollectionProperty(name="Zones", type=Zone)
active_zone_index: IntProperty(name="Active Zone Index")
+
+ if TYPE_CHECKING:
+ attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ is_loaded: bool
+ is_editing: int
+ zones: bpy.types.bpy_prop_collection_idprop[Zone]
+ active_zone_index: int
diff --git a/src/bonsai/bonsai/bim/module/system/ui.py b/src/bonsai/bonsai/bim/module/system/ui.py
index 2bd49feb7d..b09181c327 100644
--- a/src/bonsai/bonsai/bim/module/system/ui.py
+++ b/src/bonsai/bonsai/bim/module/system/ui.py
@@ -350,7 +350,7 @@ class BIM_PT_zones(Panel):
def draw(self, context):
if not ZonesData.is_loaded:
ZonesData.load()
- self.props = context.scene.BIMZoneProperties
+ self.props = tool.System.get_zone_props()
row = self.layout.row(align=True)
row.label(text="{} Zones Found".format(ZonesData.data["total_zones"]), icon="SEQ_STRIP_META")
@@ -394,7 +394,7 @@ class BIM_PT_active_object_zones(Panel):
def draw(self, context):
if not ActiveObjectZonesData.is_loaded:
ActiveObjectZonesData.load()
- self.props = context.scene.BIMZoneProperties
+ self.props = tool.System.get_zone_props()
for zone in ActiveObjectZonesData.data["zones"]:
row = self.layout.row()
diff --git a/src/bonsai/bonsai/core/system.py b/src/bonsai/bonsai/core/system.py
index 7e6d0b7164..2acd1d21d4 100644
--- a/src/bonsai/bonsai/core/system.py
+++ b/src/bonsai/bonsai/core/system.py
@@ -16,58 +16,68 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+from __future__ import annotations
+from typing import TYPE_CHECKING, Optional
-def load_systems(system):
+if TYPE_CHECKING:
+ import bpy
+ import ifcopenshell
+ import bonsai.tool as tool
+
+
+def load_systems(system: tool.System) -> None:
system.import_systems()
system.enable_system_editing_ui()
system.disable_editing_system()
-def disable_system_editing_ui(system):
+def disable_system_editing_ui(system: tool.System) -> None:
system.disable_editing_system()
system.disable_system_editing_ui()
-def add_system(ifc, system, ifc_class=None):
+def add_system(ifc: tool.Ifc, system: tool.System, ifc_class: str) -> None:
ifc.run("system.add_system", ifc_class=ifc_class)
system.import_systems()
-def edit_system(ifc, system_tool, system=None):
+def edit_system(ifc: tool.Ifc, system_tool: tool.System, system: ifcopenshell.entity_instance) -> None:
attributes = system_tool.export_system_attributes()
ifc.run("system.edit_system", system=system, attributes=attributes)
system_tool.disable_editing_system()
system_tool.import_systems()
-def remove_system(ifc, system_tool, system=None):
+def remove_system(ifc: tool.Ifc, system_tool: tool.System, system: ifcopenshell.entity_instance) -> None:
ifc.run("system.remove_system", system=system)
system_tool.import_systems()
-def enable_editing_system(system_tool, system=None):
+def enable_editing_system(system_tool: tool.System, system: ifcopenshell.entity_instance) -> None:
system_tool.import_system_attributes(system)
system_tool.set_active_edited_system(system)
-def disable_editing_system(system):
+def disable_editing_system(system: tool.System) -> None:
system.disable_editing_system()
-def assign_system(ifc, system=None, product=None):
+def assign_system(ifc: tool.Ifc, system: ifcopenshell.entity_instance, product: ifcopenshell.entity_instance) -> None:
ifc.run("system.assign_system", products=[product], system=system)
-def unassign_system(ifc, system=None, product=None):
+def unassign_system(ifc: tool.Ifc, system: ifcopenshell.entity_instance, product: ifcopenshell.entity_instance) -> None:
ifc.run("system.unassign_system", products=[product], system=system)
-def select_system_products(system_tool, system=None):
+def select_system_products(system_tool: tool.System, system: ifcopenshell.entity_instance) -> None:
system_tool.select_system_products(system)
system_tool.set_active_system(system)
-def show_ports(ifc, system, spatial, element=None):
+def show_ports(
+ ifc: tool.Ifc, system: tool.System, spatial: tool.Spatial, element: ifcopenshell.entity_instance
+) -> None:
obj = ifc.get_object(element)
if obj and ifc.is_moved(obj):
system.run_geometry_edit_object_placement(obj=obj)
@@ -77,7 +87,7 @@ def show_ports(ifc, system, spatial, element=None):
spatial.select_products(ports)
-def hide_ports(ifc, system, element=None):
+def hide_ports(ifc: tool.Ifc, system: tool.System, element: ifcopenshell.entity_instance) -> None:
obj = ifc.get_object(element)
if obj and ifc.is_moved(obj):
system.run_geometry_edit_object_placement(obj=obj)
@@ -91,27 +101,27 @@ def hide_ports(ifc, system, element=None):
system.delete_element_objects(ports)
-def add_port(ifc, system, element=None):
+def add_port(ifc: tool.Ifc, system: tool.System, element: ifcopenshell.entity_instance) -> None:
system.load_ports(element, system.get_ports(element))
obj = system.create_empty_at_cursor_with_element_orientation(element)
port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort", should_add_representation=False)
ifc.run("system.assign_port", element=element, port=port)
-def remove_port(ifc, system, port=None):
+def remove_port(ifc: tool.Ifc, system: tool.System, port: ifcopenshell.entity_instance) -> None:
system.delete_element_objects([port])
ifc.run("root.remove_product", product=port)
-def connect_port(ifc, port1=None, port2=None):
+def connect_port(ifc: tool.Ifc, port1: ifcopenshell.entity_instance, port2: ifcopenshell.entity_instance) -> None:
ifc.run("system.connect_port", port1=port1, port2=port2)
-def disconnect_port(ifc, port=None):
+def disconnect_port(ifc: tool.Ifc, port: ifcopenshell.entity_instance) -> None:
ifc.run("system.disconnect_port", port=port)
-def set_flow_direction(ifc, system, port=None, direction=None):
+def set_flow_direction(ifc: tool.Ifc, system: tool.System, port: ifcopenshell.entity_instance, direction: str) -> None:
port2 = system.get_connected_port(port)
if not port2:
return
diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py
index 5345d073a6..b75717044a 100644
--- a/src/bonsai/bonsai/tool/drawing.py
+++ b/src/bonsai/bonsai/tool/drawing.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 os
import re
import collections
@@ -34,6 +35,7 @@ import subprocess
import numpy as np
import bonsai.core.tool
import bonsai.core.geometry
+import bonsai.core.type
import bonsai.tool as tool
import ifcopenshell.api
import ifcopenshell.api.document
@@ -49,9 +51,13 @@ 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
+from typing import Optional, Union, Iterable, Any, Literal, Sequence, TYPE_CHECKING
from pathlib import Path
+if TYPE_CHECKING:
+ from bonsai.bim.module.drawing.prop import DocProperties, Sheet
+ from bonsai.bim.module.drawing.prop import Drawing as DrawingProperties
+
class Drawing(bonsai.core.tool.Drawing):
ANNOTATION_DATA_TYPE = Literal["empty", "curve", "mesh"]
@@ -81,6 +87,10 @@ class Drawing(bonsai.core.tool.Drawing):
}
# fmt: on
+ @classmethod
+ def get_document_props(cls) -> DocProperties:
+ return bpy.context.scene.DocProperties
+
@classmethod
def canonicalise_class_name(cls, name: str) -> str:
return re.sub("[^0-9a-zA-Z]+", "", name)
@@ -820,7 +830,7 @@ class Drawing(bonsai.core.tool.Drawing):
@classmethod
def import_documents(cls, document_type: DOCUMENT_TYPE) -> None:
- dprops = bpy.context.scene.DocProperties
+ dprops = cls.get_document_props()
if document_type == "SCHEDULE":
documents_collection = dprops.schedules
elif document_type == "REFERENCE":
@@ -839,7 +849,7 @@ class Drawing(bonsai.core.tool.Drawing):
@classmethod
def import_sheets(cls) -> None:
- props = bpy.context.scene.DocProperties
+ props = cls.get_document_props()
expanded_sheets = {s.ifc_definition_id for s in props.sheets if s.is_expanded}
if not hasattr(cls, "sheet_selected_states"):
cls.sheet_selected_states = {}
@@ -879,13 +889,13 @@ class Drawing(bonsai.core.tool.Drawing):
new.reference_type = reference_description
@classmethod
- def get_active_sheet(cls, context: bpy.types.Context) -> bpy.types.PropertyGroup:
- props = context.scene.DocProperties
+ def get_active_sheet(cls, context: bpy.types.Context) -> Sheet:
+ props = cls.get_document_props()
return next(s for s in props.sheets[: props.active_sheet_index + 1][::-1] if s.is_sheet)
@classmethod
- def get_active_drawing_item(cls) -> Union[bpy.types.PropertyGroup, None]:
- props = bpy.context.scene.DocProperties
+ def get_active_drawing_item(cls) -> Union[DrawingProperties, None]:
+ props = cls.get_document_props()
drawing_index = props.active_drawing_index
if len(props.drawings) > drawing_index >= 0:
item = props.drawings[drawing_index]
@@ -893,10 +903,8 @@ class Drawing(bonsai.core.tool.Drawing):
return item
@classmethod
- def get_active_sheet_item(
- cls, *, is_sheet: bool = False, reference_type: str = ""
- ) -> Union[bpy.types.PropertyGroup, None]:
- props = bpy.context.scene.DocProperties
+ def get_active_sheet_item(cls, *, is_sheet: bool = False, reference_type: str = "") -> Union[Sheet, None]:
+ props = cls.get_document_props()
sheet_index = props.active_sheet_index
if len(props.sheets) > sheet_index >= 0:
item = props.sheets[sheet_index]
diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py
index 133189b184..cc951596e3 100644
--- a/src/bonsai/bonsai/tool/geometry.py
+++ b/src/bonsai/bonsai/tool/geometry.py
@@ -57,10 +57,14 @@ from typing_extensions import TypeIs
if TYPE_CHECKING:
from bonsai.bim.prop import Attribute
- from bonsai.bim.module.geometry.prop import BIMObjectGeometryProperties
+ from bonsai.bim.module.geometry.prop import BIMObjectGeometryProperties, BIMGeometryProperties
class Geometry(bonsai.core.tool.Geometry):
+ @classmethod
+ def get_geometry_props(cls) -> BIMGeometryProperties:
+ return bpy.context.scene.BIMGeometryProperties
+
@classmethod
def get_object_geometry_props(cls, object: bpy.types.Object) -> BIMObjectGeometryProperties:
return object.BIMGeometryProperties
diff --git a/src/bonsai/bonsai/tool/system.py b/src/bonsai/bonsai/tool/system.py
index d9cf2e517a..51334be3a2 100644
--- a/src/bonsai/bonsai/tool/system.py
+++ b/src/bonsai/bonsai/tool/system.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.util.element
import ifcopenshell.util.system
@@ -31,11 +32,30 @@ from mathutils import Matrix, Vector
from bonsai.bim.module.system.data import ObjectSystemData, SystemDecorationData
from bonsai.bim.module.drawing.decoration import profile_consequential
from enum import Enum
+from typing import TYPE_CHECKING, Optional, Any, Union
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.system.prop import BIMSystemProperties, BIMZoneProperties
class System(bonsai.core.tool.System):
@classmethod
- def add_ports(cls, obj, add_start_port=True, add_end_port=True, end_port_pos=None, offset_end_port=None):
+ def get_system_props(cls) -> BIMSystemProperties:
+ return bpy.context.scene.BIMSystemProperties
+
+ @classmethod
+ def get_zone_props(cls) -> BIMZoneProperties:
+ return bpy.context.scene.BIMZoneProperties
+
+ @classmethod
+ def add_ports(
+ cls,
+ obj: bpy.types.Object,
+ add_start_port: bool = True,
+ add_end_port: bool = True,
+ end_port_pos: Optional[Vector] = None,
+ offset_end_port: Optional[Vector] = None,
+ ) -> list[ifcopenshell.entity_instance]:
def add_port(mep_element, matrix):
port = tool.Ifc.run("system.add_port", element=mep_element)
port.FlowDirection = "NOTDEFINED"
@@ -66,7 +86,7 @@ class System(bonsai.core.tool.System):
return ports
@classmethod
- def create_empty_at_cursor_with_element_orientation(cls, element):
+ def create_empty_at_cursor_with_element_orientation(cls, element: ifcopenshell.entity_instance) -> bpy.types.Object:
element_obj = tool.Ifc.get_object(element)
obj = bpy.data.objects.new("Port", None)
obj.matrix_world = element_obj.matrix_world
@@ -75,38 +95,38 @@ class System(bonsai.core.tool.System):
return obj
@classmethod
- def delete_element_objects(cls, elements):
+ def delete_element_objects(cls, elements: list[ifcopenshell.entity_instance]) -> None:
for element in elements:
obj = tool.Ifc.get_object(element)
if obj:
bpy.data.objects.remove(obj)
@classmethod
- def disable_editing_system(cls):
- bpy.context.scene.BIMSystemProperties.edited_system_id = 0
+ def disable_editing_system(cls) -> None:
+ cls.get_system_props().edited_system_id = 0
@classmethod
- def disable_system_editing_ui(cls):
- bpy.context.scene.BIMSystemProperties.is_editing = False
+ def disable_system_editing_ui(cls) -> None:
+ cls.get_system_props().is_editing = False
@classmethod
- def enable_system_editing_ui(cls):
- bpy.context.scene.BIMSystemProperties.is_editing = True
+ def enable_system_editing_ui(cls) -> None:
+ cls.get_system_props().is_editing = True
@classmethod
- def export_system_attributes(cls):
- return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMSystemProperties.system_attributes)
+ def export_system_attributes(cls) -> dict[str, Any]:
+ return bonsai.bim.helper.export_attributes(cls.get_system_props().system_attributes)
@classmethod
- def get_connected_port(cls, port):
+ def get_connected_port(cls, port: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.system.get_connected_port(port)
@classmethod
- def get_ports(cls, element):
+ def get_ports(cls, element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.system.get_ports(element)
@classmethod
- def get_port_relating_element(cls, port):
+ def get_port_relating_element(cls, port: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
if tool.Ifc.get_schema() == "IFC2X3":
element = port.ContainedIn[0].RelatedElement
else:
@@ -114,7 +134,7 @@ class System(bonsai.core.tool.System):
return element
@classmethod
- def get_port_predefined_type(cls, mep_element):
+ def get_port_predefined_type(cls, mep_element: ifcopenshell.entity_instance) -> str:
split_camel_case = lambda x: re.findall("[A-Z][^A-Z]*", x)
mep_class = mep_element.is_a()
if mep_class.endswith("Type"):
@@ -125,20 +145,20 @@ class System(bonsai.core.tool.System):
return class_name
@classmethod
- def import_system_attributes(cls, system):
- props = bpy.context.scene.BIMSystemProperties
+ def import_system_attributes(cls, system: ifcopenshell.entity_instance) -> None:
+ props = cls.get_system_props()
props.system_attributes.clear()
bonsai.bim.helper.import_attributes2(system, props.system_attributes)
@classmethod
- def get_systems(cls):
+ def get_systems(cls) -> list[ifcopenshell.entity_instance]:
if tool.Ifc.get_schema() == "IFC2X3":
return tool.Ifc.get().by_type("IfcSystem") + tool.Ifc.get().by_type("IfcZone")
return tool.Ifc.get().by_type("IfcSystem")
@classmethod
- def import_systems(cls):
- props = bpy.context.scene.BIMSystemProperties
+ def import_systems(cls) -> None:
+ props = cls.get_system_props()
props.systems.clear()
for system in cls.get_systems():
if system.is_a() in ["IfcStructuralAnalysisModel"]:
@@ -179,7 +199,7 @@ class System(bonsai.core.tool.System):
port_obj.matrix_parent_inverse = obj.matrix_world.inverted()
@classmethod
- def run_geometry_edit_object_placement(cls, obj=None):
+ def run_geometry_edit_object_placement(cls, obj: bpy.types.Object) -> None:
return bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
@classmethod
@@ -205,24 +225,24 @@ class System(bonsai.core.tool.System):
)
@classmethod
- def select_system_products(cls, system):
+ def select_system_products(cls, system: ifcopenshell.entity_instance) -> None:
tool.Spatial.select_products(ifcopenshell.util.system.get_system_elements(system))
@classmethod
- def set_active_edited_system(cls, system):
- bpy.context.scene.BIMSystemProperties.edited_system_id = system.id()
+ def set_active_edited_system(cls, system: ifcopenshell.entity_instance) -> None:
+ cls.get_system_props().edited_system_id = system.id()
@classmethod
- def set_active_system(cls, system):
- bpy.context.scene.BIMSystemProperties.active_system_id = system.id()
+ def set_active_system(cls, system: ifcopenshell.entity_instance) -> None:
+ cls.get_system_props().active_system_id = system.id()
@classmethod
- def get_active_system(cls):
- system_props = bpy.context.scene.BIMSystemProperties
+ def get_active_system(cls) -> Union[ifcopenshell.entity_instance, None]:
+ system_props = cls.get_system_props()
return tool.Ifc.get_entity_by_id(system_props.active_system_id)
@classmethod
- def get_decoration_data(cls):
+ def get_decoration_data(cls) -> dict[str, Any]:
all_vertices = []
preview_edges = []
special_vertices = []
@@ -367,7 +387,11 @@ class System(bonsai.core.tool.System):
return decoration_data
@classmethod
- def get_connected_elements(cls, element, traversed_elements=None):
+ def get_connected_elements(
+ cls,
+ element: ifcopenshell.entity_instance,
+ traversed_elements: Optional[set[ifcopenshell.entity_instance]] = None,
+ ) -> set[ifcopenshell.entity_instance]:
"""Recursively retrieves all connected elements to the given `element`.
`traversed_elements` is a set to store connected elements fetched recursively,
@@ -387,17 +411,19 @@ class System(bonsai.core.tool.System):
return traversed_elements
@classmethod
- def is_mep_element(cls, element):
+ def is_mep_element(cls, element: ifcopenshell.entity_instance) -> bool:
return element.is_a("IfcFlowSegment") or element.is_a("IfcFlowFitting")
@classmethod
- def get_flow_element_controls(cls, element):
+ def get_flow_element_controls(cls, element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
if not element.HasControlElements:
return []
return [control for control in element.HasControlElements[0].RelatedControlElements]
@classmethod
- def get_flow_control_flow_element(cls, element):
+ def get_flow_control_flow_element(
+ cls, element: ifcopenshell.entity_instance
+ ) -> Union[ifcopenshell.entity_instance, None]:
if not element.AssignedToFlowElement:
return
return element.AssignedToFlowElement[0].RelatingFlowElement