From c5c8df097371af1da1f05022877aabe2ba96f77a Mon Sep 17 00:00:00 2001 From: Nicolas Predella Date: Tue, 13 Oct 2026 16:22:23 +0300 Subject: [PATCH] flattening in join --- core/nodeslicer/nodes/base.py | 45 +++++++++++++++++++++++++++++++++-- tests/test_lists.py | 33 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/core/nodeslicer/nodes/base.py b/core/nodeslicer/nodes/base.py index 734a557..b47ea9b 100644 --- a/core/nodeslicer/nodes/base.py +++ b/core/nodeslicer/nodes/base.py @@ -164,13 +164,54 @@ class SliceNodeListJoiner(SliceNode): def evaluate_output(self, socket_name: str, **kwargs) -> list[Any]: from ....infra.blender.list_utils import flatten_list, is_list_data items: list[Any] = [] - raw_inputs = self.resolve_multi_input(self.socket_label) - for val in raw_inputs: + + if "resolve_multi_input" in self.__dict__: + raw_inputs = self.resolve_multi_input(self.socket_label) + for val in raw_inputs: + if val is not None: + if is_list_data(val): + items.extend(flatten_list(val)) + else: + items.append(val) + return items + + target_inputs = [ + i for i in getattr(self, "inputs", []) + if getattr(i, "name", "") == self.socket_label or getattr(i, "identifier", "").startswith(f"{self.socket_label}_ms_in_") + ] + + if not target_inputs: + raw_inputs = self.resolve_multi_input(self.socket_label) + for val in raw_inputs: + if val is not None: + if is_list_data(val): + items.extend(flatten_list(val)) + else: + items.append(val) + return items + + cache = getattr(self, "_current_cache", None) + visited = getattr(self, "_current_visited", None) + from ....infra.blender.blender_slice_nodes_generic import resolve_input + + for inp in target_inputs: + if not getattr(inp, "is_linked", False): + val = getattr(inp, "value", None) + if val is None or val == "": + val = getattr(inp, "default_value", None) + if val is None or val == "": + continue + if type(val).__name__ == "SlicePaths" and len(val) == 0: + continue + else: + val = resolve_input(inp, cache=cache, visited=visited, fieldname=self.socket_label, node=self) + if val is not None: if is_list_data(val): items.extend(flatten_list(val)) else: items.append(val) + return items class ExecSliceNode(SliceNode): diff --git a/tests/test_lists.py b/tests/test_lists.py index 8fbbe95..0373e31 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -383,6 +383,39 @@ class TestNodeEvaluationsWithLists(unittest.TestCase): g_node.resolve_multi_input = lambda label: [["G1 X1"], "G1 X2"] self.assertEqual(g_node.evaluate_output("Gcode"), ["G1 X1", "G1 X2"]) + def test_x_to_list_flattens_nested_and_incoming_lists(self): + from NodeSlicer.core.nodeslicer.nodes.inputs import SLICENODE_ObjectsToList + from NodeSlicer.core.nodeslicer.nodes.mesh import SLICENODE_MeshesToList + from NodeSlicer.core.nodeslicer.nodes.path import SLICENODE_PathsToList + from NodeSlicer.core.nodeslicer.nodes.gcode import SLICENODE_GcodesToList + + # ObjectsToList with nested lists and single elements + obj1, obj2, obj3, obj4 = DummyObject("O1"), DummyObject("O2"), DummyObject("O3"), DummyObject("O4") + o_node = SLICENODE_ObjectsToList() + o_node.resolve_multi_input = lambda label: [[obj1, obj2], obj3, [obj4]] + self.assertEqual(o_node.evaluate_output("Objects"), [obj1, obj2, obj3, obj4]) + + # MeshesToList with nested lists + sc1 = SlicingCollection([], "M1") + sc2 = SlicingCollection([], "M2") + sc3 = SlicingCollection([], "M3") + m_node = SLICENODE_MeshesToList() + m_node.resolve_multi_input = lambda label: [[sc1, sc2], [sc3]] + self.assertEqual(m_node.evaluate_output("Meshes"), [sc1, sc2, sc3]) + + # PathsToList with list input and single input + sp1 = SlicePaths([np.array([[0, 0, 0], [1, 1, 0]], dtype=np.float64)]) + sp2 = SlicePaths([np.array([[2, 2, 0], [3, 3, 0]], dtype=np.float64)]) + sp3 = SlicePaths([np.array([[4, 4, 0], [5, 5, 0]], dtype=np.float64)]) + p_node = SLICENODE_PathsToList() + p_node.resolve_multi_input = lambda label: [[sp1, sp2], sp3] + self.assertEqual(p_node.evaluate_output("Paths"), [sp1, sp2, sp3]) + + # GcodesToList with list input and single input + g_node = SLICENODE_GcodesToList() + g_node.resolve_multi_input = lambda label: [["G1 X1", "G1 X2"], "G1 X3", ["G1 X4", "G1 X5"]] + self.assertEqual(g_node.evaluate_output("Gcode"), ["G1 X1", "G1 X2", "G1 X3", "G1 X4", "G1 X5"]) + def test_join_mesh_flattens_lists(self): from NodeSlicer.core.nodeslicer.nodes.mesh import SLICENODE_JoinMesh