multi gcode for file save
This commit is contained in:
parent
f532fada23
commit
fdf49ef611
4 changed files with 107 additions and 66 deletions
|
|
@ -524,7 +524,6 @@ class SLICENODE_Infill(SliceNode):
|
||||||
update=notify_tree_changed
|
update=notify_tree_changed
|
||||||
) # pyright: ignore[reportInvalidTypeForm]
|
) # pyright: ignore[reportInvalidTypeForm]
|
||||||
|
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_input("SLICESOCK_Paths")
|
self.add_input("SLICESOCK_Paths")
|
||||||
self.add_input("SLICESOCK_Float", "Density").value = 2.0
|
self.add_input("SLICESOCK_Float", "Density").value = 2.0
|
||||||
|
|
@ -680,26 +679,7 @@ class SliceNodeJoiner(SliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_output(self.output_socket_type or self.socket_type)
|
self.add_output(self.output_socket_type or self.socket_type)
|
||||||
|
self.add_multi_input(self.socket_type)
|
||||||
def update(self) -> None:
|
|
||||||
def is_active(input) -> bool:
|
|
||||||
a: bool = input.is_linked or getattr(input, 'value', False)
|
|
||||||
b: bool = getattr(self.inputs[-1], 'value', False)
|
|
||||||
return a or b
|
|
||||||
|
|
||||||
if len(self.inputs) == 0:
|
|
||||||
self.add_input(self.socket_type)
|
|
||||||
return
|
|
||||||
|
|
||||||
if is_active(self.inputs[-1]):
|
|
||||||
self.add_input(self.socket_type)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if len(self.inputs) < 2: break
|
|
||||||
if not is_active(self.inputs[-1]) and not is_active(self.inputs[-2]):
|
|
||||||
self.inputs.remove(self.inputs[-2])
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_JoinGcode(SliceNodeJoiner):
|
class SLICENODE_JoinGcode(SliceNodeJoiner):
|
||||||
|
|
@ -975,8 +955,8 @@ class SLICENODE_GcodeToFile(ExecSliceNode):
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self._init_exec()
|
self._init_exec()
|
||||||
self.add_input("SLICESOCK_Gcode")
|
self.add_input("SLICESOCK_DirPath", "Folder")
|
||||||
self.add_input("SLICESOCK_FilePath", "Filename")
|
self.add_multi_input("SLICESOCK_Gcode", "Gcode")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
super().draw_buttons(context, layout)
|
super().draw_buttons(context, layout)
|
||||||
|
|
@ -985,42 +965,57 @@ class SLICENODE_GcodeToFile(ExecSliceNode):
|
||||||
from ...infra.networking.bgcode import convert_to_bgcode
|
from ...infra.networking.bgcode import convert_to_bgcode
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
gcode: str = resolve_input(self.inputs.get("Gcode"))
|
gcodes: str = self.resolve_multi_input('Gcode')
|
||||||
filename: str = resolve_input(self.inputs.get("Filename"))
|
|
||||||
if not gcode:
|
folder: str = resolve_input(self.inputs.get("Folder"))
|
||||||
|
if not gcodes:
|
||||||
if operator: operator.report({'ERROR'}, "No Gcode to save")
|
if operator: operator.report({'ERROR'}, "No Gcode to save")
|
||||||
return 'error'
|
return 'error'
|
||||||
if not filename:
|
if not folder:
|
||||||
if operator: operator.report({'ERROR'}, "No Filename provided")
|
if operator: operator.report({'ERROR'}, "No Folder provided")
|
||||||
return 'error'
|
return 'error'
|
||||||
|
|
||||||
# User's requested logic: .split('.')[0]
|
for i, gc in enumerate(gcodes):
|
||||||
filepath_abs = bpy.path.abspath(filename)
|
|
||||||
dirname = os.path.dirname(filepath_abs)
|
import re, json
|
||||||
basename = os.path.basename(filepath_abs)
|
parts_match = re.search(r"; objects_info = (.*)", gc)
|
||||||
name_part = basename.split('.')[0]
|
name: str = ""
|
||||||
os.makedirs(dirname, exist_ok=True)
|
if parts_match:
|
||||||
|
gcode_info = json.loads(parts_match.group(1))
|
||||||
# Detect binary trigger in gcode comments
|
names: list[str] = [str(v['name']) for v in gcode_info['objects']]
|
||||||
# Looking for "; binary_gcode = 1"
|
from collections import Counter
|
||||||
is_binary = "; binary_gcode = 1" in gcode
|
counts = Counter(names)
|
||||||
|
name = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items())
|
||||||
try:
|
|
||||||
if is_binary:
|
|
||||||
filepath = os.path.join(dirname, name_part + ".bgcode")
|
|
||||||
content = convert_to_bgcode(gcode, name_part, True)
|
|
||||||
with open(filepath, "wb") as f:
|
|
||||||
f.write(content)
|
|
||||||
if operator: operator.report({'INFO'}, f"Binary G-code generated at {filepath}")
|
|
||||||
else:
|
else:
|
||||||
filepath = os.path.join(dirname, name_part + ".gcode")
|
name = 'object_'+str(i)
|
||||||
with open(filepath, "w", encoding="utf-8", newline="\n") as f:
|
|
||||||
f.write(gcode)
|
# User's requested logic: .split('.')[0]
|
||||||
if operator: operator.report({'INFO'}, f"G-code saved to {filepath}")
|
folder_path = Path(bpy.path.abspath(f"//{folder}"))
|
||||||
return 'ok'
|
filepath_abs = folder_path / name
|
||||||
except Exception as e:
|
dirname = os.path.dirname(filepath_abs)
|
||||||
if operator: operator.report({'ERROR'}, str(e))
|
basename = os.path.basename(filepath_abs)
|
||||||
return 'error'
|
name_part = basename.split('.')[0]
|
||||||
|
os.makedirs(dirname, exist_ok=True)
|
||||||
|
|
||||||
|
# Detect binary trigger in gcode comments
|
||||||
|
is_binary = "; binary_gcode = 1" in gc
|
||||||
|
|
||||||
|
try:
|
||||||
|
if is_binary:
|
||||||
|
filepath = os.path.join(dirname, name_part + ".bgcode")
|
||||||
|
content = convert_to_bgcode(gc, name_part, True)
|
||||||
|
with open(filepath, "wb") as f:
|
||||||
|
f.write(content)
|
||||||
|
if operator: operator.report({'INFO'}, f"Binary G-code generated at {filepath}")
|
||||||
|
else:
|
||||||
|
filepath = os.path.join(dirname, name_part + ".gcode")
|
||||||
|
with open(filepath, "w", encoding="utf-8", newline="\n") as f:
|
||||||
|
f.write(gc)
|
||||||
|
if operator: operator.report({'INFO'}, f"G-code saved to {filepath}")
|
||||||
|
return 'ok'
|
||||||
|
except Exception as e:
|
||||||
|
if operator: operator.report({'ERROR'}, str(e))
|
||||||
|
return 'error'
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_GcodeToTextBlock(ExecSliceNode):
|
class SLICENODE_GcodeToTextBlock(ExecSliceNode):
|
||||||
|
|
@ -1328,6 +1323,7 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
|
||||||
self.add_output("SLICESOCK_GcodeRo")
|
self.add_output("SLICESOCK_GcodeRo")
|
||||||
|
|
||||||
def draw_buttons(self, context, layout):
|
def draw_buttons(self, context, layout):
|
||||||
|
assert layout
|
||||||
layout.prop(self, "prusaslicer_executable")
|
layout.prop(self, "prusaslicer_executable")
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **kwargs) -> Any:
|
def evaluate_output(self, socket_name: str, **kwargs) -> Any:
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,13 @@ class SLICESOCK_FilePath(InputNodeSocket):
|
||||||
value: NfStringProperty(name="Filepath", default="", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm]
|
value: NfStringProperty(name="Filepath", default="", subtype='FILE_PATH') # pyright: ignore[reportInvalidTypeForm]
|
||||||
bl_color = (0.4, 0.7, 1.0, 1.0)
|
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
|
@register_class
|
||||||
class SLICESOCK_VectorRotation_Common(InputNodeSocket):
|
class SLICESOCK_VectorRotation_Common(InputNodeSocket):
|
||||||
bl_label = "VectorRotation"
|
bl_label = "VectorRotation"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from dataclasses import field
|
||||||
import typing
|
import typing
|
||||||
import bpy
|
import bpy
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
@ -23,24 +24,61 @@ class SliceNodeSocket(NodeSocket, AutoBlIdName):
|
||||||
def draw_color(self, context, node) -> tuple[float, float, float, float]: # pyright: ignore[reportIncompatibleMethodOverride]
|
def draw_color(self, context, node) -> tuple[float, float, float, float]: # pyright: ignore[reportIncompatibleMethodOverride]
|
||||||
return self.bl_color
|
return self.bl_color
|
||||||
|
|
||||||
|
from ...registry import get_label_by_name
|
||||||
class SliceNode(Node, AutoBlIdName):
|
class SliceNode(Node, AutoBlIdName):
|
||||||
|
def update(self) -> None:
|
||||||
|
self._update_multi_inputs()
|
||||||
|
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any: ...
|
def evaluate_output(self, socket_name: str, **_kwargs) -> Any: ...
|
||||||
|
|
||||||
def resolve(self, fieldname) -> Any:
|
def resolve(self, fieldname) -> Any:
|
||||||
return resolve_input(self.inputs.get(fieldname))
|
return resolve_input(self.inputs.get(fieldname))
|
||||||
|
|
||||||
def add_output(self, bl_idname: str, name: str = ""):
|
def add_output(self, bl_idname: str, name: str = "", identifier: str = ""):
|
||||||
from ...registry import get_label_by_name
|
eval_name = name or get_label_by_name(bl_idname)
|
||||||
return self.outputs.new(bl_idname, name or get_label_by_name(bl_idname))
|
return self.outputs.new(bl_idname, eval_name, identifier=identifier or eval_name)
|
||||||
|
|
||||||
def add_input(self, bl_idname: str, name: str = "", multi: bool = False):
|
def add_input(self, bl_idname: str, name: str = "", identifier: str = ""):
|
||||||
from ...registry import get_label_by_name
|
eval_name = name or get_label_by_name(bl_idname)
|
||||||
sock = self.inputs.new(bl_idname, name or get_label_by_name(bl_idname))
|
return self.inputs.new(bl_idname, eval_name, identifier=identifier or eval_name)
|
||||||
if multi:
|
|
||||||
sock.link_limit = 0
|
|
||||||
sock.display_shape = 'LIST'
|
|
||||||
return sock
|
|
||||||
|
|
||||||
|
def add_multi_input(self, bl_idname: str, name: str = "") -> None:
|
||||||
|
if not self.get('multi_sockets'): self['multi_sockets'] = {}
|
||||||
|
if not self['multi_sockets'].get(name): self['multi_sockets'][name] = {'bl_idname': bl_idname, 'name': name or get_label_by_name(bl_idname)}
|
||||||
|
self._update_multi_inputs()
|
||||||
|
|
||||||
|
def resolve_multi_input(self, fieldname) -> Any:
|
||||||
|
inputs = [i for i in list(self.inputs) if i.name == fieldname]
|
||||||
|
return [resolve_input(r) for r in inputs]
|
||||||
|
|
||||||
|
def _update_multi_inputs(self):
|
||||||
|
if not self.get('multi_sockets'): return
|
||||||
|
for k, ms in self['multi_sockets'].items():
|
||||||
|
def is_active(input) -> bool:
|
||||||
|
a: bool = input.is_linked or getattr(input, 'value', False)
|
||||||
|
b: bool = getattr(inputs()[-1], 'value', False)
|
||||||
|
return a or b
|
||||||
|
|
||||||
|
def inputs():
|
||||||
|
return [i for i in list(self.inputs) if i.name == ms['name']]
|
||||||
|
|
||||||
|
socket_type = ms['bl_idname']
|
||||||
|
name = ms['name']
|
||||||
|
|
||||||
|
if len(inputs()) == 0:
|
||||||
|
self.add_input(socket_type, name, name+str(len(inputs())))
|
||||||
|
return
|
||||||
|
|
||||||
|
if is_active(inputs()[-1]):
|
||||||
|
self.add_input(socket_type, name, name+str(len(inputs())))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if len(inputs()) < 2: break
|
||||||
|
if not is_active(inputs()[-1]) and not is_active(inputs()[-2]):
|
||||||
|
self.inputs.remove(inputs()[-1])
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
|
||||||
## Evaluation helpers
|
## Evaluation helpers
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ def register_class(cls: type) -> type:
|
||||||
def get() -> list[type]:
|
def get() -> list[type]:
|
||||||
return list(_bpy_class_registry)
|
return list(_bpy_class_registry)
|
||||||
|
|
||||||
def get_label_by_name(name: str) -> str | None:
|
def get_label_by_name(name: str) -> str:
|
||||||
for cls in get():
|
for cls in get():
|
||||||
if cls.__name__ == name:
|
if cls.__name__ == name:
|
||||||
return getattr(cls, "bl_label", "No Label")
|
return getattr(cls, "bl_label", "No Label")
|
||||||
return None
|
return ''
|
||||||
|
|
||||||
def get_class_by_name(name: str) -> type | None:
|
def get_class_by_name(name: str) -> type | None:
|
||||||
for cls in get():
|
for cls in get():
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue