This commit is contained in:
Nicolas Predella 2026-08-25 12:34:29 +03:00
parent 3793c45f54
commit 29f7b71be1
5 changed files with 124 additions and 4 deletions

View file

@ -232,7 +232,7 @@ class SLICENODE_SeparateMeshByRange(SliceNode):
# Attribute values might be floats or ints, we check for equality
mask = vals == target_val
num_digits = max(2, self.end_val)
num_digits = max(2, len(str(self.end_val)))
if np.all(mask):
res.objects.append(so)

View file

@ -32,6 +32,10 @@ class SLICENODE_GetPaths(SliceNode):
sp = SlicePaths(so, extrusion_rate=e_rates, feedrate=f_rates, extra_attributes=extra_attrs_data)
if hasattr(obj, "transforms"):
sp.transforms = list(getattr(obj, "transforms", []))
if hasattr(obj, "name") and getattr(obj, "name"):
name_val = str(getattr(obj, "name"))
sp.name = name_val
sp.objects_info = {"objects": [{"name": name_val, "polygon": []}]}
return sp
@register_class
@ -109,6 +113,22 @@ class SLICENODE_SliceShells(SliceNode):
slices.first_layer_height = fl_height
slices.transforms = list(getattr(coll, "transforms", [])) or list(getattr(merged_object, "transforms", []))
names = []
if objects:
for o in objects:
n = getattr(o, "name", "")
if n:
if n.endswith("_merged"):
n = n[:-7]
if n and n not in names:
names.append(n)
if not names and hasattr(coll, "name") and getattr(coll, "name"):
names.append(str(getattr(coll, "name")))
if names:
slices.name = "-".join(names)
slices.objects_info = {"objects": [{"name": n, "polygon": []} for n in names]}
if len(z_values) > 0 and fl_height is not None and layer_height > 0:
mult = fl_height / layer_height
z_first = z_values[0]

View file

@ -3,6 +3,7 @@ from numpy import float64
import numpy as np
import pyclipper
from typing import Any, Literal
import copy
from shapely.geometry.base import BaseGeometry
from ..infra.blender.blender_mesh_capture import PdfProperties
@ -164,8 +165,10 @@ class SlicePaths(list[np.ndarray]):
transforms: list[dict]
object_properties: list[tuple[str, str]]
pdf_properties: PdfProperties
name: str
objects_info: dict | None
def __init__(self, paths: list[np.ndarray] = [], groups: list[int] = [], extrusion_rate: list[np.ndarray] | None = None, feedrate: list[np.ndarray] | None = None, extra_attributes: dict[str, list[np.ndarray]] | None = None, layer_height: float = 0.2, first_layer_height: float | None = None, transforms: list[dict] | None = None):
def __init__(self, paths: list[np.ndarray] = [], groups: list[int] = [], extrusion_rate: list[np.ndarray] | None = None, feedrate: list[np.ndarray] | None = None, extra_attributes: dict[str, list[np.ndarray]] | None = None, layer_height: float = 0.2, first_layer_height: float | None = None, transforms: list[dict] | None = None, name: str = "", objects_info: dict | None = None):
super().__init__(paths)
self.groups = []
@ -188,6 +191,8 @@ class SlicePaths(list[np.ndarray]):
self.layer_height = layer_height
self.first_layer_height = first_layer_height
self.transforms = list(transforms) if transforms is not None else []
self.name = name
self.objects_info = copy.deepcopy(objects_info) if objects_info is not None else None
def copy_with(self, paths: list[np.ndarray], groups: list[int] | None = None, extrusion_rate: list[np.ndarray] | None = None, feedrate: list[np.ndarray] | None = None, extra_attributes: dict[str, list[np.ndarray]] | None = None) -> SlicePaths:
"""Returns a new SlicePaths object with new paths/groups, preserving all metadata and instance attributes."""
@ -349,11 +354,30 @@ class SlicePaths(list[np.ndarray]):
merged.append_with_group(path, group=g, extrusion_rate=ext, feedrate=feed, extra_attributes=extra)
merged_transforms = []
merged_names = []
merged_objs = []
for sp in paths_list:
if hasattr(sp, "objects_info") and getattr(sp, "objects_info"):
for obj_entry in sp.objects_info.get("objects", []):
if obj_entry not in merged_objs:
merged_objs.append(dict(obj_entry))
n_str = obj_entry.get("name", "")
if n_str and n_str not in merged_names:
merged_names.append(n_str)
elif hasattr(sp, "name") and getattr(sp, "name"):
n_str = str(getattr(sp, "name"))
if n_str and n_str not in merged_names:
merged_names.append(n_str)
merged_objs.append({"name": n_str, "polygon": []})
for t in getattr(sp, "transforms", []):
if t not in merged_transforms:
merged_transforms.append(t)
merged.transforms = merged_transforms
if merged_objs:
merged.objects_info = {"objects": merged_objs}
merged.name = "-".join(merged_names)
return merged
@ -1583,6 +1607,8 @@ class Pather(object):
self.retraction_feedrate: float = 0.0
self.lift: float = 0.0
self.transforms: list[dict] = list(getattr(paths, "transforms", []))
self.objects_info: dict | None = getattr(paths, "objects_info", None)
self.name: str = str(getattr(paths, "name", ""))
@property
def layer_width(self) -> float:
@ -1653,6 +1679,13 @@ class Pather(object):
f";OFFSET_Y={offset_y:.6f}",
])
if self.objects_info:
import json
header.append(f"; objects_info = {json.dumps(self.objects_info)}")
elif self.name:
import json
header.append(f'; objects_info = {json.dumps({"objects": [{"name": self.name, "polygon": []}]})}')
if hasattr(self, "transforms") and self.transforms:
import json
header.append(f";PREPARATION_TRANSFORMS={json.dumps(self.transforms)}")

View file

@ -37,7 +37,7 @@ def parse_gcode_metadata(gcode: str) -> dict[str, Any]:
return fields
def get_gcode_name_from_metadata(gcode: str, default: str = "output") -> str:
"""Generates a descriptive name for the G-code based on its objects_info metadata."""
"""Generates a descriptive name for the G-code based on its objects_info metadata or object comments."""
parts_match = re.search(r"; objects_info = (.*)", gcode)
filament_match = re.search(r"; filament_type = (.*)", gcode)
@ -54,8 +54,19 @@ def get_gcode_name_from_metadata(gcode: str, default: str = "output") -> str:
object_names = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items())
parts = [object_names, filament, printer]
return "-".join([str(p) for p in parts if p])
except:
except Exception:
pass
# Fallback search for object name comments (e.g. ; OBJECT_NAME = ..., ; object_name = ..., ; OBJECT: ...)
obj_matches = re.findall(r";\s*(?:OBJECT_NAME|object_name|OBJECT|object)\s*[:=]\s*(.+)", gcode)
if obj_matches:
names = [m.strip().split(" id:")[0] for m in obj_matches if m.strip()]
if names:
counts = Counter(names)
object_names = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items())
parts = [object_names, filament, printer]
return "-".join([str(p) for p in parts if p])
return default

View file

@ -706,5 +706,61 @@ class TestSlice(unittest.TestCase):
res_intr = node.evaluate_output("SLICESOCK_Intrinsics")
self.assertEqual(res_intr.retraction_feedrate, 1800.0)
def test_gcode_name_from_metadata_paths_to_gcode(self):
from NodeSlicer.core.slice import SlicePaths, Pather
from NodeSlicer.infra.networking.gcode_utils import get_gcode_name_from_metadata
# 1. Test SlicePaths with explicit name
p1 = np.array([[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]])
sp = SlicePaths([p1], name="GearPart")
pather = Pather(sp)
gcode = pather.to_gcode(include_header=True)
self.assertIn("; objects_info =", gcode)
name = get_gcode_name_from_metadata(gcode)
self.assertEqual(name, "GearPart")
# 2. Test SlicePaths with objects_info (multiple objects)
sp_multi = SlicePaths(
[p1],
objects_info={"objects": [{"name": "PartA", "polygon": []}, {"name": "PartB", "polygon": []}]}
)
pather_multi = Pather(sp_multi)
gcode_multi = pather_multi.to_gcode(include_header=True)
self.assertIn("; objects_info =", gcode_multi)
name_multi = get_gcode_name_from_metadata(gcode_multi)
self.assertEqual(name_multi, "PartA-PartB")
# 3. Test get_gcode_name_from_metadata fallback object comments
gcode_fallback = "; Generated Gcode\n; OBJECT_NAME = CustomBracket\nG1 X10 Y10\n"
self.assertEqual(get_gcode_name_from_metadata(gcode_fallback), "CustomBracket")
def test_separate_mesh_by_range_padding(self):
from NodeSlicer.core.nodeslicer.nodes.mesh import SLICENODE_SeparateMeshByRange
from NodeSlicer.infra.blender.blender_mesh_capture import SlicingCollection, SlicingObject
node = SLICENODE_SeparateMeshByRange()
node.end_val = 6
so = SlicingObject()
so.name = "CA"
so.mesh = np.zeros((4, 4, 3))
so.attributes = {
"part_id": {
"domain": "FACE",
"type": "INT",
"values": np.array([1, 1, 2, 2])
}
}
coll = SlicingCollection.__new__(SlicingCollection)
coll.objects = [so]
coll.name = "TestColl"
coll.transforms = []
node.resolve = lambda name: coll if name == "Mesh" else ("part_id" if name == "Attribute" else None)
res_1 = node.evaluate_output("1")
self.assertIsNotNone(res_1)
self.assertEqual(res_1.objects[0].name, "CA01")
if __name__ == '__main__':
unittest.main()