diff --git a/core/nodeslicer/nodes/gcode.py b/core/nodeslicer/nodes/gcode.py index 7a287f9..93f0e3a 100644 --- a/core/nodeslicer/nodes/gcode.py +++ b/core/nodeslicer/nodes/gcode.py @@ -17,7 +17,7 @@ class SLICENODE_PathsToGcode(SliceNode): def init(self, context): self.add_input("SLICESOCK_Paths") self.add_input("SLICESOCK_Intrinsics") - self.add_output("SLICESOCK_GcodeRo") + self.add_output("NodeSocketString") def draw_buttons(self, context, layout): assert layout @@ -53,7 +53,7 @@ class SLICENODE_GcodeBlock(SliceNode): bl_label = "Gcode Block" def init(self, context): - self.add_output("SLICESOCK_Gcode", "Gcode") + self.add_output("NodeSocketString", "Gcode") def evaluate_output(self, socket_name: str, **_kwargs) -> str: return self.outputs['Gcode'].value @@ -66,7 +66,7 @@ class SLICENODE_AssignMetadata(SliceNode): self.add_input("NodeSocketString", "Name") self.add_input("NodeSocketString", "Filament Type") self.add_input("NodeSocketString", "Printer Model") - self.add_output("SLICESOCK_GcodeRo", "Gcode") + self.add_output("NodeSocketString", "Gcode") def evaluate_output(self, socket_name: str, **_kwargs) -> str: name = self.resolve("Name") @@ -86,10 +86,10 @@ class SLICENODE_GcodeRegexReplace(SliceNode): bl_label = "Gcode Regex Replace" def init(self, context): - self.add_input("SLICESOCK_Gcode", "Gcode") + self.add_input("NodeSocketString", "Gcode") self.add_input("NodeSocketString", "Find") self.add_input("NodeSocketString", "Replace") - self.add_output("SLICESOCK_GcodeRo", "Gcode") + self.add_output("NodeSocketString", "Gcode") def evaluate_output(self, socket_name: str, **_kwargs) -> str: import re @@ -154,7 +154,7 @@ class SLICENODE_GcodeHelper(SliceNode): parameters: NfStringProperty(name="Params", default="") # pyright: ignore[reportInvalidTypeForm] def init(self, context): - self.add_output("SLICESOCK_GcodeRo") + self.add_output("NodeSocketString") def draw_buttons(self, context, layout): assert layout @@ -177,8 +177,8 @@ class SLICENODE_GcodeHelper(SliceNode): @register_class class SLICENODE_JoinGcode(SliceNodeJoiner): bl_label = "Join Gcode" - socket_type = "SLICESOCK_Gcode" - output_socket_type = "SLICESOCK_GcodeRo" + socket_type = "NodeSocketString" + output_socket_type = "NodeSocketString" def evaluate_output(self, socket_name: str, **_kwargs) -> str: from ....infra.blender.blender_slice_nodes_generic import resolve_input @@ -193,8 +193,8 @@ class SLICENODE_LoadGcode(SliceNode): bl_label = "Load Gcode File" def init(self, context): - self.add_input("SLICESOCK_FilePath", "Filepath") - self.add_output("SLICESOCK_GcodeRo") + self.add_input("NodeSocketString", "Filepath") + self.add_output("NodeSocketString") def evaluate_output(self, socket_name: str, **_kwargs) -> str: path = self.resolve("Filepath") diff --git a/core/nodeslicer/nodes/mesh.py b/core/nodeslicer/nodes/mesh.py index 2ae35d7..cfc1320 100644 --- a/core/nodeslicer/nodes/mesh.py +++ b/core/nodeslicer/nodes/mesh.py @@ -590,10 +590,12 @@ class SLICENODE_TransformGeometry(SliceNode): self.add_output("SLICESOCK_Geometry", "Geometry") def evaluate_output(self, socket_name: str, **_kwargs) -> Any: - if socket_name not in ("Geometry"): + if socket_name not in ("Geometry", "Mesh"): return None geom_data: Any = self.resolve("Geometry") + if geom_data is None: + geom_data = self.resolve("Mesh") if geom_data is None: return None diff --git a/core/nodeslicer/nodes/output.py b/core/nodeslicer/nodes/output.py index 29c88f4..723583b 100644 --- a/core/nodeslicer/nodes/output.py +++ b/core/nodeslicer/nodes/output.py @@ -19,8 +19,8 @@ class SLICENODE_GcodeToFile(ExecSliceNode): def init(self, context): self._init_exec() - self.add_input("SLICESOCK_DirPath", "Folder") - self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode") + self.add_input("NodeSocketString", "Folder") + self.add_multi_socket_group("NodeSocketString", name="Gcode") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) @@ -73,7 +73,7 @@ class SLICENODE_GcodeToTextBlock(ExecSliceNode): def init(self, context): self._init_exec() - self.add_input("SLICESOCK_Gcode") + self.add_input("NodeSocketString") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) @@ -109,7 +109,7 @@ class SLICENODE_StartPrint(ExecSliceNode): def init(self, context): self._init_exec() - self.add_input("SLICESOCK_Gcode") + self.add_input("NodeSocketString") self.add_input("SLICESOCK_Printer") def evaluate(self, operator=None): @@ -178,7 +178,7 @@ class ExecSliceNode_DatabaseSubmit(ExecSliceNode): def init(self, context): self._init_exec() - self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode") + self.add_multi_socket_group("NodeSocketString", name="Gcode") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) diff --git a/core/nodeslicer/nodes/preview.py b/core/nodeslicer/nodes/preview.py index fe2ac59..5604c61 100644 --- a/core/nodeslicer/nodes/preview.py +++ b/core/nodeslicer/nodes/preview.py @@ -16,7 +16,7 @@ class SLICENODE_GcodeToMesh(ExecSliceNode): def init(self, context): self._init_exec() self.add_input("NodeSocketString", "Name") - self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode") + self.add_multi_socket_group("NodeSocketString", name="Gcode") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) diff --git a/core/nodeslicer/nodes/system.py b/core/nodeslicer/nodes/system.py index cdb5ecb..705120e 100644 --- a/core/nodeslicer/nodes/system.py +++ b/core/nodeslicer/nodes/system.py @@ -41,7 +41,7 @@ class SLICENODE_PrinterList(ExecSliceNode): printer_names.add(name) sock = self.inputs.get(name) if not sock: - self.add_input("SLICESOCK_GcodeRo", name) + self.add_input("NodeSocketString", name) # Remove stale input sockets for sock in list(self.inputs): @@ -162,7 +162,7 @@ class SLICENODE_PrusaslicerSlice(SliceNode): def init(self, context): self.add_input("SLICESOCK_PrusaslicerConf", "Config") self.add_input("SLICESOCK_Settings", "Settings") - self.add_multi_socket_group("SLICESOCK_Mesh", "SLICESOCK_GcodeRo", name="Slicing Group", in_name="Mesh", out_name="Gcode", in_display_shape='LIST') + self.add_multi_socket_group("SLICESOCK_Mesh", "NodeSocketString", name="Slicing Group", in_name="Mesh", out_name="Gcode", in_display_shape='LIST') def draw_buttons(self, context, layout): assert layout @@ -317,7 +317,7 @@ class SLICENODE_PrusaslicerPreview(ExecSliceNode): def init(self, context): self._init_exec() - self.add_multi_socket_group("SLICESOCK_Gcode", name="Gcode") + self.add_multi_socket_group("NodeSocketString", name="Gcode") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) @@ -468,7 +468,7 @@ class SLICENODE_Test(ExecSliceNode): def init(self, context): self._init_exec() - self.add_input("SLICESOCK_Gcode") + self.add_input("NodeSocketString") def evaluate(self, operator=None): _ = self.resolve("Gcode") diff --git a/core/nodeslicer/sockets.py b/core/nodeslicer/sockets.py index 7f4ec2b..fda0b47 100644 --- a/core/nodeslicer/sockets.py +++ b/core/nodeslicer/sockets.py @@ -44,18 +44,7 @@ class ReadonlySocket(): def draw(self, context: Any, layout: Any, node: Any, text: str | None): return self._draw_as_ro(context, layout, node, text) # pyright: ignore[reportAttributeAccessIssue] -# Native Blender socket type aliases -SLICESOCK_Object = "NodeSocketObject" -SLICESOCK_ObjectRo = "NodeSocketObject" -SLICESOCK_Float = "NodeSocketFloat" -SLICESOCK_FloatRo = "NodeSocketFloat" -SLICESOCK_Int = "NodeSocketInt" -SLICESOCK_IntRo = "NodeSocketInt" -SLICESOCK_String = "NodeSocketString" -SLICESOCK_VectorRotation = "NodeSocketRotation" -SLICESOCK_VectorRotationRo = "NodeSocketRotation" -SLICESOCK_Vector = "NodeSocketVector" -SLICESOCK_VectorRo = "NodeSocketVector" + @register_class class SLICESOCK_Geometry(LabelNodeSocket): @@ -86,20 +75,6 @@ class SLICESOCK_Geometry(LabelNodeSocket): else: return _get_upstream_color(self) -@register_class -class SLICESOCK_FilePath(InputNodeSocket): - bl_label = "Filepath" - - value: NfStringProperty(name="Filepath", default="", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm] - bl_color = (0.4, 0.7, 1.0, 1.0) - -@register_class -class SLICESOCK_DirPath(InputNodeSocket): - bl_label = "Filepath" - - value: NfStringProperty(name="Folder", default="", subtype='DIR_PATH') # pyright: ignore[reportInvalidTypeForm] - bl_color = (0.4, 0.7, 1.0, 1.0) - @register_class class SLICESOCK_Mesh(LabelNodeSocket): bl_label = "Mesh" @@ -112,18 +87,6 @@ class SLICESOCK_Paths(LabelNodeSocket): default = SlicePaths() bl_color = (214/255, 0, 120/255, 1.0) - - -class SLICESOCK_Gcode_Common(InputNodeSocket): - bl_label = "Gcode" - value: NfStringProperty(name="Gcode", default="") # pyright: ignore[reportInvalidTypeForm] - bl_color = (1., 1., 1., 1.) - -@register_class -class SLICESOCK_Gcode(SLICESOCK_Gcode_Common): ... -@register_class -class SLICESOCK_GcodeRo(ReadonlySocket, SLICESOCK_Gcode_Common): ... - @register_class class SLICESOCK_Intrinsics(LabelNodeSocket): bl_label = "Intrinsics" diff --git a/core/nodeslicer/tree.py b/core/nodeslicer/tree.py index dfeb937..db3077b 100644 --- a/core/nodeslicer/tree.py +++ b/core/nodeslicer/tree.py @@ -126,6 +126,8 @@ def object_init_handler(scene, depsgraph): update_collection(pg.modifiers) + + @persistent def depsgraph_update_handler(scene, depsgraph): global _is_updating, _tree_changed @@ -134,7 +136,7 @@ def depsgraph_update_handler(scene, depsgraph): tree_update_handler(scene, depsgraph) object_init_handler(scene, depsgraph) - # Sync SLICENODE_Group sockets if interface changed + # Sync SLICENODE_Group sockets if interface changed and migrate legacy sockets for tree in bpy.data.node_groups: if tree.bl_idname == "SLICETREE_NodeTree": for node in tree.nodes: diff --git a/doc/NODES.md b/doc/NODES.md index c79e988..22feea0 100644 --- a/doc/NODES.md +++ b/doc/NODES.md @@ -7,13 +7,12 @@ | `NodeSocketObject` | `bpy.types.Object` | Blender RNA Object | Reference to a Blender Object | | `NodeSocketFloat` | `float` | Python float | Numeric value | | `NodeSocketInt` | `int` | Python int | Integer value | -| `NodeSocketString` | `str` | Python string | Text value | +| `NodeSocketString` | `str` | Python string | Text, G-code, file/folder path value | | `NodeSocketVector` | `tuple` | Python tuple (3) | XYZ vector value | | `NodeSocketRotation` | `tuple` | Python tuple (3) | Euler rotation value | | `NodeSocketGeometry` | `Geometry` | Geometry Data | Generic geometry reference | | `SLICESOCK_Mesh` | `SlicingObject` | NumPy NDArray (Triangles) | Internal mesh data (Transformed/Scaled) | | `SLICESOCK_Paths` | `SlicePaths` (list) | List of paths (N, 3) + groups | Generic toolpaths or slice boundaries | -| `SLICESOCK_Gcode` | `str` | Multi-line string | Generated G-code text | | `SLICESOCK_Settings` | `dict[str, str]` | Python dictionary | Mapping of configuration fields to values | | `SLICESOCK_SettingsRo` | `dict[str, str]` | Python dictionary | Read-only merged configuration overrides | diff --git a/tests/test_transform.py b/tests/test_transform.py index 4cf5ab4..ab03456 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -436,26 +436,26 @@ class TestTransform(unittest.TestCase): node.evaluate() node._process_gcode.assert_called_once_with("GCODE1", "Part", None) - # 2. Three parts - padded to 2 digits (01, 02, 03) + # 2. Three parts - padded to 2 digits (00, 01, 02) node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2", "GC3"]) node.resolve = MagicMock(return_value="Part") node._process_gcode = MagicMock() node.evaluate() self.assertEqual(node._process_gcode.call_count, 3) - node._process_gcode.assert_any_call("GC1", "Part01", None) - node._process_gcode.assert_any_call("GC2", "Part02", None) - node._process_gcode.assert_any_call("GC3", "Part03", None) + node._process_gcode.assert_any_call("GC1", "Part00", None) + node._process_gcode.assert_any_call("GC2", "Part01", None) + node._process_gcode.assert_any_call("GC3", "Part02", None) - # 3. Fifteen parts - padded to 2 digits (01..15) + # 3. Fifteen parts - padded to 2 digits (00..14) node.resolve_multi_input = MagicMock(return_value=[f"GC{i}" for i in range(1, 16)]) node.resolve = MagicMock(return_value="Part") node._process_gcode = MagicMock() node.evaluate() self.assertEqual(node._process_gcode.call_count, 15) - node._process_gcode.assert_any_call("GC1", "Part01", None) - node._process_gcode.assert_any_call("GC15", "Part15", None) + node._process_gcode.assert_any_call("GC1", "Part00", None) + node._process_gcode.assert_any_call("GC15", "Part14", None) # 4. Default base name when empty name provided node.resolve_multi_input = MagicMock(return_value=["GC1", "GC2"]) @@ -463,8 +463,8 @@ class TestTransform(unittest.TestCase): node._process_gcode = MagicMock() node.evaluate() - node._process_gcode.assert_any_call("GC1", "GcodePreview01", None) - node._process_gcode.assert_any_call("GC2", "GcodePreview02", None) + node._process_gcode.assert_any_call("GC1", "GcodePreview00", None) + node._process_gcode.assert_any_call("GC2", "GcodePreview01", None) if __name__ == '__main__':