refac
This commit is contained in:
parent
e540c85e59
commit
2f09772928
5 changed files with 256 additions and 404 deletions
|
|
@ -32,12 +32,6 @@ class SLICENODE_PathsToGcode(SliceNode):
|
|||
layout.prop(self, "use_header")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
paths: Any = self.resolve("Paths")
|
||||
intrinsics: Any = self.resolve("Intrinsics")
|
||||
|
||||
if not paths:
|
||||
return ""
|
||||
|
||||
def _paths_to_gcode(p: Any, intr: Any) -> str:
|
||||
if not p:
|
||||
return ""
|
||||
|
|
@ -59,11 +53,12 @@ class SLICENODE_PathsToGcode(SliceNode):
|
|||
|
||||
return pather.to_gcode(include_header=self.use_header)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_paths_to_gcode,
|
||||
paths,
|
||||
intrinsics,
|
||||
input_names=["Intrinsics"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Intrinsics"],
|
||||
socket_name=socket_name,
|
||||
default="",
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -88,10 +83,6 @@ class SLICENODE_AssignMetadata(SliceNode):
|
|||
self.add_output("NodeSocketString", "Gcode")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
name = self.resolve("Name")
|
||||
filament = self.resolve("Filament Type")
|
||||
printer = self.resolve("Printer Model")
|
||||
|
||||
def _meta_one(n: Any, f: Any, p: Any) -> str:
|
||||
lines = []
|
||||
if n:
|
||||
|
|
@ -102,12 +93,12 @@ class SLICENODE_AssignMetadata(SliceNode):
|
|||
lines.append(f'; printer_model = {p}')
|
||||
return '\n'.join(lines)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_meta_one,
|
||||
name,
|
||||
filament,
|
||||
printer,
|
||||
input_names=["Filament Type", "Printer Model"]
|
||||
main_socket="Name",
|
||||
secondary_sockets=["Filament Type", "Printer Model"],
|
||||
socket_name=socket_name,
|
||||
default="",
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -123,15 +114,6 @@ class SLICENODE_GcodeRegexReplace(SliceNode):
|
|||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
import re
|
||||
gcode = self.resolve("Gcode")
|
||||
find = self.resolve("Find") or ""
|
||||
replace = self.resolve("Replace") or ""
|
||||
|
||||
if not gcode:
|
||||
return ""
|
||||
|
||||
if not find:
|
||||
return gcode
|
||||
|
||||
def _replace_one(gc: Any, f_pattern: Any, r_pattern: Any) -> str:
|
||||
gc_str = str(gc or "")
|
||||
|
|
@ -153,12 +135,12 @@ class SLICENODE_GcodeRegexReplace(SliceNode):
|
|||
print(f"Regex error in node {self.name}: {e}")
|
||||
return gc_str
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_replace_one,
|
||||
gcode,
|
||||
find,
|
||||
replace,
|
||||
input_names=["Find", "Replace"]
|
||||
main_socket="Gcode",
|
||||
secondary_sockets=["Find", "Replace"],
|
||||
socket_name=socket_name,
|
||||
default="",
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
|
|||
|
|
@ -129,12 +129,6 @@ class SLICENODE_SeparateMesh(SliceNode):
|
|||
layout.prop(self, "comparison", text="")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
coll: Any = self.resolve("Mesh")
|
||||
attr_name: Any = self.resolve("Attribute")
|
||||
value: Any = self.resolve("Value")
|
||||
|
||||
if not coll: return None
|
||||
|
||||
def _split_coll(sc: SlicingCollection, a_name: str, comp_val: float) -> tuple[SlicingCollection, SlicingCollection]:
|
||||
res_selection = SlicingCollection([], sc.name + "_selection")
|
||||
res_inverted = SlicingCollection([], sc.name + "_inverted")
|
||||
|
|
@ -191,29 +185,13 @@ class SLICENODE_SeparateMesh(SliceNode):
|
|||
res_inverted.objects.append(so_inv)
|
||||
return res_selection, res_inverted
|
||||
|
||||
is_list, items, (attr_list, val_list) = prepare_indexed_inputs(
|
||||
coll,
|
||||
attr_name,
|
||||
value,
|
||||
input_names=["Attribute", "Value"]
|
||||
return self.eval_multivalue(
|
||||
lambda sc, a, v: _split_coll(sc, str(a or ""), float(v if v is not None else 0.0)),
|
||||
main_socket="Mesh",
|
||||
secondary_sockets=["Attribute", "Value"],
|
||||
socket_name=socket_name,
|
||||
)
|
||||
|
||||
if is_list:
|
||||
results = [
|
||||
_split_coll(c, str(a or ""), float(v if v is not None else 0.0))
|
||||
for c, a, v in zip(items, attr_list, val_list)
|
||||
]
|
||||
return [r[0] for r in results] if socket_name == "Selection" else [r[1] for r in results]
|
||||
|
||||
res_selection, res_inverted = _split_coll(
|
||||
items[0],
|
||||
str(attr_list[0] or ""),
|
||||
float(val_list[0] if val_list[0] is not None else 0.0)
|
||||
)
|
||||
if socket_name == "Selection":
|
||||
return res_selection
|
||||
return res_inverted
|
||||
|
||||
@register_class
|
||||
class SLICENODE_SeparateMeshByRange(SliceNode):
|
||||
bl_label = "Separate Mesh by Range"
|
||||
|
|
@ -268,12 +246,6 @@ class SLICENODE_SeparateMeshByRange(SliceNode):
|
|||
col.prop(self, "end_val")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
coll: Any = self.resolve("Mesh")
|
||||
attr_name: str = self.resolve("Attribute")
|
||||
|
||||
if not coll: return None
|
||||
|
||||
# Check if we can parse the socket name as an integer
|
||||
try:
|
||||
target_val = int(socket_name)
|
||||
except ValueError:
|
||||
|
|
@ -317,7 +289,7 @@ class SLICENODE_SeparateMeshByRange(SliceNode):
|
|||
res.objects.append(so_new)
|
||||
return res
|
||||
|
||||
return eval_element_wise_multivalue(_split_coll, coll, attr_name, input_names=["Attribute"])
|
||||
return self.eval_multivalue(_split_coll, "Mesh", ["Attribute"])
|
||||
|
||||
@register_class
|
||||
class SLICENODE_ArrangeMesh(SliceNode):
|
||||
|
|
@ -583,37 +555,17 @@ 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",):
|
||||
return None
|
||||
|
||||
geom_data: Any = self.resolve("Geometry")
|
||||
if geom_data is None:
|
||||
return None
|
||||
|
||||
translation = self.resolve("Translation")
|
||||
if translation is None:
|
||||
translation = (0.0, 0.0, 0.0)
|
||||
rotation = self.resolve("Rotation")
|
||||
if rotation is None:
|
||||
rotation = (0.0, 0.0, 0.0)
|
||||
scale = self.resolve("Scale")
|
||||
if scale is None:
|
||||
scale = (1.0, 1.0, 1.0)
|
||||
|
||||
def _transform_one(g: Any, t: Any, r: Any, s: Any) -> Any:
|
||||
t_arr = np.array(t, dtype=np.float64)
|
||||
r_arr = np.array(r, dtype=np.float64)
|
||||
s_arr = np.array(s, dtype=np.float64)
|
||||
t_arr = np.array(t if t is not None else (0.0, 0.0, 0.0), dtype=np.float64)
|
||||
r_arr = np.array(r if r is not None else (0.0, 0.0, 0.0), dtype=np.float64)
|
||||
s_arr = np.array(s if s is not None else (1.0, 1.0, 1.0), dtype=np.float64)
|
||||
return _transform_geometry_item(g, t_arr, r_arr, s_arr)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_transform_one,
|
||||
geom_data,
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
is_vector=[True, True, True],
|
||||
input_names=["Translation", "Rotation", "Scale"]
|
||||
main_socket="Geometry",
|
||||
secondary_sockets=["Translation", "Rotation", "Scale"],
|
||||
socket_name=socket_name,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -629,33 +581,15 @@ class SLICENODE_GetTransforms(SliceNode):
|
|||
self.add_output("NodeSocketVector", "Scale")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
geom_data: Any = self.resolve("Geometry")
|
||||
if geom_data is None:
|
||||
if socket_name == "Scale":
|
||||
return (1.0, 1.0, 1.0)
|
||||
return (0.0, 0.0, 0.0)
|
||||
|
||||
from ....core.slice import extract_geometry_transforms
|
||||
from ....infra.blender.list_utils import is_list_data
|
||||
|
||||
if is_list_data(geom_data):
|
||||
results = [extract_geometry_transforms(g) for g in geom_data]
|
||||
if socket_name == "Translation":
|
||||
return [r[0] for r in results]
|
||||
elif socket_name == "Rotation":
|
||||
return [r[1] for r in results]
|
||||
elif socket_name == "Scale":
|
||||
return [r[2] for r in results]
|
||||
return (0.0, 0.0, 0.0)
|
||||
|
||||
t, r, s = extract_geometry_transforms(geom_data)
|
||||
if socket_name == "Translation":
|
||||
return t
|
||||
elif socket_name == "Rotation":
|
||||
return r
|
||||
elif socket_name == "Scale":
|
||||
return s
|
||||
return (0.0, 0.0, 0.0)
|
||||
default = (1.0, 1.0, 1.0) if socket_name == "Scale" else (0.0, 0.0, 0.0)
|
||||
return self.eval_multivalue(
|
||||
extract_geometry_transforms,
|
||||
main_socket="Geometry",
|
||||
secondary_sockets=[],
|
||||
socket_name=socket_name,
|
||||
default=default,
|
||||
)
|
||||
|
||||
|
||||
@register_class
|
||||
|
|
@ -670,19 +604,12 @@ class SLICENODE_DropGeometry(SliceNode):
|
|||
self.add_output("NodeSocketVector", "Translation")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
mesh_data: Any = self.resolve("Mesh")
|
||||
if not mesh_data:
|
||||
return None
|
||||
|
||||
z = self.resolve('Z')
|
||||
if z is None:
|
||||
z = 0.0
|
||||
|
||||
def _drop_item(item: Any, target_z: float) -> tuple[Any, tuple[float, float, float]]:
|
||||
def _drop_item(item: Any, target_z: Any) -> tuple[Any, tuple[float, float, float]]:
|
||||
if not item or getattr(item, "min_z", None) is None:
|
||||
return item, (0.0, 0.0, 0.0)
|
||||
z_float = float(target_z if target_z is not None else 0.0)
|
||||
min_z = round(item.min_z, 6)
|
||||
offset_z = float(target_z - min_z)
|
||||
offset_z = float(z_float - min_z)
|
||||
if isinstance(item, SlicingGroup):
|
||||
copied_data = _copy_slicing_group(item)
|
||||
else:
|
||||
|
|
@ -700,23 +627,14 @@ class SLICENODE_DropGeometry(SliceNode):
|
|||
so.transforms = update_transform(getattr(so, "transforms", []), t_arr=drop_vec)
|
||||
return copied_data, (0.0, 0.0, offset_z)
|
||||
|
||||
is_list, items, (z_vals,) = prepare_indexed_inputs(
|
||||
mesh_data,
|
||||
z,
|
||||
input_names=["Z"]
|
||||
return self.eval_multivalue(
|
||||
_drop_item,
|
||||
main_socket="Mesh",
|
||||
secondary_sockets=["Z"],
|
||||
socket_name=socket_name,
|
||||
default=(0.0, 0.0, 0.0) if socket_name == "Translation" else None
|
||||
)
|
||||
|
||||
if is_list:
|
||||
results = [_drop_item(m, float(z_i if z_i is not None else 0.0)) for m, z_i in zip(items, z_vals)]
|
||||
if socket_name == "Translation":
|
||||
return [r[1] for r in results]
|
||||
return [r[0] for r in results]
|
||||
|
||||
dropped, trans = _drop_item(items[0], float(z_vals[0] if z_vals[0] is not None else 0.0))
|
||||
if socket_name == "Translation":
|
||||
return trans
|
||||
return dropped
|
||||
|
||||
|
||||
def format_name_template(template: str, so: Any, default_old_name: str = "") -> str:
|
||||
if not template or "{" not in template or "}" not in template:
|
||||
|
|
@ -789,25 +707,11 @@ class SLICENODE_RenameMesh(SliceNode):
|
|||
self.add_output("SLICESOCK_Mesh", "Geometry")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Geometry":
|
||||
return None
|
||||
|
||||
geom: Any = self.resolve("Geometry")
|
||||
if geom is None:
|
||||
return None
|
||||
|
||||
target_name = self.resolve("Name")
|
||||
|
||||
def _rename_one(g: Any, name: Any) -> Any:
|
||||
if not name:
|
||||
return g
|
||||
return _rename_slicing_item(g, str(name))
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
_rename_one,
|
||||
geom,
|
||||
target_name,
|
||||
input_names=["Name"]
|
||||
return self.eval_multivalue(
|
||||
lambda g, name: _rename_slicing_item(g, str(name)) if name else g,
|
||||
main_socket="Geometry",
|
||||
secondary_sockets=["Name"],
|
||||
socket_name=socket_name
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -865,15 +769,6 @@ class SLICENODE_AssignPdfProperties(SliceNode):
|
|||
layout.prop(self, "show_name")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Geometry":
|
||||
return None
|
||||
|
||||
geom: Any = self.resolve("Geometry")
|
||||
if not geom:
|
||||
return None
|
||||
|
||||
width_socket = self.resolve("Line Width")
|
||||
lw = width_socket if width_socket is not None else float(self.line_width)
|
||||
lc = (float(self.line_color[0]), float(self.line_color[1]), float(self.line_color[2]))
|
||||
sn = bool(self.show_name)
|
||||
|
||||
|
|
@ -881,12 +776,7 @@ class SLICENODE_AssignPdfProperties(SliceNode):
|
|||
w = float(line_w) if line_w is not None else float(self.line_width)
|
||||
return _assign_pdf_properties_to_item(g, w, lc, sn)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
_assign_one,
|
||||
geom,
|
||||
lw,
|
||||
input_names=["Line Width"]
|
||||
)
|
||||
return self.eval_multivalue(_assign_one, "Geometry", ["Line Width"], socket_name=socket_name)
|
||||
|
||||
|
||||
def _parse_settings_props(raw_settings: Any) -> list[tuple[str, str]]:
|
||||
|
|
@ -956,28 +846,9 @@ class SLICENODE_AssignPropertyToObject(SliceNode):
|
|||
self.add_output("SLICESOCK_Mesh", "Geometry")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Geometry":
|
||||
return None
|
||||
|
||||
geom: Any = self.resolve("Geometry")
|
||||
if not geom:
|
||||
return None
|
||||
|
||||
raw_settings = self.resolve("Settings")
|
||||
|
||||
if not _is_single_settings_spec(raw_settings):
|
||||
settings_arg = list(raw_settings)
|
||||
else:
|
||||
settings_arg = raw_settings
|
||||
|
||||
def _assign_one(g: Any, s: Any) -> Any:
|
||||
props = _parse_settings_props(s)
|
||||
return _assign_properties_to_item(g, props)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
_assign_one,
|
||||
geom,
|
||||
settings_arg,
|
||||
input_names=["Settings"]
|
||||
)
|
||||
return self.eval_multivalue(_assign_one, "Geometry", ["Settings"], socket_name=socket_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,11 +30,6 @@ class SLICENODE_GetPaths(SliceNode):
|
|||
pass
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
obj: Any = self.resolve("Object")
|
||||
e_attr_name: str = self.resolve("Extrusion Multiplier Attribute")
|
||||
f_attr_name: str = self.resolve("Feedrate Attribute")
|
||||
extra_attrs: dict[str, str] = self.resolve("Extra Attributes")
|
||||
|
||||
from ....infra.blender.blender_mesh_capture import capture_mesh_edges
|
||||
|
||||
def _get_single_path(o: Any, e_name: Any, f_name: Any, extra: Any) -> SlicePaths:
|
||||
|
|
@ -50,13 +45,12 @@ class SLICENODE_GetPaths(SliceNode):
|
|||
sp.objects_info = {"objects": [{"name": name_val, "polygon": []}]}
|
||||
return sp
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_get_single_path,
|
||||
obj,
|
||||
e_attr_name,
|
||||
f_attr_name,
|
||||
extra_attrs,
|
||||
input_names=["Extrusion Multiplier Attribute", "Feedrate Attribute", "Extra Attributes"]
|
||||
main_socket="Object",
|
||||
secondary_sockets=["Extrusion Multiplier Attribute", "Feedrate Attribute", "Extra Attributes"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
def _slice_single_mesh_item(coll: Any, layer_height: float, fl_height: float | None, rotation: Any, ignore_holes: bool) -> SlicePaths:
|
||||
|
|
@ -193,25 +187,6 @@ class SLICENODE_SliceShells(SliceNode):
|
|||
layout.prop(self, "ignore_holes")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
coll: Any = self.resolve("Mesh")
|
||||
layer_height: float = float(self.resolve("Layer Height") or 0.2)
|
||||
if layer_height <= 0:
|
||||
layer_height = 0.2
|
||||
|
||||
fl_height: float | None = None
|
||||
try:
|
||||
fl_val = self.resolve("First Layer Height")
|
||||
if fl_val is not None:
|
||||
fl_height = float(fl_val)
|
||||
except Exception:
|
||||
fl_height = None
|
||||
if fl_height is not None and fl_height <= 0:
|
||||
fl_height = None
|
||||
|
||||
rotation: Any = self.resolve("Rotation") or (0.0, 0.0, 0.0)
|
||||
|
||||
ignore_holes: bool = bool(getattr(self, "ignore_holes", False))
|
||||
if hasattr(self, "inputs") and "Ignore Holes" in self.inputs:
|
||||
try:
|
||||
|
|
@ -221,21 +196,17 @@ class SLICENODE_SliceShells(SliceNode):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if not coll: return SlicePaths()
|
||||
|
||||
def _slice_one(item: Any, lh_val: Any, flh_val: Any, rot_val: Any) -> SlicePaths:
|
||||
lh = float(lh_val) if lh_val is not None and float(lh_val) > 0 else 0.2
|
||||
flh = float(flh_val) if flh_val is not None and float(flh_val) > 0 else None
|
||||
return _slice_single_mesh_item(item, lh, flh, rot_val, ignore_holes)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_slice_one,
|
||||
coll,
|
||||
layer_height,
|
||||
fl_height,
|
||||
rotation,
|
||||
is_vector=[False, False, True],
|
||||
input_names=["Layer Height", "First Layer Height", "Rotation"]
|
||||
main_socket="Mesh",
|
||||
secondary_sockets=["Layer Height", "First Layer Height", "Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -263,26 +234,16 @@ class SLICENODE_BufferShells(SliceNode):
|
|||
layout.prop(self, "shape_option")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
distance_val = self.resolve("Distance")
|
||||
distance: float = float(distance_val) if distance_val is not None else 0.0
|
||||
rotation: Any = self.resolve("Rotation") or (0.0, 0.0, 0.0)
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _buf(p: Any, dist: Any, rot: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.buffer(float(dist if dist is not None else 0.0), miter=self.shape_option, rotation=rot)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_buf,
|
||||
paths,
|
||||
distance,
|
||||
rotation,
|
||||
is_vector=[False, True],
|
||||
input_names=["Distance", "Rotation"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Distance", "Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -329,14 +290,6 @@ class SLICENODE_BufferCurve(SliceNode):
|
|||
layout.prop(self, "single_sided")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
distance = self.resolve("Distance")
|
||||
rotation = self.resolve("Rotation")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _buf(p: Any, dist: Any, rot: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.buffer_curve(
|
||||
|
|
@ -347,13 +300,12 @@ class SLICENODE_BufferCurve(SliceNode):
|
|||
rotation=rot
|
||||
)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_buf,
|
||||
paths,
|
||||
distance,
|
||||
rotation,
|
||||
is_vector=[False, True],
|
||||
input_names=["Distance", "Rotation"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Distance", "Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -382,14 +334,6 @@ class SLICENODE_OffsetCurve(SliceNode):
|
|||
layout.prop(self, "join_style")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
distance = self.resolve("Distance")
|
||||
rotation = self.resolve("Rotation")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _off(p: Any, dist: Any, rot: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.offset_curve(
|
||||
|
|
@ -398,13 +342,12 @@ class SLICENODE_OffsetCurve(SliceNode):
|
|||
rotation=rot
|
||||
)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_off,
|
||||
paths,
|
||||
distance,
|
||||
rotation,
|
||||
is_vector=[False, True],
|
||||
input_names=["Distance", "Rotation"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Distance", "Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -418,21 +361,16 @@ class SLICENODE_PathOffset(SliceNode):
|
|||
self.add_output("SLICESOCK_Paths", "Paths")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
paths = self.resolve("Paths")
|
||||
offset = self.resolve("Offset")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _off(p: Any, off: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.offset(np.array(off if off is not None else (0.0, 0.0, 0.0), dtype=np.float64))
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_off,
|
||||
paths,
|
||||
offset,
|
||||
is_vector=[True],
|
||||
input_names=["Offset"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Offset"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -447,24 +385,17 @@ class SLICENODE_RotatePath(SliceNode):
|
|||
self.add_output("SLICESOCK_Paths", "Paths")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
paths = self.resolve("Paths")
|
||||
angle = self.resolve("Angle")
|
||||
center = self.resolve("Center")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _rot(p: Any, ang: Any, cent: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
center_vec = np.array(cent, dtype=np.float64) if cent and any(cent) else None
|
||||
return p.rotate(float(ang if ang is not None else 0.0), center=center_vec)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_rot,
|
||||
paths,
|
||||
angle,
|
||||
center,
|
||||
is_vector=[False, True],
|
||||
input_names=["Angle", "Center"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Angle", "Center"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -496,15 +427,6 @@ class SLICENODE_BufferShellsMultiple(SliceNode):
|
|||
layout.prop(self, "shape_option")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
paths = self.resolve("Paths")
|
||||
base_distance = self.resolve("Distance")
|
||||
width_step = self.resolve("Width Step")
|
||||
count = self.resolve("Count")
|
||||
rotation = self.resolve("Rotation")
|
||||
|
||||
if not paths:
|
||||
return SlicePaths()
|
||||
|
||||
def _buf_multi(p: Any, b_dist: Any, w_step: Any, cnt: Any, rot: Any) -> SlicePaths:
|
||||
c = int(cnt if cnt is not None else 1)
|
||||
if not p or c <= 0:
|
||||
|
|
@ -521,15 +443,12 @@ class SLICENODE_BufferShellsMultiple(SliceNode):
|
|||
return p.buffer_multiple(dist, c, step, miter=self.shape_option, rotation=rot)
|
||||
return SlicePaths()
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_buf_multi,
|
||||
paths,
|
||||
base_distance,
|
||||
width_step,
|
||||
count,
|
||||
rotation,
|
||||
is_vector=[False, False, False, True],
|
||||
input_names=["Distance", "Width Step", "Count", "Rotation"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Distance", "Width Step", "Count", "Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -560,15 +479,6 @@ class SLICENODE_Infill(SliceNode):
|
|||
layout.prop(self, "infill_type", text="")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
density = self.resolve("Density")
|
||||
infill_rotation = self.resolve("Infill Rotation")
|
||||
rotation = self.resolve("Layer Rotation")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _inf(p: Any, dens: Any, inf_rot: Any, rot: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.infill(
|
||||
|
|
@ -578,14 +488,12 @@ class SLICENODE_Infill(SliceNode):
|
|||
layer_rotation=rot
|
||||
)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_inf,
|
||||
paths,
|
||||
density,
|
||||
infill_rotation,
|
||||
rotation,
|
||||
is_vector=[False, False, True],
|
||||
input_names=["Density", "Infill Rotation", "Layer Rotation"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Density", "Infill Rotation", "Layer Rotation"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
|
|
@ -602,18 +510,17 @@ class SLICENODE_OverhangMask(SliceNode):
|
|||
pass
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
tolerance = self.resolve("Tolerance")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
def _mask(p: Any, tol: Any) -> SlicePaths:
|
||||
if not p: return SlicePaths()
|
||||
return p.get_overhang_mask(tolerance=float(tol if tol is not None else 0.05))
|
||||
|
||||
return eval_element_wise_multivalue(_mask, paths, tolerance, input_names=["Tolerance"])
|
||||
return self.eval_multivalue(
|
||||
_mask,
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Tolerance"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
@register_class
|
||||
class SLICENODE_PathOperations(SliceNodeJoiner):
|
||||
|
|
@ -752,21 +659,19 @@ class SLICENODE_SimplifyPaths(SliceNode):
|
|||
layout.prop(self, "preserve_topology")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths":
|
||||
return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
tolerance = self.resolve("Tolerance")
|
||||
if not paths:
|
||||
return SlicePaths()
|
||||
|
||||
def _simp(p: Any, tol: Any) -> SlicePaths:
|
||||
t_val = float(tol if tol is not None else 0.0)
|
||||
if not p or t_val <= 0:
|
||||
return p if isinstance(p, SlicePaths) else SlicePaths()
|
||||
return p.simplify(t_val, preserve_topology=self.preserve_topology)
|
||||
|
||||
return eval_element_wise_multivalue(_simp, paths, tolerance, input_names=["Tolerance"])
|
||||
return self.eval_multivalue(
|
||||
_simp,
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Tolerance"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
|
||||
@register_class
|
||||
|
|
@ -804,17 +709,6 @@ class SLICENODE_SeamPlacement(SliceNode):
|
|||
layout.prop(self, "acquire_transforms")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
if socket_name != "Paths":
|
||||
return SlicePaths()
|
||||
|
||||
paths = self.resolve("Paths")
|
||||
mesh_input: Any = self.resolve("Mesh")
|
||||
stagger_val = self.resolve("Seam Staggering")
|
||||
stagger_mm: float = float(stagger_val) if stagger_val is not None else 0.0
|
||||
|
||||
if not paths:
|
||||
return SlicePaths()
|
||||
|
||||
acquire_t = bool(getattr(self, "acquire_transforms", True))
|
||||
|
||||
def _place(p: Any, m_in: Any, stg: Any) -> SlicePaths:
|
||||
|
|
@ -825,12 +719,12 @@ class SLICENODE_SeamPlacement(SliceNode):
|
|||
return p
|
||||
return p.place_seams(m_in, mode=self.mode.lower(), acquire_transforms=acquire_t, stagger_mm=s_val)
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
return self.eval_multivalue(
|
||||
_place,
|
||||
paths,
|
||||
mesh_input,
|
||||
stagger_mm,
|
||||
input_names=["Mesh", "Seam Staggering"]
|
||||
main_socket="Paths",
|
||||
secondary_sockets=["Mesh", "Seam Staggering"],
|
||||
socket_name=socket_name,
|
||||
default=SlicePaths(),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,27 @@ class SliceNode(Node, AutoBlIdName):
|
|||
return resolve_input(None, cache=cache, visited=visited, fieldname=fieldname, node=self)
|
||||
return resolve_input(inputs.get(fieldname), cache=cache, visited=visited, fieldname=fieldname, node=self)
|
||||
|
||||
def eval_multivalue(
|
||||
self,
|
||||
op_func: Callable[..., Any],
|
||||
main_socket: str | None = None,
|
||||
secondary_sockets: list[str] | None = None,
|
||||
*,
|
||||
socket_name: str | None = None,
|
||||
default: Any = None,
|
||||
is_vector: list[bool] | None = None,
|
||||
) -> Any:
|
||||
from .list_utils import eval_multivalue
|
||||
return eval_multivalue(
|
||||
self,
|
||||
op_func,
|
||||
main_socket=main_socket,
|
||||
secondary_sockets=secondary_sockets,
|
||||
socket_name=socket_name,
|
||||
default=default,
|
||||
is_vector=is_vector,
|
||||
)
|
||||
|
||||
def add_output(self, bl_idname: str, name: str = "", identifier: str = "", hide: bool = False, display_shape: str = "", description: str = ""):
|
||||
bl_idname = NATIVE_SOCKET_MAP.get(bl_idname, bl_idname)
|
||||
eval_name = name or NATIVE_SOCKET_LABELS.get(bl_idname) or get_label_by_name(bl_idname) or bl_idname
|
||||
|
|
|
|||
|
|
@ -85,47 +85,31 @@ def prepare_indexed_inputs(
|
|||
(is_main_list, main_items, broadcasted_secondary_lists)
|
||||
"""
|
||||
main_is_list = is_input_a_list(main_data, is_vector=False)
|
||||
if main_is_list:
|
||||
main_items = list(main_data)
|
||||
n = len(main_items)
|
||||
else:
|
||||
main_items = [main_data]
|
||||
n = 1
|
||||
main_items = list(main_data) if main_is_list else [main_data]
|
||||
n = len(main_items)
|
||||
|
||||
vec_flags = is_vector or [False] * len(secondary_inputs)
|
||||
names = input_names or [f"input_{i+1}" for i in range(len(secondary_inputs))]
|
||||
|
||||
broadcasted_secondaries: list[list[Any]] = []
|
||||
|
||||
for i, sec_val in enumerate(secondary_inputs):
|
||||
is_vec = vec_flags[i] if i < len(vec_flags) else False
|
||||
name = names[i] if i < len(names) else f"input_{i+1}"
|
||||
|
||||
sec_is_list = is_input_a_list(sec_val, is_vector=is_vec)
|
||||
|
||||
if not sec_is_list:
|
||||
if main_is_list:
|
||||
broadcasted_secondaries.append([sec_val] * n)
|
||||
else:
|
||||
broadcasted_secondaries.append([sec_val])
|
||||
if not is_input_a_list(sec_val, is_vector=is_vec):
|
||||
broadcasted_secondaries.append([sec_val] * n)
|
||||
elif len(sec_val) == n:
|
||||
broadcasted_secondaries.append(list(sec_val))
|
||||
elif len(sec_val) == 1:
|
||||
broadcasted_secondaries.append([sec_val[0]] * n)
|
||||
else:
|
||||
m = len(sec_val)
|
||||
if not main_is_list:
|
||||
if m == 1:
|
||||
broadcasted_secondaries.append([sec_val[0]])
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Input list length mismatch: main input is a single item, but secondary input '{name}' has {m} items (must be 1 or a single value)."
|
||||
)
|
||||
else:
|
||||
if m == n:
|
||||
broadcasted_secondaries.append(list(sec_val))
|
||||
elif m == 1:
|
||||
broadcasted_secondaries.append([sec_val[0]] * n)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Input list length mismatch: main input has {n} items, but secondary input '{name}' has {m} items (must be {n} or 1)."
|
||||
)
|
||||
expected = "1 or a single value" if not main_is_list else f"{n} or 1"
|
||||
main_desc = f"{n} items" if main_is_list else "a single item"
|
||||
raise ValueError(
|
||||
f"Input list length mismatch: main input has {main_desc}, "
|
||||
f"but secondary input '{name}' has {m} items (must be {expected})."
|
||||
)
|
||||
|
||||
return main_is_list, main_items, broadcasted_secondaries
|
||||
|
||||
|
|
@ -135,6 +119,7 @@ def eval_element_wise_multivalue(
|
|||
*secondary_inputs: Any,
|
||||
is_vector: list[bool] | None = None,
|
||||
input_names: list[str] | None = None,
|
||||
output_index: int | None = None,
|
||||
) -> Any:
|
||||
"""Evaluates op_func element-wise with support for main and secondary list inputs:
|
||||
- If main_data is a list, returns a list of results.
|
||||
|
|
@ -142,6 +127,7 @@ def eval_element_wise_multivalue(
|
|||
- Secondary inputs that are lists are matched index per index.
|
||||
- Secondary inputs that are single values (or length-1 lists) are broadcast across all main elements.
|
||||
- Mismatched secondary list lengths raise ValueError.
|
||||
- If output_index is not None, unpacks result[output_index] from the op_func return value.
|
||||
"""
|
||||
is_main_list, main_items, sec_lists = prepare_indexed_inputs(
|
||||
main_data,
|
||||
|
|
@ -150,12 +136,110 @@ def eval_element_wise_multivalue(
|
|||
input_names=input_names,
|
||||
)
|
||||
|
||||
def _call(item: Any, i: int) -> Any:
|
||||
res = op_func(item, *(sec[i] for sec in sec_lists))
|
||||
if output_index is not None and isinstance(res, tuple):
|
||||
try:
|
||||
return res[output_index]
|
||||
except (IndexError, TypeError):
|
||||
return None
|
||||
return res
|
||||
|
||||
if is_main_list:
|
||||
return [
|
||||
op_func(item, *(sec[i] for sec in sec_lists))
|
||||
for i, item in enumerate(main_items)
|
||||
]
|
||||
return op_func(main_items[0], *(sec[0] for sec in sec_lists))
|
||||
return [_call(item, i) for i, item in enumerate(main_items)]
|
||||
return _call(main_items[0], 0)
|
||||
|
||||
def is_socket_vector_type(node: Any, socket_name: str) -> bool:
|
||||
"""Checks whether the socket on the node is a vector/rotation/color socket."""
|
||||
inputs = getattr(node, "inputs", None)
|
||||
if inputs:
|
||||
sock = None
|
||||
if hasattr(inputs, "get"):
|
||||
sock = inputs.get(socket_name)
|
||||
elif isinstance(inputs, (list, tuple)):
|
||||
for s in inputs:
|
||||
if getattr(s, "name", "") == socket_name:
|
||||
sock = s
|
||||
break
|
||||
if sock is not None:
|
||||
bl_id = getattr(sock, "bl_idname", "")
|
||||
if bl_id in ("NodeSocketVector", "NodeSocketRotation", "NodeSocketColor", "NodeSocketVectorXYZ", "NodeSocketVectorEuler"):
|
||||
return True
|
||||
if bl_id in ("NodeSocketFloat", "NodeSocketInt", "NodeSocketString", "NodeSocketBool", "SLICESOCK_Mesh", "SLICESOCK_Paths", "SLICESOCK_Settings"):
|
||||
return False
|
||||
name_lower = socket_name.lower()
|
||||
return name_lower in ("translation", "rotation", "scale", "offset", "center", "layer rotation", "color")
|
||||
|
||||
def eval_multivalue(
|
||||
node: Any,
|
||||
op_func: Callable[..., Any],
|
||||
main_socket: str | None = None,
|
||||
secondary_sockets: list[str] | None = None,
|
||||
*,
|
||||
socket_name: str | None = None,
|
||||
default: Any = None,
|
||||
is_vector: list[bool] | None = None,
|
||||
) -> Any:
|
||||
"""Generalized element-wise multi-value evaluator for SliceNode instances."""
|
||||
mapping = getattr(node, "list_socket_mapping", None)
|
||||
|
||||
# Infer main_socket if not specified
|
||||
if main_socket is None:
|
||||
if mapping and len(mapping) > 0 and mapping[0][0]:
|
||||
main_socket = mapping[0][0]
|
||||
else:
|
||||
inputs = getattr(node, "inputs", None)
|
||||
if inputs and len(inputs) > 0:
|
||||
main_socket = getattr(inputs[0], "name", "")
|
||||
else:
|
||||
return default
|
||||
|
||||
# Determine output index / socket validation
|
||||
output_index: int | None = None
|
||||
if socket_name is not None and mapping:
|
||||
out_spec = next((outs for m_in, outs in mapping if m_in == main_socket), None)
|
||||
if out_spec is not None:
|
||||
if isinstance(out_spec, (list, tuple)):
|
||||
if socket_name not in out_spec:
|
||||
return default
|
||||
output_index = out_spec.index(socket_name)
|
||||
elif isinstance(out_spec, str) and out_spec:
|
||||
if socket_name != out_spec:
|
||||
return default
|
||||
|
||||
# Resolve main input
|
||||
resolve_fn = getattr(node, "resolve", None)
|
||||
if not callable(resolve_fn):
|
||||
return default
|
||||
|
||||
main_data = resolve_fn(main_socket)
|
||||
if main_data is None:
|
||||
return default
|
||||
if isinstance(main_data, list) and len(main_data) == 0:
|
||||
return []
|
||||
|
||||
# Infer secondary_sockets if not specified
|
||||
if secondary_sockets is None:
|
||||
inputs = getattr(node, "inputs", [])
|
||||
secondary_sockets = [s.name for s in inputs if getattr(s, "name", "") != main_socket]
|
||||
|
||||
# Resolve secondary inputs
|
||||
sec_vals = [resolve_fn(sname) for sname in secondary_sockets]
|
||||
|
||||
# Infer is_vector if not specified
|
||||
if is_vector is None:
|
||||
vec_flags = [is_socket_vector_type(node, sname) for sname in secondary_sockets]
|
||||
else:
|
||||
vec_flags = is_vector
|
||||
|
||||
return eval_element_wise_multivalue(
|
||||
op_func,
|
||||
main_data,
|
||||
*sec_vals,
|
||||
is_vector=vec_flags,
|
||||
input_names=secondary_sockets,
|
||||
output_index=output_index,
|
||||
)
|
||||
|
||||
def eval_element_wise(op_func: Callable[[Any], Any], data: Any) -> Any:
|
||||
"""If data is a list, applies op_func to each element and returns a list.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue