This commit is contained in:
Nicolas Predella 2026-07-29 19:56:41 +03:00
parent c97be7bdd0
commit f932939534
4 changed files with 134 additions and 1 deletions

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Any
import numpy as np
from ..bpy_ns_types import NfBoolProperty, NfEnumProperty, NfIntProperty, NfFloatProperty, notify_tree_changed
from ..bpy_ns_types import NfBoolProperty, NfEnumProperty, NfIntProperty, NfFloatProperty, NfStringProperty, notify_tree_changed
from ....registry import register_class
from .base import SliceNode, SlicingCollection, get_all_children, SliceNodeJoiner, SlicingGroup, SlicingObject
@ -549,3 +549,67 @@ class SLICENODE_DropGeometry(SliceNode):
copied_data.transforms.append(drop_rec)
return copied_data
def _rename_slicing_item(geom: Any, new_name: str) -> Any:
if geom is None or not new_name:
return geom
elif isinstance(geom, (list, tuple)):
return [_rename_slicing_item(item, f"{new_name}_{i}" if len(geom) > 1 else new_name) for i, item in enumerate(geom)]
elif isinstance(geom, SlicingGroup):
copied = _copy_slicing_group(geom)
old_colls = dict(copied.collections)
new_colls = {}
for i, (old_key, coll) in enumerate(old_colls.items()):
coll_name = f"{new_name}_{i}" if len(old_colls) > 1 else new_name
coll.name = coll_name
for obj in coll.objects:
obj.parent = coll_name
obj.name = coll_name
new_colls[coll_name] = coll
copied.collections = new_colls
return copied
elif isinstance(geom, SlicingCollection):
copied = _copy_slicing_collection(geom)
copied.name = new_name
for obj in copied.objects:
obj.parent = new_name
obj.name = new_name
return copied
elif isinstance(geom, SlicingObject):
copied = _copy_slicing_object(geom)
copied.name = new_name
return copied
return geom
@register_class
class SLICENODE_RenameMesh(SliceNode):
bl_label = "Rename Mesh"
new_name: NfStringProperty(name="Name", default="RenamedMesh") # pyright: ignore[reportInvalidTypeForm]
def init(self, context):
self.add_input("SLICESOCK_Mesh", "Geometry")
self.add_input("SLICESOCK_String", "Name")
self.add_output("SLICESOCK_Mesh", "Geometry")
def draw_buttons(self, context, layout):
super().draw_buttons(context, layout)
layout.prop(self, "new_name")
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
if socket_name != "Geometry":
return None
geom: Any = self.resolve("Geometry")
if not geom:
return None
name_socket: str | None = self.resolve("Name")
target_name = name_socket if name_socket else self.new_name
if not target_name:
return geom
return _rename_slicing_item(geom, target_name)

View file

@ -498,6 +498,7 @@ class SLICENODE_MT_Geometry(Menu):
layout = self.layout
assert layout
layout.operator("node.add_node", text="Get Mesh").type = "SLICENODE_GetMesh"
layout.operator("node.add_node", text="Rename Mesh").type = "SLICENODE_RenameMesh"
layout.operator("node.add_node", text="Transform Geometry").type = "SLICENODE_TransformGeometry"
layout.operator("node.add_node", text="Drop Geometry").type = "SLICENODE_DropGeometry"
layout.operator("node.add_node", text="Separate Mesh").type = "SLICENODE_SeparateMesh"

View file

@ -60,6 +60,9 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
### Geometry Processing
- **Get Mesh**: Converts a Blender Object into internal `SlicingObject` format.
- **Rename Mesh**: Overrides the internal metadata name of a `SlicingObject`, `SlicingCollection`, or `SlicingGroup`.
- **Geometry**: Input geometry data.
- **Name**: String input socket or property for the new name.
- **Transform Geometry**: Moves, rotates, and scales a `SlicingCollection` or `SlicingGroup` relative to the origin.
- **Mesh**: Input mesh data.
- **Translation**: 3D vector specifying the translation offset.

65
tests/test_rename_mesh.py Normal file
View file

@ -0,0 +1,65 @@
import sys
import unittest
from unittest.mock import MagicMock
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.append(str(PROJECT_ROOT.parent))
# Mock bpy and dependencies for unit testing
class DummyNodeSocket: pass
class DummyNode: pass
class DummyObject:
def __init__(self, name="TestObj"):
self.name = name
mock_types = MagicMock()
mock_types.NodeSocket = DummyNodeSocket
mock_types.Node = DummyNode
mock_types.Object = DummyObject
mock_bpy = MagicMock()
mock_bpy.context.scene = MagicMock()
mock_bpy.context.scene.unit_settings.scale_length = 0.001
mock_bpy.types = mock_types
sys.modules['bpy'] = mock_bpy
sys.modules['bpy.types'] = mock_types
sys.modules['bpy.props'] = MagicMock()
sys.modules['bpy.utils'] = MagicMock()
from NodeSlicer.infra.blender.blender_mesh_capture import SlicingObject, SlicingCollection, SlicingGroup
from NodeSlicer.core.nodeslicer.nodes.mesh import _rename_slicing_item
class TestRenameMesh(unittest.TestCase):
def test_rename_slicing_object(self):
so = SlicingObject()
so.name = "OldName"
renamed = _rename_slicing_item(so, "NewName")
self.assertEqual(renamed.name, "NewName")
def test_rename_slicing_collection(self):
so1 = SlicingObject()
so1.name = "Part1"
sc = SlicingCollection([], "OldCollection")
sc.objects = [so1]
renamed = _rename_slicing_item(sc, "NewColl")
self.assertEqual(renamed.name, "NewColl")
self.assertEqual(renamed.objects[0].name, "NewColl")
self.assertEqual(renamed.objects[0].parent, "NewColl")
def test_rename_slicing_group(self):
sc = SlicingCollection([], "Coll1")
sg = SlicingGroup([])
sg.collections = {"Coll1": sc}
renamed = _rename_slicing_item(sg, "NewGroup")
self.assertIn("NewGroup", renamed.collections)
self.assertEqual(renamed.collections["NewGroup"].name, "NewGroup")
if __name__ == '__main__':
unittest.main()