diff --git a/src/bonsai/bonsai/bim/handler.py b/src/bonsai/bonsai/bim/handler.py
index 88e785194f..fc45d6c01c 100644
--- a/src/bonsai/bonsai/bim/handler.py
+++ b/src/bonsai/bonsai/bim/handler.py
@@ -347,8 +347,8 @@ def load_post(scene):
# Bonsai overlays
georeference_props = tool.Georeference.get_georeference_props()
- aggregate_props = bpy.context.scene.BIMAggregateProperties
- nest_props = bpy.context.scene.BIMNestProperties
+ aggregate_props = tool.Aggregate.get_aggregate_props()
+ nest_props = tool.Nest.get_nest_props()
model_props = tool.Model.get_model_props()
if georeference_props.should_visualise:
GeoreferenceDecorator.install(bpy.context)
diff --git a/src/bonsai/bonsai/bim/module/aggregate/decorator.py b/src/bonsai/bonsai/bim/module/aggregate/decorator.py
index 239253ec2a..347daae714 100644
--- a/src/bonsai/bonsai/bim/module/aggregate/decorator.py
+++ b/src/bonsai/bonsai/bim/module/aggregate/decorator.py
@@ -20,6 +20,7 @@ import blf
import bpy
import gpu
import ifcopenshell
+import ifcopenshell.util.element
import bonsai.tool as tool
from bpy.types import SpaceView3D
from bpy_extras import view3d_utils
@@ -162,7 +163,8 @@ class AggregateDecorator:
batch.draw(shader)
def draw_aggregate(self, context):
- if context.scene.BIMAggregateProperties.in_aggregate_mode:
+ props = tool.Aggregate.get_aggregate_props()
+ if props.in_aggregate_mode:
return
self.addon_prefs = tool.Blender.get_addon_preferences()
decorator_color_special = self.addon_prefs.decorator_color_special
@@ -210,7 +212,7 @@ class AggregateDecorator:
self.draw_batch("LINES", line_y, color, [(0, 1)])
line_z = (location - Vector((0.0, 0.0, size)), location + Vector((0.0, 0.0, size)))
self.draw_batch("LINES", line_z, color, [(0, 1)])
- if context.scene.BIMAggregateProperties.in_aggregate_mode:
+ if props.in_aggregate_mode:
return
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(aggregate))
parts_objs = [tool.Ifc.get_object(p) for p in parts]
@@ -261,7 +263,7 @@ class AggregateModeDecorator:
return
region = context.region
rv3d = region.data
- props = context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
aggregate_obj = props.editing_aggregate
if not aggregate_obj:
@@ -292,7 +294,7 @@ class AggregateModeDecorator:
def draw_aggregate_empty(self, context):
if context.mode == "EDIT_MESH":
return
- props = context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
aggregate_obj = props.editing_aggregate
if not aggregate_obj:
return
diff --git a/src/bonsai/bonsai/bim/module/aggregate/operator.py b/src/bonsai/bonsai/bim/module/aggregate/operator.py
index c324037b89..f84dab2186 100644
--- a/src/bonsai/bonsai/bim/module/aggregate/operator.py
+++ b/src/bonsai/bonsai/bim/module/aggregate/operator.py
@@ -23,6 +23,7 @@ import ifcopenshell.util.element
import bonsai.tool as tool
import bonsai.core.aggregate as core
import bonsai.core.spatial
+from typing import TYPE_CHECKING
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
@@ -37,16 +38,23 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
relating_object: bpy.props.IntProperty()
related_object: bpy.props.IntProperty()
+ if TYPE_CHECKING:
+ relating_object: int
+ related_object: int
+
def _execute(self, context):
relating_obj = None
if self.relating_object:
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
+ assert relating_obj
elif self.related_object:
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get().by_id(self.related_object))
if aggregate:
relating_obj = tool.Ifc.get_object(aggregate)
- elif context.active_object:
+ assert relating_obj
+ else:
relating_obj = context.active_object
+
if not relating_obj:
self.report({"ERROR"}, "No relating object is provided.")
return
@@ -66,7 +74,7 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
relating_obj=relating_obj,
related_obj=obj,
)
- props = context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
if relating_obj == props.editing_aggregate and props.in_aggregate_mode:
new_editing_obj = props.editing_objects.add()
new_editing_obj.obj = obj
@@ -182,7 +190,7 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
)
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
- def create_aggregate(self, context, ifc_class, aggregate_name):
+ def create_aggregate(self, context: bpy.types.Context, ifc_class: str, aggregate_name: str) -> bpy.types.Object:
aggregate = bpy.data.objects.new(aggregate_name, None)
aggregate.location = context.scene.cursor.location
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class=ifc_class)
@@ -388,7 +396,7 @@ class BIM_OT_toggle_aggregate_mode_local_view(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
objs = [o.obj for o in props.editing_objects]
if props.in_aggregate_mode:
if context.space_data.local_view:
diff --git a/src/bonsai/bonsai/bim/module/aggregate/prop.py b/src/bonsai/bonsai/bim/module/aggregate/prop.py
index 1d524b3645..0fb63661b3 100644
--- a/src/bonsai/bonsai/bim/module/aggregate/prop.py
+++ b/src/bonsai/bonsai/bim/module/aggregate/prop.py
@@ -33,6 +33,7 @@ from bpy.props import (
CollectionProperty,
)
from bonsai.bim.module.aggregate.decorator import AggregateDecorator, AggregateModeDecorator
+from typing import TYPE_CHECKING, Union
def can_aggregate(relating_obj: bpy.types.Object, related_obj: bpy.types.Object) -> bool:
@@ -98,12 +99,22 @@ class BIMObjectAggregateProperties(PropertyGroup):
poll=poll_related_object,
)
+ if TYPE_CHECKING:
+ is_editing: bool
+ relating_object: Union[bpy.types.Object, None]
+ related_object: Union[bpy.types.Object, None]
+
class Objects(bpy.types.PropertyGroup):
obj: PointerProperty(type=bpy.types.Object)
previous_display_type: bpy.props.StringProperty(default="TEXTURED")
previous_hide_select: bpy.props.BoolProperty(default=False)
+ if TYPE_CHECKING:
+ obj: Union[bpy.types.Object, None]
+ previous_display_type: str
+ previous_hide_select: bool
+
class BIMAggregateProperties(PropertyGroup):
in_aggregate_mode: BoolProperty(name="In Edit Mode", update=update_aggregate_mode_decorator)
@@ -115,3 +126,10 @@ class BIMAggregateProperties(PropertyGroup):
default=False,
update=update_aggregate_decorator,
)
+
+ if TYPE_CHECKING:
+ in_aggregate_mode: bool
+ editing_aggregate: Union[bpy.types.Object, None]
+ editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
+ not_editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
+ aggregate_decorator: bool
diff --git a/src/bonsai/bonsai/bim/module/aggregate/ui.py b/src/bonsai/bonsai/bim/module/aggregate/ui.py
index 0755d3a54f..91a4e138f4 100644
--- a/src/bonsai/bonsai/bim/module/aggregate/ui.py
+++ b/src/bonsai/bonsai/bim/module/aggregate/ui.py
@@ -49,11 +49,13 @@ class BIM_PT_aggregate(Panel):
layout = self.layout
row = layout.row()
row.label(text="Aggregate Decorator")
- row.prop(context.scene.BIMAggregateProperties, "aggregate_decorator", icon="HIDE_OFF", text="")
+ props = tool.Aggregate.get_aggregate_props()
+ row.prop(props, "aggregate_decorator", icon="HIDE_OFF", text="")
if not AggregateData.is_loaded:
AggregateData.load()
- props = context.active_object.BIMObjectAggregateProperties
+ assert (obj := context.active_object)
+ props = tool.Aggregate.get_object_aggregate_props(obj)
if props.is_editing:
row = layout.row()
@@ -131,9 +133,8 @@ class BIM_PT_linked_aggregate(Panel):
if not AggregateData.is_loaded:
AggregateData.load()
- obj = context.active_object
- element = tool.Ifc.get_entity(obj)
- props = obj.BIMObjectAggregateProperties
+ assert (obj := context.active_object)
+ assert (element := tool.Ifc.get_entity(obj))
row = layout.row(align=True)
if element.Decomposes:
diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py
index 3062aa332c..d779e52765 100644
--- a/src/bonsai/bonsai/bim/module/geometry/operator.py
+++ b/src/bonsai/bonsai/bim/module/geometry/operator.py
@@ -1929,7 +1929,7 @@ class OverrideEscape(bpy.types.Operator):
tool.Geometry.disable_item_mode()
elif tool.Model.get_model_props().openings:
bpy.ops.bim.hide_all_openings()
- elif context.scene.BIMAggregateProperties.in_aggregate_mode:
+ elif tool.Aggregate.get_aggregate_props().in_aggregate_mode:
bpy.ops.bim.disable_aggregate_mode()
elif active_object := context.active_object:
if tool.Blender.Modifier.try_canceling_editing_modifier_parameters_or_path(active_object):
@@ -1953,24 +1953,26 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
selected_objs = context.selected_objects # Purposely exclude active object
+ aprops = tool.Aggregate.get_aggregate_props()
+ nprops = tool.Nest.get_nest_props()
if self.has_aggregates(selected_objs):
- if not context.scene.BIMAggregateProperties.in_aggregate_mode:
+ if not aprops.in_aggregate_mode:
bonsai.core.aggregate.enable_aggregate_mode(tool.Aggregate, context.active_object)
return {"FINISHED"}
if self.has_nests(selected_objs):
- if not context.scene.BIMNestProperties.in_nest_mode:
+ if not nprops.in_nest_mode:
bonsai.core.nest.enable_nest_mode(tool.Nest, context.active_object)
return {"FINISHED"}
if len(selected_objs) == 1 and context.active_object == selected_objs[0]:
self.handle_single_object(context, context.active_object)
elif len(selected_objs) == 0:
- if context.scene.BIMAggregateProperties.in_aggregate_mode:
+ if aprops.in_aggregate_mode:
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
return {"FINISHED"}
- if context.scene.BIMNestProperties.in_nest_mode:
+ if nprops.in_nest_mode:
bonsai.core.nest.disable_nest_mode(tool.Nest)
return {"FINISHED"}
tool.Geometry.disable_item_mode()
@@ -1981,9 +1983,10 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
element = tool.Ifc.get_entity(obj)
props = tool.Geometry.get_geometry_props()
pprops = tool.Project.get_project_props()
+ aprops = tool.Aggregate.get_aggregate_props()
if obj == props.representation_obj:
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly")
- elif obj in [o.obj for o in context.scene.BIMAggregateProperties.not_editing_objects]:
+ elif obj in [o.obj for o in aprops.not_editing_objects]:
obj.select_set(False)
self.report(
{"ERROR"}, f"Element '{obj.name}' does not belong to this aggregate and cannot be edited directly"
@@ -2099,7 +2102,8 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
continue
aggregate = ifcopenshell.util.element.get_aggregate(element)
parts = ifcopenshell.util.element.get_parts(element)
- if (aggregate or parts) and not bpy.context.scene.BIMAggregateProperties.in_aggregate_mode:
+ props = tool.Aggregate.get_aggregate_props()
+ if (aggregate or parts) and not props.in_aggregate_mode:
return True
else:
return False
@@ -2111,7 +2115,8 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
continue
nest = ifcopenshell.util.element.get_nest(element)
components = ifcopenshell.util.element.get_components(element)
- if (nest or components) and not bpy.context.scene.BIMNestProperties.in_nest_mode:
+ props = tool.Nest.get_nest_props()
+ if (nest or components) and not props.in_nest_mode:
return True
else:
return False
@@ -3270,7 +3275,7 @@ class OverrideMove(bpy.types.Operator):
self.new_active_obj = obj
# Get aggregates
- props = context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
not_editing_objs = [o.obj for o in props.not_editing_objects]
aggregates_to_move = []
for obj in context.selected_objects:
@@ -3305,7 +3310,7 @@ class OverrideMove(bpy.types.Operator):
return {"FINISHED"}
# Get nests
- props = context.scene.BIMNestProperties
+ props = tool.Nest.get_nest_props()
not_editing_objs = [o.obj for o in props.not_editing_objects]
nests_to_move = []
for obj in context.selected_objects:
diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py
index b4814a4d86..42a767e726 100644
--- a/src/bonsai/bonsai/bim/module/model/workspace.py
+++ b/src/bonsai/bonsai/bim/module/model/workspace.py
@@ -717,12 +717,14 @@ class EditObjectUI:
AuthoringData.load(ifc_element_type)
if context.region.type == "TOOL_HEADER":
- if context.scene.BIMAggregateProperties.in_aggregate_mode:
+ aprops = tool.Aggregate.get_aggregate_props()
+ if aprops.in_aggregate_mode:
layout.label(text=f"Aggregate Mode", icon="EMPTY_AXIS")
row = cls.layout.row(align=True)
op = row.operator("bim.disable_aggregate_mode", text="", icon="X")
op = row.operator("bim.toggle_aggregate_mode_local_view", text="", icon="ZOOM_SELECTED")
- if context.scene.BIMNestProperties.in_nest_mode:
+ nprops = tool.Nest.get_nest_props()
+ if nprops.in_nest_mode:
layout.label(text=f"Nest Mode", icon="EMPTY_AXIS")
row = cls.layout.row(align=True)
op = row.operator("bim.disable_nest_mode", text="", icon="X")
diff --git a/src/bonsai/bonsai/bim/module/nest/decorator.py b/src/bonsai/bonsai/bim/module/nest/decorator.py
index 64f3083b78..5a16ef8e44 100644
--- a/src/bonsai/bonsai/bim/module/nest/decorator.py
+++ b/src/bonsai/bonsai/bim/module/nest/decorator.py
@@ -20,6 +20,7 @@ import blf
import bpy
import gpu
import ifcopenshell
+import ifcopenshell.util.element
import bonsai.tool as tool
from bpy.types import SpaceView3D
from bpy_extras import view3d_utils
@@ -162,8 +163,9 @@ class NestDecorator:
shader.uniform_float("color", color)
batch.draw(shader)
- def draw_nest(self, context):
- if context.scene.BIMNestProperties.in_nest_mode:
+ def draw_nest(self, context: bpy.types.Context) -> None:
+ props = tool.Nest.get_nest_props()
+ if props.in_nest_mode:
return
self.addon_prefs = tool.Blender.get_addon_preferences()
decorator_color_special = self.addon_prefs.decorator_color_special
@@ -262,7 +264,7 @@ class NestModeDecorator:
return
region = context.region
rv3d = region.data
- props = context.scene.BIMNestProperties
+ props = tool.Nest.get_nest_props()
aggregate_obj = props.editing_nest
if not aggregate_obj:
@@ -291,7 +293,7 @@ class NestModeDecorator:
def draw_nest_empty(self, context):
if context.mode == "EDIT_MESH":
return
- props = context.scene.BIMNestProperties
+ props = tool.Nest.get_nest_props()
nest_obj = props.editing_nest
if not nest_obj:
return
diff --git a/src/bonsai/bonsai/bim/module/nest/operator.py b/src/bonsai/bonsai/bim/module/nest/operator.py
index f3247817ec..68b6839e79 100644
--- a/src/bonsai/bonsai/bim/module/nest/operator.py
+++ b/src/bonsai/bonsai/bim/module/nest/operator.py
@@ -158,7 +158,7 @@ class BIM_OT_toggle_nest_mode_local_view(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- props = context.scene.BIMNestProperties
+ props = tool.Nest.get_nest_props()
objs = [o.obj for o in props.editing_objects]
if props.in_nest_mode:
if context.space_data.local_view:
diff --git a/src/bonsai/bonsai/bim/module/nest/prop.py b/src/bonsai/bonsai/bim/module/nest/prop.py
index d5e48b2479..6cb787b3d4 100644
--- a/src/bonsai/bonsai/bim/module/nest/prop.py
+++ b/src/bonsai/bonsai/bim/module/nest/prop.py
@@ -32,10 +32,11 @@ from bpy.props import (
CollectionProperty,
)
from bonsai.bim.module.nest.decorator import NestDecorator, NestModeDecorator
+from typing import TYPE_CHECKING, Union
-def update_relating_object(self, context):
- def message(self, context):
+def update_relating_object(self: "BIMObjectNestProperties", context: bpy.types.Context) -> None:
+ def message(self, context: bpy.types.Context) -> None:
self.layout.label(text="Please select a valid Ifc Element")
if self.relating_object is None:
@@ -49,15 +50,19 @@ class BIMObjectNestProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_object: PointerProperty(name="Nest Host", type=bpy.types.Object, update=update_relating_object)
+ if TYPE_CHECKING:
+ is_editing: bool
+ relating_object: bpy.types.Object
-def update_nest_decorator(self, context):
+
+def update_nest_decorator(self: "BIMNestProperties", context: bpy.types.Context) -> None:
if self.nest_decorator:
NestDecorator.install(bpy.context)
else:
NestDecorator.uninstall()
-def update_nest_mode_decorator(self, context):
+def update_nest_mode_decorator(self: "BIMNestProperties", context: bpy.types.Context) -> None:
if self.in_nest_mode:
NestModeDecorator.install(bpy.context)
else:
@@ -68,6 +73,10 @@ class Objects(bpy.types.PropertyGroup):
obj: PointerProperty(type=bpy.types.Object)
previous_display_type: bpy.props.StringProperty(default="TEXTURED")
+ if TYPE_CHECKING:
+ obj: Union[bpy.types.Object, None]
+ previous_display_type: str
+
class BIMNestProperties(PropertyGroup):
in_nest_mode: BoolProperty(name="In Edit Mode", update=update_nest_mode_decorator)
@@ -79,3 +88,10 @@ class BIMNestProperties(PropertyGroup):
default=False,
update=update_nest_decorator,
)
+
+ if TYPE_CHECKING:
+ in_nest_mode: bool
+ editing_nest: Union[bpy.types.Object, None]
+ editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
+ not_editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
+ nest_decorator: bool
diff --git a/src/bonsai/bonsai/bim/module/nest/ui.py b/src/bonsai/bonsai/bim/module/nest/ui.py
index 601fedf098..2ea33de49a 100644
--- a/src/bonsai/bonsai/bim/module/nest/ui.py
+++ b/src/bonsai/bonsai/bim/module/nest/ui.py
@@ -48,7 +48,8 @@ class BIM_PT_nest(Panel):
if not NestData.is_loaded:
NestData.load()
- props = context.active_object.BIMObjectNestProperties
+ assert (obj := context.active_object)
+ props = tool.Nest.get_object_nest_props(obj)
if props.is_editing:
row = layout.row(align=True)
diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py
index c204200a94..fcc9303fc4 100644
--- a/src/bonsai/bonsai/bim/module/root/operator.py
+++ b/src/bonsai/bonsai/bim/module/root/operator.py
@@ -35,6 +35,7 @@ import bonsai.bim.module.root.prop as root_prop
from bonsai.bim.ifc import IfcStore
from bonsai.bim.helper import get_enum_items, prop_with_search
from mathutils import Vector
+from typing import TYPE_CHECKING
class EnableReassignClass(bpy.types.Operator):
@@ -182,6 +183,15 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
should_add_representation: bpy.props.BoolProperty(default=True)
ifc_representation_class: bpy.props.StringProperty()
+ if TYPE_CHECKING:
+ obj: str
+ ifc_class: str
+ predefined_type: str
+ userdefined_type: str
+ context_id: int
+ should_add_representation: bool
+ ifc_representation_class: str
+
def _execute(self, context):
props = tool.Root.get_root_props()
objects: list[bpy.types.Object] = []
diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py
index a6e8d2b25f..e73dab5288 100644
--- a/src/bonsai/bonsai/bim/ui.py
+++ b/src/bonsai/bonsai/bim/ui.py
@@ -1262,8 +1262,8 @@ class BIM_PT_decorators_overlay(Panel):
overlay = view.overlay
georeference_props = tool.Georeference.get_georeference_props()
- aggregate_props = bpy.context.scene.BIMAggregateProperties
- nest_props = bpy.context.scene.BIMNestProperties
+ aggregate_props = tool.Aggregate.get_aggregate_props()
+ nest_props = tool.Nest.get_nest_props()
model_props = tool.Model.get_model_props()
display_all = overlay.show_overlays
diff --git a/src/bonsai/bonsai/core/nest.py b/src/bonsai/bonsai/core/nest.py
index 45eb660a9d..18fe5c5831 100644
--- a/src/bonsai/bonsai/core/nest.py
+++ b/src/bonsai/bonsai/core/nest.py
@@ -25,15 +25,21 @@ if TYPE_CHECKING:
import bonsai.tool as tool
-def enable_editing_nest(nest, obj=None):
+def enable_editing_nest(nest: tool.Nest, obj: bpy.types.Object) -> None:
nest.enable_editing(obj)
-def disable_editing_nest(nest, obj=None):
+def disable_editing_nest(nest: tool.Nest, obj: bpy.types.Object) -> None:
nest.disable_editing(obj)
-def assign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
+def assign_object(
+ ifc: tool.Ifc,
+ nest: tool.Nest,
+ collector: tool.Collector,
+ relating_obj: bpy.types.Object,
+ related_obj: bpy.types.Object,
+) -> Union[ifcopenshell.entity_instance, None]:
if not nest.can_nest(relating_obj, related_obj):
return
rel = ifc.run(
@@ -47,14 +53,22 @@ def assign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
return rel
-def unassign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
+def unassign_object(
+ ifc: tool.Ifc,
+ nest: tool.Nest,
+ collector: tool.Collector,
+ relating_obj: bpy.types.Object,
+ related_obj: bpy.types.Object,
+) -> Union[ifcopenshell.entity_instance, None]:
related_element = ifc.get_entity(related_obj)
+ assert related_element
container = nest.get_container(related_element)
+ # TODO: this branch is never used?
if not relating_obj:
relating_element = nest.get_relating_object(related_element)
if related_element:
relating_obj = ifc.get_object(relating_element)
- if relating_obj:
+ else:
ifc.run("nest.unassign_object", related_objects=[related_element])
if container:
ifc.run("spatial.assign_container", products=[related_element], relating_structure=container)
@@ -62,21 +76,28 @@ def unassign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
collector.assign(related_obj)
-def add_part_to_object(ifc, nest, collector, blender, obj, part_class, part_name=None):
+def add_part_to_object(
+ ifc: tool.Ifc,
+ nest: tool.Nest,
+ collector: tool.Collector,
+ blender: tool.Blender,
+ obj: bpy.types.Object,
+ part_class: str,
+ part_name: str,
+) -> None:
part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name)
assign_object(ifc, nest, collector, relating_obj=obj, related_obj=part_obj)
- blender.set_active_object(obj)
def enable_nest_mode(
nest: tool.Nest,
obj: bpy.types.Object,
-):
+) -> None:
if nest.get_nest_mode():
disable_nest_mode(nest)
nest.enable_nest_mode(obj)
-def disable_nest_mode(nest: tool.Nest):
+def disable_nest_mode(nest: tool.Nest) -> None:
nest.disable_nest_mode()
diff --git a/src/bonsai/bonsai/tool/aggregate.py b/src/bonsai/bonsai/tool/aggregate.py
index d3c0589cea..e2fb4df5f9 100644
--- a/src/bonsai/bonsai/tool/aggregate.py
+++ b/src/bonsai/bonsai/tool/aggregate.py
@@ -16,15 +16,27 @@
# 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
import ifcopenshell.util.element
import ifcopenshell.util.representation
-from typing import Union
+from typing import Union, TYPE_CHECKING, Literal
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.aggregate.prop import BIMAggregateProperties, BIMObjectAggregateProperties
class Aggregate(bonsai.core.tool.Aggregate):
+ @classmethod
+ def get_aggregate_props(cls) -> BIMAggregateProperties:
+ return bpy.context.scene.BIMAggregateProperties
+
+ @classmethod
+ def get_object_aggregate_props(cls, obj: bpy.types.Object) -> BIMObjectAggregateProperties:
+ return obj.BIMObjectAggregateProperties
+
@classmethod
def can_aggregate(cls, relating_obj: bpy.types.Object, related_obj: bpy.types.Object) -> bool:
relating_object = tool.Ifc.get_entity(relating_obj)
@@ -56,11 +68,13 @@ class Aggregate(bonsai.core.tool.Aggregate):
@classmethod
def disable_editing(cls, obj: bpy.types.Object) -> None:
- obj.BIMObjectAggregateProperties.is_editing = False
+ props = cls.get_object_aggregate_props(obj)
+ props.is_editing = False
@classmethod
def enable_editing(cls, obj: bpy.types.Object) -> None:
- obj.BIMObjectAggregateProperties.is_editing = True
+ props = cls.get_object_aggregate_props(obj)
+ props.is_editing = True
@classmethod
def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
@@ -86,13 +100,14 @@ class Aggregate(bonsai.core.tool.Aggregate):
return parts
@classmethod
- def get_aggregate_mode(cls):
- return bpy.context.scene.BIMAggregateProperties.in_aggregate_mode
+ def get_aggregate_mode(cls) -> bool:
+ props = cls.get_aggregate_props()
+ return props.in_aggregate_mode
@classmethod
- def enable_aggregate_mode(cls, active_object: bpy.types.Object):
+ def enable_aggregate_mode(cls, active_object: bpy.types.Object) -> set[Literal["FINISHED"]]:
context = bpy.context
- props = context.scene.BIMAggregateProperties
+ props = cls.get_aggregate_props()
element = tool.Ifc.get_entity(active_object)
if not element:
@@ -131,7 +146,7 @@ class Aggregate(bonsai.core.tool.Aggregate):
@classmethod
def disable_aggregate_mode(cls):
context = bpy.context
- props = context.scene.BIMAggregateProperties
+ props = cls.get_aggregate_props()
for obj_prop in props.not_editing_objects:
obj = obj_prop.obj
if not obj:
diff --git a/src/bonsai/bonsai/tool/nest.py b/src/bonsai/bonsai/tool/nest.py
index a4f0dc20ec..094299c499 100644
--- a/src/bonsai/bonsai/tool/nest.py
+++ b/src/bonsai/bonsai/tool/nest.py
@@ -16,13 +16,26 @@
# 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
import ifcopenshell.util.element
+from typing import TYPE_CHECKING, Union
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.nest.prop import BIMNestProperties, BIMObjectNestProperties
class Nest(bonsai.core.tool.Nest):
+ @classmethod
+ def get_nest_props(cls) -> BIMNestProperties:
+ return bpy.context.scene.BIMNestProperties
+
+ @classmethod
+ def get_object_nest_props(cls, obj: bpy.types.Object) -> BIMObjectNestProperties:
+ return obj.BIMObjectNestProperties
+
@classmethod
def can_nest(cls, relating_obj, related_obj):
relating_object = tool.Ifc.get_entity(relating_obj)
@@ -34,19 +47,23 @@ class Nest(bonsai.core.tool.Nest):
return False
@classmethod
- def disable_editing(cls, obj):
- obj.BIMObjectNestProperties.is_editing = False
+ def disable_editing(cls, obj: bpy.types.Object) -> None:
+ props = cls.get_object_nest_props(obj)
+ props.is_editing = False
@classmethod
- def enable_editing(cls, obj):
- obj.BIMObjectNestProperties.is_editing = True
+ def enable_editing(cls, obj: bpy.types.Object) -> None:
+ props = cls.get_object_nest_props(obj)
+ props.is_editing = True
@classmethod
- def get_container(cls, element):
+ def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.element.get_container(element)
@classmethod
- def get_relating_object(cls, related_element):
+ def get_relating_object(
+ cls, related_element: ifcopenshell.entity_instance
+ ) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.element.get_nest(related_element)
@classmethod
@@ -61,13 +78,14 @@ class Nest(bonsai.core.tool.Nest):
return components
@classmethod
- def get_nest_mode(cls):
- return bpy.context.scene.BIMNestProperties.in_nest_mode
+ def get_nest_mode(cls) -> bool:
+ props = cls.get_nest_props()
+ return props.in_nest_mode
@classmethod
def enable_nest_mode(cls, active_object: bpy.types.Object):
context = bpy.context
- props = context.scene.BIMNestProperties
+ props = cls.get_nest_props()
element = tool.Ifc.get_entity(active_object)
if not element:
diff --git a/src/bonsai/bonsai/tool/root.py b/src/bonsai/bonsai/tool/root.py
index 7ce135b730..bb6aa116c0 100644
--- a/src/bonsai/bonsai/tool/root.py
+++ b/src/bonsai/bonsai/tool/root.py
@@ -238,13 +238,13 @@ class Root(bonsai.core.tool.Root):
@classmethod
def is_in_aggregate_mode(cls, element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
- props = bpy.context.scene.BIMAggregateProperties
+ props = tool.Aggregate.get_aggregate_props()
if props.editing_aggregate and props.in_aggregate_mode:
return tool.Ifc.get_entity(props.editing_aggregate)
@classmethod
def is_in_nest_mode(cls, element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
- props = bpy.context.scene.BIMNestProperties
+ props = tool.Nest.get_nest_props()
if props.editing_nest and props.in_nest_mode:
return tool.Ifc.get_entity(props.editing_nest)
diff --git a/src/bonsai/test/tool/test_aggregate.py b/src/bonsai/test/tool/test_aggregate.py
index 0f58f617eb..94130a5661 100644
--- a/src/bonsai/test/tool/test_aggregate.py
+++ b/src/bonsai/test/tool/test_aggregate.py
@@ -122,14 +122,16 @@ class TestDisableEditing(NewFile):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
- assert obj.BIMObjectAggregateProperties.is_editing is False
+ props = tool.Aggregate.get_object_aggregate_props(obj)
+ assert props.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
- assert obj.BIMObjectAggregateProperties.is_editing is True
+ props = tool.Aggregate.get_object_aggregate_props(obj)
+ assert props.is_editing is True
class TestGetContainer(NewFile):
diff --git a/src/bonsai/test/tool/test_nest.py b/src/bonsai/test/tool/test_nest.py
index c68f5ae692..c9499e40de 100644
--- a/src/bonsai/test/tool/test_nest.py
+++ b/src/bonsai/test/tool/test_nest.py
@@ -54,14 +54,16 @@ class TestDisableEditing(NewFile):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
- assert obj.BIMObjectNestProperties.is_editing is False
+ props = tool.Nest.get_object_nest_props(obj)
+ assert props.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
- assert obj.BIMObjectNestProperties.is_editing is True
+ props = tool.Nest.get_object_nest_props(obj)
+ assert props.is_editing is True
class TestGetContainer(NewFile):
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
index 8244c3e434..d6d729d8aa 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
@@ -112,15 +112,15 @@ class Usecase:
ifc_vertices: list[ifcopenshell.entity_instance]
coordinate_offset: Union[npt.NDArray[np.float64], None]
geometry: Union[bpy.types.Mesh, bpy.types.Curve]
+ blender_object: bpy.types.Object
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
self.is_manifold = None
self.coordinate_offset = self.settings["coordinate_offset"]
self.geometry = self.settings["geometry"]
- if (
- isinstance(self.settings["geometry"], bpy.types.Mesh)
- and self.settings["geometry"] == self.settings["blender_object"].data
- ):
+ self.blender_object = self.settings["blender_object"]
+
+ if isinstance(self.geometry, bpy.types.Mesh) and self.geometry == self.blender_object.data:
self.evaluate_geometry()
if self.settings["unit_scale"] is None:
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -147,11 +147,11 @@ class Usecase:
return any([abs((tM @ v.co).z) > threshold for v in face.verts])
def evaluate_geometry(self) -> None:
- for modifier in self.settings["blender_object"].modifiers:
+ for modifier in self.blender_object.modifiers:
if modifier.type == "BOOLEAN":
modifier.show_viewport = False
- mesh = self.settings["blender_object"].evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
+ mesh = self.blender_object.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
bm = bmesh.new()
bm.from_mesh(mesh)
@@ -173,7 +173,7 @@ class Usecase:
self.settings["geometry"] = mesh
- for modifier in self.settings["blender_object"].modifiers:
+ for modifier in self.blender_object.modifiers:
if modifier.type == "BOOLEAN":
modifier.show_viewport = True
@@ -540,7 +540,7 @@ class Usecase:
# create dummy object that will have more detailed curves
# since now we do not really support splines curves natively
- obj = self.settings["blender_object"]
+ obj = self.blender_object
dummy = bpy.data.objects.new("Dummy", obj.data.copy())
bpy.context.scene.collection.objects.link(dummy)
tool.Blender.select_and_activate_single_object(bpy.context, dummy)
@@ -752,7 +752,7 @@ class Usecase:
profile_def,
position,
self.file.createIfcDirection((0.0, 0.0, 1.0)),
- self.convert_si_to_unit(self.settings["blender_object"].dimensions[2]),
+ self.convert_si_to_unit(self.blender_object.dimensions[2]),
)
return self.file.createIfcShapeRepresentation(
self.settings["context"],
@@ -952,7 +952,7 @@ class Usecase:
)
def create_box_representation(self) -> ifcopenshell.entity_instance:
- obj = self.settings["blender_object"]
+ obj = self.blender_object
bounding_box = self.file.createIfcBoundingBox(
self.create_cartesian_point(obj.bound_box[0][0], obj.bound_box[0][1], obj.bound_box[0][2]),
self.convert_si_to_unit(obj.dimensions[0]),