diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py index 539ec89736..6eb2639a56 100644 --- a/src/bonsai/bonsai/bim/import_ifc.py +++ b/src/bonsai/bonsai/bim/import_ifc.py @@ -871,7 +871,7 @@ class IfcImporter: for ifcclass in self.classes_to_wireframe_list: if element.is_a(ifcclass): - obj.display_type = "WIRE" + tool.Blender.Display.set_wireframe(obj) if shape and mesh: # We use numpy here because Blender mathutils.Matrix is not accurate enough diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 2f5b9c906e..4941e67b5d 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -1546,11 +1546,12 @@ class ToggleLinkVisibility(bpy.types.Operator): def toggle_wireframe(self, link: "Link") -> None: link.is_wireframe = not link.is_wireframe - display_type = "WIRE" if link.is_wireframe else "TEXTURED" for collection in self.get_linked_collections(): objs = filter(lambda obj: "IfcOpeningElement" not in obj.name, collection.all_objects) - for obj in objs: - obj.display_type = display_type + if link.is_wireframe: + tool.Blender.Display.set_wireframe(objs) + else: + tool.Blender.Display.set_textured(objs) def toggle_visibility(self, link: "Link") -> None: linked_collections = self.get_linked_collections() diff --git a/src/bonsai/bonsai/tool/aggregate.py b/src/bonsai/bonsai/tool/aggregate.py index db037c2b02..82f2ca71a9 100644 --- a/src/bonsai/bonsai/tool/aggregate.py +++ b/src/bonsai/bonsai/tool/aggregate.py @@ -171,7 +171,7 @@ class Aggregate(bonsai.core.tool.Aggregate): not_editing_obj.obj = obj.original not_editing_obj.previous_display_type = obj.original.display_type not_editing_obj.previous_hide_select = obj.original.hide_select - obj.original.display_type = "WIRE" + tool.Blender.Display.set_wireframe(obj.original) obj.hide_select = True else: editing_obj = props.editing_objects.add() @@ -188,7 +188,7 @@ class Aggregate(bonsai.core.tool.Aggregate): obj = obj_prop.obj if not obj: continue - obj.original.display_type = obj_prop.previous_display_type + tool.Blender.Display.set_display(obj.original, obj_prop.previous_display_type) obj.hide_select = obj_prop.previous_hide_select element = tool.Ifc.get_entity(obj) if not element: diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index df8d12b90b..03f1677aff 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -1059,6 +1059,57 @@ class Blender(bonsai.core.tool.Blender): return False return True + class Display: + """Utilities for managing object display properties in the 3D viewport.""" + + @classmethod + def _set_display_type( + cls, obj: bpy.types.Object, display_type: str, hide_render: Optional[bool] = None + ) -> None: + """Internal helper to set display type and render visibility for a single object. + + :param obj: Object to modify + :param display_type: Display type to set (e.g., 'WIRE', 'TEXTURED', 'SOLID', 'BOUNDS') + :param hide_render: Render visibility state. If None, render visibility is not modified. + Special cases: True for 'WIRE' and 'BOUNDS', False otherwise + """ + obj.display_type = display_type + if hide_render is not None: + obj.hide_render = hide_render + + @classmethod + def set_display(cls, obj: Union[bpy.types.Object, Iterable[bpy.types.Object]], display_type: str) -> None: + """Set object(s) to a specific display type with appropriate render visibility. + + :param obj: Single object or iterable of objects to modify + :param display_type: Display type to set (e.g., 'WIRE', 'TEXTURED', 'SOLID', 'BOUNDS') + Special handling: 'WIRE' and 'BOUNDS' hide from renders, others show in renders + """ + # Determine hide_render based on display type + hide_render = True if display_type in ("WIRE", "BOUNDS") else False + + if isinstance(obj, bpy.types.Object): + cls._set_display_type(obj, display_type, hide_render) + else: + for o in obj: + cls._set_display_type(o, display_type, hide_render) + + @classmethod + def set_wireframe(cls, obj: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None: + """Set object(s) to wireframe display and hide from renders. + + :param obj: Single object or iterable of objects to set to wireframe display + """ + cls.set_display(obj, "WIRE") + + @classmethod + def set_textured(cls, obj: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None: + """Set object(s) to textured display and show in renders. + + :param obj: Single object or iterable of objects to set to textured display + """ + cls.set_display(obj, "TEXTURED") + class Modifier: @classmethod def try_applying_edit_mode(cls, obj: bpy.types.Object, element: entity_instance) -> bool: diff --git a/src/bonsai/bonsai/tool/collector.py b/src/bonsai/bonsai/tool/collector.py index 65d5ae49bd..595eb89b02 100644 --- a/src/bonsai/bonsai/tool/collector.py +++ b/src/bonsai/bonsai/tool/collector.py @@ -133,7 +133,7 @@ class Collector(bonsai.core.tool.Collector): cls.link_collection_object_safe(collection, obj) if element.is_a("IfcFeatureElementSubtraction"): - obj.display_type = "WIRE" + tool.Blender.Display.set_wireframe(obj) @classmethod def _create_project_child_collection(cls, name: str) -> bpy.types.Collection: diff --git a/src/bonsai/bonsai/tool/nest.py b/src/bonsai/bonsai/tool/nest.py index 094299c499..b1d6bb1ab8 100644 --- a/src/bonsai/bonsai/tool/nest.py +++ b/src/bonsai/bonsai/tool/nest.py @@ -111,7 +111,7 @@ class Nest(bonsai.core.tool.Nest): not_editing_obj = props.not_editing_objects.add() not_editing_obj.obj = obj.original not_editing_obj.previous_display_type = obj.original.display_type - obj.original.display_type = "WIRE" + tool.Blender.Display.set_wireframe(obj.original) else: editing_obj = props.editing_objects.add() editing_obj.obj = obj.original @@ -125,7 +125,7 @@ class Nest(bonsai.core.tool.Nest): props = context.scene.BIMNestProperties for obj_prop in props.not_editing_objects: obj = obj_prop.obj - obj.original.display_type = obj_prop.previous_display_type + tool.Blender.Display.set_display(obj.original, obj_prop.previous_display_type) element = tool.Ifc.get_entity(obj) if not element: continue diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index a177a237e8..8ffc131cad 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -1234,15 +1234,13 @@ class Spatial(bonsai.core.tool.Spatial): if first_obj.display_type == "TEXTURED": for space in spaces: obj = tool.Ifc.get_object(space) - obj.show_wire = True - obj.display_type = "WIRE" + tool.Blender.Display.set_wireframe(obj) return elif first_obj.display_type == "WIRE": for space in spaces: obj = tool.Ifc.get_object(space) - obj.show_wire = False - obj.display_type = "TEXTURED" + tool.Blender.Display.set_textured(obj) return @classmethod