wipe tower
This commit is contained in:
parent
0e14fa19f7
commit
8cff75d569
6 changed files with 92 additions and 3 deletions
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
from typing import Any
|
||||
import bpy
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from ..bpy_ns_types import NfStringProperty, notify_tree_changed, NfEnumProperty
|
||||
from ....registry import register_class
|
||||
|
|
@ -76,6 +77,32 @@ class SLICENODE_CollectionObjects(SliceNode):
|
|||
return bpy.data.objects[socket_name]
|
||||
return None
|
||||
|
||||
@register_class
|
||||
class SLICENODE_WipeTower(SliceNode):
|
||||
bl_label = "Wipe Tower"
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Float", "X Position").value = 170.0
|
||||
self.add_input("SLICESOCK_Float", "Y Position").value = 140.0
|
||||
self.add_output("SLICESOCK_SettingsRo", "Settings")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Settings":
|
||||
return {}
|
||||
|
||||
raw_x = self.resolve("X Position")
|
||||
raw_y = self.resolve("Y Position")
|
||||
|
||||
if isinstance(raw_x, (list, tuple, np.ndarray)) and len(raw_x) >= 2:
|
||||
pos_x, pos_y = float(raw_x[0]), float(raw_x[1])
|
||||
else:
|
||||
pos_x = float(raw_x) if raw_x is not None else 0.0
|
||||
pos_y = float(raw_y) if raw_y is not None else 0.0
|
||||
|
||||
return {
|
||||
"wipe_tower_position": (pos_x, pos_y)
|
||||
}
|
||||
|
||||
@register_class
|
||||
class SLICENODE_InputSettings(InputNode):
|
||||
input_type = "Settings"
|
||||
|
|
|
|||
|
|
@ -220,8 +220,20 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
|
|||
offset = np.array([bed_center[0], bed_center[1], a_z]) - np.array([slicing_group.center_xy[0], slicing_group.center_xy[1], 0])
|
||||
slicing_group.offset(offset)
|
||||
|
||||
# Prepare overrides for generate_conf_writer
|
||||
writer_overrides = {k: {'value': v} for k, v in overrides.items()}
|
||||
# Check for wipe_tower_position override
|
||||
if overrides and "wipe_tower_position" in overrides:
|
||||
wipe_tower_pos = overrides["wipe_tower_position"]
|
||||
try:
|
||||
wx = float(wipe_tower_pos[0])
|
||||
wy = float(wipe_tower_pos[1])
|
||||
slicing_group.wipe_tower_xy = np.array([wx, wy])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Prepare overrides for generate_conf_writer (excluding wipe_tower_position)
|
||||
writer_overrides = {
|
||||
k: {'value': v} for k, v in (overrides or {}).items() if k != "wipe_tower_position"
|
||||
}
|
||||
|
||||
# Generate the ConfigWriter
|
||||
from ....infra.prusaslicer_profiles import ConfigWriter
|
||||
|
|
|
|||
|
|
@ -481,6 +481,7 @@ class SLICENODE_MT_DataInput(Menu):
|
|||
layout.operator("node.add_node", text="Object").type = "SLICENODE_InputObject"
|
||||
layout.operator("node.add_node", text="Collection Objects").type = "SLICENODE_CollectionObjects"
|
||||
layout.operator("node.add_node", text="Settings").type = "SLICENODE_InputSettings"
|
||||
layout.operator("node.add_node", text="Wipe Tower").type = "SLICENODE_WipeTower"
|
||||
layout.operator("node.add_node", text="PrusaSlicer Config").type = "SLICENODE_PrusaslicerConf"
|
||||
layout.separator()
|
||||
layout.operator("node.add_node", text="Printer").type = "SLICENODE_InputPrinter"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ
|
|||
- **Float**: Single float input.
|
||||
- **Integer**: Single integer input.
|
||||
- **String**: Single string/path input.
|
||||
- **Rotation**: Input for Euler rotation vectors.
|
||||
- **Settings**: Read-only/write configuration settings dictionary.
|
||||
- **Wipe Tower**: Configures the Wipe Tower printbed position (`wipe_tower_position`) passed to 3MF metadata (`Prusa_Slicer_wipe_tower_information.xml`) without injecting into profile INI settings.
|
||||
- **Position X**: X coordinate of the wipe tower on the printbed.
|
||||
- **Position Y**: Y coordinate of the wipe tower on the printbed.
|
||||
- **Printer Intrinsics**: Configures bed size, extrusion/travel rates, retraction, and Z-lift.
|
||||
- **Gcode Block**: Manual text area for entering raw G-code.
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ class ConfigWriter:
|
|||
def write_ini_3mf(self, config_local_path: Path):
|
||||
with open(config_local_path, 'w') as file:
|
||||
for key, val in dict(sorted(self.config_dict.items())).items():
|
||||
if key == "wipe_tower_position":
|
||||
continue
|
||||
file.write(f"; {key} = {val}\n")
|
||||
|
||||
def get(self, key: str, default: str = "") -> str | list[str]:
|
||||
|
|
|
|||
44
tests/test_wipe_tower.py
Normal file
44
tests/test_wipe_tower.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import unittest
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.append(str(PROJECT_ROOT))
|
||||
|
||||
from NodeSlicer.core.nodeslicer.nodes.inputs import SLICENODE_WipeTower
|
||||
from NodeSlicer.infra.prusaslicer_profiles.models import ConfigWriter
|
||||
from NodeSlicer.infra.blender.blender_mesh_capture import SlicingGroup
|
||||
|
||||
class TestWipeTowerNode(unittest.TestCase):
|
||||
def test_wipe_tower_node_output(self):
|
||||
node = SLICENODE_WipeTower()
|
||||
node.resolve = lambda key: 180.5 if key == "Position X" else 135.2
|
||||
|
||||
res = node.evaluate_output("Settings")
|
||||
self.assertIn("wipe_tower_position", res)
|
||||
self.assertEqual(res["wipe_tower_position"], (180.5, 135.2))
|
||||
|
||||
def test_config_writer_excludes_wipe_tower_position(self):
|
||||
conf = {
|
||||
"layer_height": "0.2",
|
||||
"wipe_tower_position": "(180.5, 135.2)",
|
||||
"nozzle_diameter": "0.4"
|
||||
}
|
||||
writer = ConfigWriter(conf)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
ini_path = Path(temp_dir) / "Slic3r_PE.config"
|
||||
writer.write_ini_3mf(ini_path)
|
||||
|
||||
with open(ini_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
self.assertIn("; layer_height = 0.2", content)
|
||||
self.assertIn("; nozzle_diameter = 0.4", content)
|
||||
self.assertNotIn("wipe_tower_position", content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Reference in a new issue