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:
|
||||
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
|
||||
class SLICENODE_GcodeHelper(SliceNode):
|
||||
bl_label = "Gcode Helper"
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ class SLICENODE_MT_Gcode(Menu):
|
|||
assert layout
|
||||
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="Assign Metadata").type = "SLICENODE_AssignMetadata"
|
||||
layout.operator("node.add_node", text="Gcode Helper").type = "SLICENODE_GcodeHelper"
|
||||
layout.operator("node.add_node", text="Load Gcode").type = "SLICENODE_LoadGcode"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from bpy.types import NodeSocket, Node
|
|||
|
||||
class NodeException(Exception): ...
|
||||
|
||||
_EvalCache = dict[tuple[int, str], Any]
|
||||
_EvalCache = dict[tuple[int, str, str], Any]
|
||||
|
||||
class AutoBlIdName:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
|
|
@ -32,7 +32,9 @@ class SliceNode(Node, AutoBlIdName):
|
|||
def evaluate_output(self, socket_name: str, **_kwargs) -> 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 = ""):
|
||||
eval_name = name or get_label_by_name(bl_idname)
|
||||
|
|
@ -48,8 +50,10 @@ class SliceNode(Node, AutoBlIdName):
|
|||
self._update_multi_inputs()
|
||||
|
||||
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]
|
||||
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):
|
||||
if not self.get('multi_sockets'): return
|
||||
|
|
@ -81,6 +85,21 @@ class SliceNode(Node, AutoBlIdName):
|
|||
|
||||
## 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
|
||||
def node_execution_feedback(node: bpy.types.Node):
|
||||
# Save original color
|
||||
|
|
@ -93,7 +112,8 @@ def node_execution_feedback(node: bpy.types.Node):
|
|||
# Refresh UI
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
try:
|
||||
yield
|
||||
with execution_cache():
|
||||
yield
|
||||
finally:
|
||||
node.use_custom_color = old_use_color
|
||||
node.color = old_color
|
||||
|
|
@ -129,6 +149,11 @@ def resolve_input(
|
|||
|
||||
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:
|
||||
val = getattr(socket, "value", None)
|
||||
if val is not None:
|
||||
|
|
@ -153,39 +178,51 @@ def _resolve_output(
|
|||
cache: _EvalCache | None = None,
|
||||
visited: set[tuple[int, str]] | None = None,
|
||||
):
|
||||
if cache is None:
|
||||
cache = _current_execution_cache
|
||||
|
||||
cache = {} if cache is None else cache
|
||||
visited = set() if visited is None else visited
|
||||
|
||||
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 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:
|
||||
with node_execution_feedback(socket.node):
|
||||
if not isinstance(socket.node, SliceNode):
|
||||
return default
|
||||
|
||||
if getattr(socket.node, "mute", False):
|
||||
# Attempt bypass: find a matching input socket by name or type
|
||||
input_to_bypass = socket.node.inputs.get(socket.name)
|
||||
if not input_to_bypass:
|
||||
for inp in socket.node.inputs:
|
||||
if inp.bl_idname == socket.bl_idname:
|
||||
input_to_bypass = inp
|
||||
break
|
||||
# Set these temporarily so self.resolve() can find them
|
||||
socket.node._current_cache = cache # type: ignore
|
||||
socket.node._current_visited = visited # type: ignore
|
||||
|
||||
if input_to_bypass:
|
||||
value = resolve_input(typing.cast(Any, input_to_bypass), cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
return default
|
||||
try:
|
||||
if getattr(socket.node, "mute", False):
|
||||
# Attempt bypass: find a matching input socket by name or type
|
||||
input_to_bypass = socket.node.inputs.get(socket.name)
|
||||
if not input_to_bypass:
|
||||
for inp in socket.node.inputs:
|
||||
if inp.bl_idname == socket.bl_idname:
|
||||
input_to_bypass = inp
|
||||
break
|
||||
|
||||
assert socket.node.evaluate_output
|
||||
value = socket.node.evaluate_output(socket.name, cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
if input_to_bypass:
|
||||
value = resolve_input(typing.cast(Any, input_to_bypass), cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
return default
|
||||
|
||||
assert socket.node.evaluate_output
|
||||
value = socket.node.evaluate_output(socket.name, cache=cache, visited=visited)
|
||||
cache[key] = value
|
||||
return value
|
||||
finally:
|
||||
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(key)
|
||||
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']]
|
||||
counts = Counter(names)
|
||||
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:
|
||||
pass
|
||||
return default
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue