variable_f_rate
This commit is contained in:
parent
3080f65695
commit
1d0147ce73
4 changed files with 46 additions and 17 deletions
|
|
@ -345,7 +345,8 @@ class SLICENODE_GetMesh(SliceNode):
|
|||
class SLICENODE_GetPaths(SliceNode):
|
||||
bl_label = "Get Paths"
|
||||
|
||||
attr_name: NfStringProperty(name="Extrusion Multiplier", default="e_mult") # pyright: ignore[reportInvalidTypeForm]
|
||||
attr_name: NfStringProperty(name="Extrusion Attr", default="e_mult") # pyright: ignore[reportInvalidTypeForm]
|
||||
f_attr_name: NfStringProperty(name="Feedrate Attr", default="f_rate") # pyright: ignore[reportInvalidTypeForm]
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Object")
|
||||
|
|
@ -354,15 +355,17 @@ class SLICENODE_GetPaths(SliceNode):
|
|||
def draw_buttons(self, context, layout):
|
||||
assert layout
|
||||
layout.prop(self, "attr_name")
|
||||
layout.prop(self, "f_attr_name")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
|
||||
obj: Any = self.resolve("Object")
|
||||
attr_name: str = self.attr_name
|
||||
f_attr_name: str = self.f_attr_name
|
||||
|
||||
from ...infra.blender.blender_mesh_capture import capture_mesh_edges
|
||||
so, e_rates = capture_mesh_edges(obj, attr_name=attr_name)
|
||||
so, e_rates, f_rates = capture_mesh_edges(obj, attr_name=attr_name, f_attr_name=f_attr_name)
|
||||
|
||||
return SlicePaths(so, extrusion_rate=e_rates)
|
||||
return SlicePaths(so, extrusion_rate=e_rates, feedrate=f_rates)
|
||||
|
||||
@register_class
|
||||
class SLICENODE_SliceShells(SliceNode):
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ SCALE = 1_000_000
|
|||
class SlicePaths(list[np.ndarray]):
|
||||
groups: list[int]
|
||||
extrusion_rate: list[np.ndarray]
|
||||
feedrate: list[np.ndarray]
|
||||
|
||||
def __init__(self, paths: list[np.ndarray] = [], groups: list[int] = [], extrusion_rate: list[np.ndarray] | 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):
|
||||
super().__init__(paths)
|
||||
|
||||
self.groups = []
|
||||
|
|
@ -24,18 +25,28 @@ class SlicePaths(list[np.ndarray]):
|
|||
self.extrusion_rate = list(extrusion_rate)
|
||||
else:
|
||||
self.extrusion_rate = [np.ones(len(p)) for p in paths]
|
||||
|
||||
self.feedrate = []
|
||||
if feedrate:
|
||||
self.feedrate = list(feedrate)
|
||||
else:
|
||||
self.feedrate = [np.zeros(len(p)) for p in paths]
|
||||
|
||||
def copy_with(self, paths: list[np.ndarray], groups: list[int] | None = None, extrusion_rate: list[np.ndarray] | None = None) -> SlicePaths:
|
||||
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) -> SlicePaths:
|
||||
"""Returns a new SlicePaths object with new paths/groups but preserving all other metadata."""
|
||||
if extrusion_rate is None:
|
||||
# Only reuse extrusion_rate if path lengths match exactly
|
||||
if len(paths) == len(self) and all(len(p) == len(op) for p, op in zip(paths, self)):
|
||||
extrusion_rate = self.extrusion_rate
|
||||
|
||||
new_obj = SlicePaths(paths, groups if groups is not None else self.groups, extrusion_rate)
|
||||
if feedrate is None:
|
||||
if len(paths) == len(self) and all(len(p) == len(op) for p, op in zip(paths, self)):
|
||||
feedrate = self.feedrate
|
||||
|
||||
new_obj = SlicePaths(paths, groups if groups is not None else self.groups, extrusion_rate, feedrate)
|
||||
# Copy any other attributes that might have been added
|
||||
for k, v in self.__dict__.items():
|
||||
if k not in ["groups", "extrusion_rate"]:
|
||||
if k not in ["groups", "extrusion_rate", "feedrate"]:
|
||||
setattr(new_obj, k, v)
|
||||
return new_obj
|
||||
|
||||
|
|
@ -522,6 +533,7 @@ class Pather(object):
|
|||
def __init__(self, paths: list[np.ndarray], **kwargs) -> None:
|
||||
self.paths = paths
|
||||
self.extrusion_rates = getattr(paths, "extrusion_rate", [np.ones(len(p)) for p in paths])
|
||||
self.feedrates = getattr(paths, "feedrate", [np.zeros(len(p)) for p in paths])
|
||||
self.bed_center_x: float = 250.
|
||||
self.bed_center_y: float = 250.
|
||||
self.feedrate: float = 1200.
|
||||
|
|
@ -580,6 +592,7 @@ class Pather(object):
|
|||
continue
|
||||
|
||||
e_rates = self.extrusion_rates[i]
|
||||
f_rates = self.feedrates[i]
|
||||
|
||||
x0, y0, z0 = coords[0]
|
||||
|
||||
|
|
@ -592,11 +605,13 @@ class Pather(object):
|
|||
gcode.append(f"G0 X{x0:.3f} Y{y0:.3f} Z{z0:.3f}")
|
||||
old_x, old_y, old_z = x0, y0, z0
|
||||
|
||||
fr = self.feedrate
|
||||
old_fr = 0
|
||||
for j, (p1, p2) in enumerate(zip(coords, coords[1:])):
|
||||
seg_len: float = segment_length_3d(p1, p2)
|
||||
e_segment = seg_len * self.extrusion_rate * e_rates[j]
|
||||
|
||||
# Per-segment feedrate value (not multiplier)
|
||||
fr = f_rates[j] if f_rates[j] > 0 else self.feedrate
|
||||
|
||||
old_x, old_y, old_z = p1
|
||||
x, y, z = p2
|
||||
|
|
|
|||
|
|
@ -410,11 +410,12 @@ def _cached_capture_mesh_edges(
|
|||
mesh_name: str,
|
||||
matrix_tuple: tuple[float, ...],
|
||||
scene_scale: float,
|
||||
attr_name: str = "e_mult"
|
||||
) -> tuple[list[NDArray[float64]], list[NDArray[float64]]]:
|
||||
attr_name: str = "e_mult",
|
||||
f_attr_name: str = "f_rate"
|
||||
) -> tuple[list[NDArray[float64]], list[NDArray[float64]], list[NDArray[float64]]]:
|
||||
# This helper expects that the object and mesh exist and are accessible via bpy.data
|
||||
obj = bpy.data.objects.get(obj_name)
|
||||
if not obj: return [], []
|
||||
if not obj: return [], [], []
|
||||
|
||||
# We don't use obj_eval here directly as it's not hashable,
|
||||
# but we assume this is called when we know we need to re-capture.
|
||||
|
|
@ -424,10 +425,10 @@ def _cached_capture_mesh_edges(
|
|||
obj_eval = obj.evaluated_get(depsgraph)
|
||||
mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
except Exception:
|
||||
return [], []
|
||||
return [], [], []
|
||||
|
||||
if not mesh or not mesh.vertices:
|
||||
return [], []
|
||||
return [], [], []
|
||||
|
||||
try:
|
||||
world_mat = np.array(matrix_tuple).reshape(4, 4)
|
||||
|
|
@ -439,6 +440,10 @@ def _cached_capture_mesh_edges(
|
|||
if attr_name in mesh.attributes:
|
||||
mesh.attributes[attr_name].data.foreach_get("value", e_mults)
|
||||
|
||||
f_rates = np.zeros(num_verts, dtype=np.float64)
|
||||
if f_attr_name in mesh.attributes:
|
||||
mesh.attributes[f_attr_name].data.foreach_get("value", f_rates)
|
||||
|
||||
tx_verts = (verts @ world_mat[:3, :3].T + world_mat[:3, 3]) * scene_scale
|
||||
|
||||
edges_set = {tuple(sorted(e.vertices)) for e in mesh.edges}
|
||||
|
|
@ -447,29 +452,34 @@ def _cached_capture_mesh_edges(
|
|||
split_at = np.where(~connected)[0] + 1
|
||||
raw_paths = np.split(tx_verts, split_at)
|
||||
raw_e_mults = np.split(e_mults, split_at)
|
||||
raw_f_rates = np.split(f_rates, split_at)
|
||||
indices_split = np.split(np.arange(num_verts), split_at)
|
||||
|
||||
final_paths = []
|
||||
final_e_mults = []
|
||||
final_f_rates = []
|
||||
for i, path_coords in enumerate(raw_paths):
|
||||
if path_coords.shape[0] == 0:
|
||||
continue
|
||||
idx = indices_split[i]
|
||||
e_vals = raw_e_mults[i]
|
||||
f_vals = raw_f_rates[i]
|
||||
if len(idx) > 2 and tuple(sorted((idx[0], idx[-1]))) in edges_set:
|
||||
path_coords = np.vstack((path_coords, path_coords[0]))
|
||||
e_vals = np.concatenate((e_vals, [e_vals[0]]))
|
||||
f_vals = np.concatenate((f_vals, [f_vals[0]]))
|
||||
final_paths.append(path_coords)
|
||||
final_e_mults.append(e_vals)
|
||||
final_f_rates.append(f_vals)
|
||||
|
||||
return final_paths, final_e_mults
|
||||
return final_paths, final_e_mults, final_f_rates
|
||||
|
||||
finally:
|
||||
if hasattr(obj_eval, "to_mesh_clear"):
|
||||
obj_eval.to_mesh_clear()
|
||||
|
||||
def capture_mesh_edges(obj: Object, attr_name: str = "e_mult") -> tuple[list[NDArray[float64]], list[NDArray[float64]]]:
|
||||
if not obj or not obj.data: return [], []
|
||||
def capture_mesh_edges(obj: Object, attr_name: str = "e_mult", f_attr_name: str = "f_rate") -> tuple[list[NDArray[float64]], list[NDArray[float64]], list[NDArray[float64]]]:
|
||||
if not obj or not obj.data: return [], [], []
|
||||
|
||||
if obj.mode == 'EDIT':
|
||||
obj.update_from_editmode()
|
||||
|
|
@ -485,5 +495,6 @@ def capture_mesh_edges(obj: Object, attr_name: str = "e_mult") -> tuple[list[NDA
|
|||
obj.data.name,
|
||||
matrix_tuple,
|
||||
scene_scale,
|
||||
attr_name
|
||||
attr_name,
|
||||
f_attr_name
|
||||
)
|
||||
BIN
nodes.blend
BIN
nodes.blend
Binary file not shown.
Loading…
Add table
Reference in a new issue