This commit is contained in:
Nicolas Predella 2026-05-21 11:28:29 +03:00
parent db343e8396
commit c9dbf0eb1d

View file

@ -4,7 +4,6 @@ import subprocess
import tempfile import tempfile
import shlex import shlex
import shutil import shutil
from math import floor
from pathlib import Path from pathlib import Path
from typing import Any, TypeAlias from typing import Any, TypeAlias
import bpy import bpy
@ -19,11 +18,8 @@ from ..slice import Pather, slice_at_range, SlicePaths
from ...infra.blender.blender_mesh_capture import SlicingCollection, SlicingObject, capture_mesh_edges, SlicingGroup from ...infra.blender.blender_mesh_capture import SlicingCollection, SlicingObject, capture_mesh_edges, SlicingGroup
from ...infra.blender.blender_bridge import get_all_children from ...infra.blender.blender_bridge import get_all_children
from ...infra.blender.blender_slice_nodes_generic import SliceNode, resolve_input from ...infra.blender.blender_slice_nodes_generic import SliceNode, resolve_input
from ...infra.prusaslicer_profiles import ConfigWriter
from ...infra.prusaslicer_profiles.cache import LocalCache from ...infra.prusaslicer_profiles.cache import LocalCache
from ...infra.prusaslicer_profiles.models import Profile from ...infra.prusaslicer_profiles.models import Profile
from ...infra.networking.airtable_backend import AirtableRecord
from ...infra._3mf_writer._3mf import prepare_3mf
class SliceIntrinsics: class SliceIntrinsics:
bed_x: float bed_x: float
@ -348,8 +344,9 @@ class SLICENODE_GetPaths(SliceNode):
self.add_output("SLICESOCK_Paths") self.add_output("SLICESOCK_Paths")
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
obj: Any = resolve_input(self.inputs.get("Object")) obj: Any = self.resolve("Object")
from ...infra.blender.blender_mesh_capture import capture_mesh_edges
so: list[np.ndarray] = capture_mesh_edges(obj) so: list[np.ndarray] = capture_mesh_edges(obj)
return SlicePaths(so) return SlicePaths(so)
@ -366,8 +363,8 @@ class SLICENODE_SliceShells(SliceNode):
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
if socket_name != "Paths": return SlicePaths() if socket_name != "Paths": return SlicePaths()
slicing_collection: SlicingCollection | None = resolve_input(self.inputs.get("Mesh")) slicing_collection: SlicingCollection | None = self.resolve("Mesh")
layer_height: float = resolve_input(self.inputs.get("Layer Height")) layer_height: float = self.resolve("Layer Height")
if not slicing_collection: return SlicePaths() if not slicing_collection: return SlicePaths()
@ -378,6 +375,7 @@ class SLICENODE_SliceShells(SliceNode):
merged_object.mesh = np.concatenate([merged_object.mesh, obj.mesh], axis=0) merged_object.mesh = np.concatenate([merged_object.mesh, obj.mesh], axis=0)
min = merged_object.mesh[:,0:3,2:3].min() min = merged_object.mesh[:,0:3,2:3].min()
from math import floor
min = floor(min / layer_height) * layer_height min = floor(min / layer_height) * layer_height
max = merged_object.mesh[:,0:3,2:3].max() max = merged_object.mesh[:,0:3,2:3].max()
@ -415,9 +413,9 @@ class SLICENODE_BufferShells(SliceNode):
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
if socket_name != "Paths": return SlicePaths() if socket_name != "Paths": return SlicePaths()
paths: SlicePaths = resolve_input(self.inputs.get("Paths")) paths: SlicePaths = self.resolve("Paths")
distance: float = resolve_input(self.inputs.get("Distance")) or 0.0 distance: float = self.resolve("Distance") or 0.0
rotation: Any = resolve_input(self.inputs.get("Rotation")) or (0.0, 0.0, 0.0) rotation: Any = self.resolve("Rotation") or (0.0, 0.0, 0.0)
if not paths: return SlicePaths() if not paths: return SlicePaths()
@ -470,11 +468,11 @@ class SLICENODE_BufferShellsMultiple(SliceNode):
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
if socket_name != "Paths": return SlicePaths() if socket_name != "Paths": return SlicePaths()
paths: SlicePaths = resolve_input(self.inputs.get("Paths")) paths: SlicePaths = self.resolve("Paths")
base_distance: float = resolve_input(self.inputs.get("Distance")) or 0.0 base_distance: float = self.resolve("Distance") or 0.0
width_step: float = resolve_input(self.inputs.get("Width Step")) or 0.0 width_step: float = self.resolve("Width Step") or 0.0
count: int = resolve_input(self.inputs.get("Count")) or 1 count: int = self.resolve("Count") or 1
rotation: Any = resolve_input(self.inputs.get("Rotation")) or (0.0, 0.0, 0.0) rotation: Any = self.resolve("Rotation") or (0.0, 0.0, 0.0)
if not paths: return SlicePaths() if not paths: return SlicePaths()
@ -547,10 +545,10 @@ class SLICENODE_Infill(SliceNode):
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
if socket_name != "Paths": return SlicePaths() if socket_name != "Paths": return SlicePaths()
paths: SlicePaths = resolve_input(self.inputs.get("Paths")) paths: SlicePaths = self.resolve("Paths")
density: float = resolve_input(self.inputs.get("Density")) or 2.0 density: float = self.resolve("Density") or 2.0
infill_rotation: float = resolve_input(self.inputs.get("Infill Rotation")) or 0.0 infill_rotation: float = self.resolve("Infill Rotation") or 0.0
rotation: Any = resolve_input(self.inputs.get("Layer Rotation")) or (0.0, 0.0, 0.0) rotation: Any = self.resolve("Layer Rotation") or (0.0, 0.0, 0.0)
if not paths: return SlicePaths() if not paths: return SlicePaths()
@ -596,8 +594,8 @@ class SLICENODE_PathsToGcode(SliceNode):
layout.prop(self, "use_header") layout.prop(self, "use_header")
def evaluate_output(self, socket_name: str, **_kwargs) -> str: def evaluate_output(self, socket_name: str, **_kwargs) -> str:
paths: list[np.ndarray] = resolve_input(self.inputs.get("Paths")) paths: list[np.ndarray] = self.resolve("Paths")
intrinsics: SliceIntrinsics | None = resolve_input(self.inputs.get("Intrinsics")) intrinsics: SliceIntrinsics | None = self.resolve("Intrinsics")
pather: Pather = Pather(paths) pather: Pather = Pather(paths)
if intrinsics: if intrinsics:
@ -874,7 +872,7 @@ class SLICENODE_LoadGcode(SliceNode):
self.add_output("SLICESOCK_GcodeRo") self.add_output("SLICESOCK_GcodeRo")
def evaluate_output(self, socket_name: str, **_kwargs) -> str: def evaluate_output(self, socket_name: str, **_kwargs) -> str:
path = resolve_input(self.inputs.get("Filepath")) path = self.resolve("Filepath")
if not path: return "" if not path: return ""
abs_path = bpy.path.abspath(path) abs_path = bpy.path.abspath(path)
if os.path.exists(abs_path): if os.path.exists(abs_path):
@ -978,7 +976,7 @@ class SLICENODE_GcodeToFile(ExecSliceNode):
gcodes: list[str] = [gc for gc in self.resolve_multi_input('Gcode') if gc] gcodes: list[str] = [gc for gc in self.resolve_multi_input('Gcode') if gc]
folder: str = resolve_input(self.inputs.get("Folder")) folder: str = self.resolve("Folder")
if not gcodes: 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'
@ -1034,7 +1032,7 @@ class SLICENODE_GcodeToTextBlock(ExecSliceNode):
layout.prop(self, "text_block_name") layout.prop(self, "text_block_name")
def evaluate(self, operator=None): def evaluate(self, operator=None):
gcode: str = resolve_input(self.inputs.get("Gcode")) gcode: str = self.resolve("Gcode")
if not gcode: if not gcode:
return 'ok' return 'ok'
@ -1075,8 +1073,8 @@ class SLICENODE_StartPrint(ExecSliceNode):
import tempfile import tempfile
from pathlib import Path from pathlib import Path
gcode: str = resolve_input(self.inputs.get("Gcode")) gcode: str = self.resolve("Gcode")
config: PrinterConfig | None = resolve_input(self.inputs.get("Printer")) config: PrinterConfig | None = self.resolve("Printer")
if not gcode: if not gcode:
if operator: operator.report({'ERROR'}, "No Gcode to send") if operator: operator.report({'ERROR'}, "No Gcode to send")
@ -1327,10 +1325,10 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
def evaluate(self, operator=None): def evaluate(self, operator=None):
self.gcode_result = "" self.gcode_result = ""
mesh_data: SlicingCollection | SlicingGroup = resolve_input(self.inputs.get("Mesh")) mesh_data: SlicingCollection | SlicingGroup = self.resolve("Mesh")
conf_data = resolve_input(self.inputs.get("Config")) conf_data = self.resolve("Config")
overrides = resolve_input(self.inputs.get("Overrides")) overrides = self.resolve("Overrides")
if not mesh_data: if not mesh_data:
if operator: operator.report({'ERROR'}, "No Mesh data connected") if operator: operator.report({'ERROR'}, "No Mesh data connected")
@ -1376,6 +1374,7 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
writer_overrides = {k: {'value': v} for k, v in overrides.items()} writer_overrides = {k: {'value': v} for k, v in overrides.items()}
# Generate the ConfigWriter # Generate the ConfigWriter
from ...infra.prusaslicer_profiles import ConfigWriter
conf_writer: ConfigWriter = cache.generate_conf_writer( conf_writer: ConfigWriter = cache.generate_conf_writer(
printer_id, printer_id,
filament_ids, filament_ids,
@ -1391,6 +1390,7 @@ class SLICENODE_PrusaslicerSlice(SliceNode):
output_path = temp_dir / f"tmp.3mf" output_path = temp_dir / f"tmp.3mf"
try: try:
from ...infra._3mf_writer._3mf import prepare_3mf
prepare_3mf(output_path, slicing_group, conf_writer, z_gcodes=[]) prepare_3mf(output_path, slicing_group, conf_writer, z_gcodes=[])
# Actual slicing # Actual slicing
@ -1630,5 +1630,5 @@ class SLICENODE_Test(ExecSliceNode):
self.add_input("SLICESOCK_Gcode") self.add_input("SLICESOCK_Gcode")
def evaluate(self, operator=None): def evaluate(self, operator=None):
_ = resolve_input(self.inputs.get("Gcode")) _ = self.resolve("Gcode")
return 'ok' return 'ok'