caching and metadata
This commit is contained in:
parent
7f480c698d
commit
64eff798ac
4 changed files with 89 additions and 27 deletions
|
|
@ -621,6 +621,29 @@ class SLICENODE_GcodeBlock(SliceNode):
|
||||||
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
return self.outputs['Gcode'].value
|
return self.outputs['Gcode'].value
|
||||||
|
|
||||||
|
@register_class
|
||||||
|
class SLICENODE_AssignMetadata(SliceNode):
|
||||||
|
bl_label = "Assign Metadata"
|
||||||
|
|
||||||
|
def init(self, context):
|
||||||
|
self.add_input("SLICESOCK_String", "Name")
|
||||||
|
self.add_input("SLICESOCK_String", "Filament Type")
|
||||||
|
self.add_input("SLICESOCK_String", "Printer Model")
|
||||||
|
self.add_output("SLICESOCK_GcodeRo", "Gcode")
|
||||||
|
|
||||||
|
def evaluate_output(self, socket_name: str, **_kwargs) -> str:
|
||||||
|
name = self.resolve("Name")
|
||||||
|
filament = self.resolve("Filament Type")
|
||||||
|
printer = self.resolve("Printer Model")
|
||||||
|
lines = []
|
||||||
|
if name:
|
||||||
|
lines.append(f'; objects_info = {{"objects": [{{"name": "{name}", "polygon": []}}]}}')
|
||||||
|
if filament:
|
||||||
|
lines.append(f'; filament_type = {filament}')
|
||||||
|
if printer:
|
||||||
|
lines.append(f'; printer_model = {printer}')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
@register_class
|
@register_class
|
||||||
class SLICENODE_GcodeHelper(SliceNode):
|
class SLICENODE_GcodeHelper(SliceNode):
|
||||||
bl_label = "Gcode Helper"
|
bl_label = "Gcode Helper"
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,7 @@ class SLICENODE_MT_Gcode(Menu):
|
||||||
assert layout
|
assert layout
|
||||||
layout.operator("node.add_node", text="Paths to Gcode").type = "SLICENODE_PathsToGcode"
|
layout.operator("node.add_node", text="Paths to Gcode").type = "SLICENODE_PathsToGcode"
|
||||||
layout.operator("node.add_node", text="Gcode Block").type = "SLICENODE_GcodeBlock"
|
layout.operator("node.add_node", text="Gcode Block").type = "SLICENODE_GcodeBlock"
|
||||||
|
layout.operator("node.add_node", text="Assign Metadata").type = "SLICENODE_AssignMetadata"
|
||||||
layout.operator("node.add_node", text="Gcode Helper").type = "SLICENODE_GcodeHelper"
|
layout.operator("node.add_node", text="Gcode Helper").type = "SLICENODE_GcodeHelper"
|
||||||
layout.operator("node.add_node", text="Load Gcode").type = "SLICENODE_LoadGcode"
|
layout.operator("node.add_node", text="Load Gcode").type = "SLICENODE_LoadGcode"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from bpy.types import NodeSocket, Node
|
||||||
|
|
||||||
class NodeException(Exception): ...
|
class NodeException(Exception): ...
|
||||||
|
|
||||||
_EvalCache = dict[tuple[int, str], Any]
|
_EvalCache = dict[tuple[int, str, str], Any]
|
||||||
|
|
||||||
class AutoBlIdName:
|
class AutoBlIdName:
|
||||||
def __init_subclass__(cls, **kwargs):
|
def __init_subclass__(cls, **kwargs):
|
||||||
|
|
@ -32,7 +32,9 @@ class SliceNode(Node, AutoBlIdName):
|
||||||
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))
|
cache = getattr(self, "_current_cache", None)
|
||||||
|
visited = getattr(self, "_current_visited", None)
|
||||||
|
return resolve_input(self.inputs.get(fieldname), cache=cache, visited=visited)
|
||||||
|
|
||||||
def add_output(self, bl_idname: str, name: str = "", identifier: str = ""):
|
def add_output(self, bl_idname: str, name: str = "", identifier: str = ""):
|
||||||
eval_name = name or get_label_by_name(bl_idname)
|
eval_name = name or get_label_by_name(bl_idname)
|
||||||
|
|
@ -48,8 +50,10 @@ class SliceNode(Node, AutoBlIdName):
|
||||||
self._update_multi_inputs()
|
self._update_multi_inputs()
|
||||||
|
|
||||||
def resolve_multi_input(self, fieldname) -> Any:
|
def resolve_multi_input(self, fieldname) -> Any:
|
||||||
|
cache = getattr(self, "_current_cache", None)
|
||||||
|
visited = getattr(self, "_current_visited", None)
|
||||||
inputs = [i for i in list(self.inputs) if i.name == fieldname]
|
inputs = [i for i in list(self.inputs) if i.name == fieldname]
|
||||||
return [resolve_input(r) for r in inputs]
|
return [resolve_input(r, cache=cache, visited=visited) for r in inputs]
|
||||||
|
|
||||||
def _update_multi_inputs(self):
|
def _update_multi_inputs(self):
|
||||||
if not self.get('multi_sockets'): return
|
if not self.get('multi_sockets'): return
|
||||||
|
|
@ -81,6 +85,21 @@ class SliceNode(Node, AutoBlIdName):
|
||||||
|
|
||||||
## Evaluation helpers
|
## Evaluation helpers
|
||||||
|
|
||||||
|
_current_execution_cache: _EvalCache | None = None
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def execution_cache():
|
||||||
|
global _current_execution_cache
|
||||||
|
if _current_execution_cache is not None:
|
||||||
|
yield _current_execution_cache
|
||||||
|
return
|
||||||
|
|
||||||
|
_current_execution_cache = {}
|
||||||
|
try:
|
||||||
|
yield _current_execution_cache
|
||||||
|
finally:
|
||||||
|
_current_execution_cache = None
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def node_execution_feedback(node: bpy.types.Node):
|
def node_execution_feedback(node: bpy.types.Node):
|
||||||
# Save original color
|
# Save original color
|
||||||
|
|
@ -93,6 +112,7 @@ def node_execution_feedback(node: bpy.types.Node):
|
||||||
# Refresh UI
|
# Refresh UI
|
||||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
try:
|
try:
|
||||||
|
with execution_cache():
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
node.use_custom_color = old_use_color
|
node.use_custom_color = old_use_color
|
||||||
|
|
@ -129,6 +149,11 @@ def resolve_input(
|
||||||
|
|
||||||
assert socket.node
|
assert socket.node
|
||||||
|
|
||||||
|
if cache is None:
|
||||||
|
cache = getattr(socket.node, "_current_cache", _current_execution_cache)
|
||||||
|
if visited is None:
|
||||||
|
visited = getattr(socket.node, "_current_visited", None)
|
||||||
|
|
||||||
if not socket.is_linked:
|
if not socket.is_linked:
|
||||||
val = getattr(socket, "value", None)
|
val = getattr(socket, "value", None)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
|
|
@ -153,21 +178,30 @@ def _resolve_output(
|
||||||
cache: _EvalCache | None = None,
|
cache: _EvalCache | None = None,
|
||||||
visited: set[tuple[int, str]] | None = None,
|
visited: set[tuple[int, str]] | None = None,
|
||||||
):
|
):
|
||||||
|
if cache is None:
|
||||||
|
cache = _current_execution_cache
|
||||||
|
|
||||||
cache = {} if cache is None else cache
|
cache = {} if cache is None else cache
|
||||||
visited = set() if visited is None else visited
|
visited = set() if visited is None else visited
|
||||||
|
|
||||||
assert socket.node
|
assert socket.node
|
||||||
key = (socket.node.as_pointer(), socket.node.name)
|
key = (socket.node.as_pointer(), socket.node.name, socket.name)
|
||||||
if key in cache: return cache[key]
|
if key in cache: return cache[key]
|
||||||
if key in visited: return default # cycle guard
|
|
||||||
|
|
||||||
visited.add(key)
|
node_key = (socket.node.as_pointer(), socket.node.name)
|
||||||
|
if node_key in visited: return default # cycle guard
|
||||||
|
|
||||||
|
visited.add(node_key)
|
||||||
try:
|
try:
|
||||||
with node_execution_feedback(socket.node):
|
with node_execution_feedback(socket.node):
|
||||||
if not isinstance(socket.node, SliceNode):
|
if not isinstance(socket.node, SliceNode):
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
# Set these temporarily so self.resolve() can find them
|
||||||
|
socket.node._current_cache = cache # type: ignore
|
||||||
|
socket.node._current_visited = visited # type: ignore
|
||||||
|
|
||||||
|
try:
|
||||||
if getattr(socket.node, "mute", False):
|
if getattr(socket.node, "mute", False):
|
||||||
# Attempt bypass: find a matching input socket by name or type
|
# Attempt bypass: find a matching input socket by name or type
|
||||||
input_to_bypass = socket.node.inputs.get(socket.name)
|
input_to_bypass = socket.node.inputs.get(socket.name)
|
||||||
|
|
@ -188,4 +222,7 @@ def _resolve_output(
|
||||||
cache[key] = value
|
cache[key] = value
|
||||||
return value
|
return value
|
||||||
finally:
|
finally:
|
||||||
visited.remove(key)
|
if hasattr(socket.node, "_current_cache"): del socket.node._current_cache
|
||||||
|
if hasattr(socket.node, "_current_visited"): del socket.node._current_visited
|
||||||
|
finally:
|
||||||
|
visited.remove(node_key)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ def get_gcode_name_from_metadata(gcode: str, default: str = "output") -> str:
|
||||||
names: list[str] = [str(v['name'].split(" id:")[0]) for v in gcode_info['objects']]
|
names: list[str] = [str(v['name'].split(" id:")[0]) for v in gcode_info['objects']]
|
||||||
counts = Counter(names)
|
counts = Counter(names)
|
||||||
object_names = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items())
|
object_names = "-".join(f"{c}x{n}" if c > 1 else n for n, c in counts.items())
|
||||||
return "-".join([object_names, filament, printer])
|
parts = [object_names, filament, printer]
|
||||||
|
return "-".join([str(p) for p in parts if p])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return default
|
return default
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue