Loop Nodes #4

Open
Denys Hsu wants to merge 5 commits from cgtinker/powership:loop into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
2 changed files with 29 additions and 24 deletions
Showing only changes of commit 7d6e455518 - Show all commits

View File

@ -81,10 +81,7 @@ class RigNodesNodeTree(bpy.types.NodeTree):
# Set iteration count foreach (nested) loop. # Set iteration count foreach (nested) loop.
for output_node in output_nodes: for output_node in output_nodes:
input_node = output_node._get_connection_node() input_node = output_node._get_connection_node()
nodes = list(output_node._get_loop_nodes(input_node)) for node in output_node._get_innern_loop_nodes(input_node):
for node in nodes:
if node == output_node:
continue
node.iterations *= output_node.num_socks node.iterations *= output_node.num_socks
cgtinker marked this conversation as resolved

There is no need to construct a list here, just loop over the generator returned by output_node._get_loop_nodes()

There is no need to construct a list here, just loop over the generator returned by `output_node._get_loop_nodes()`
def _run_loop( def _run_loop(
@ -108,10 +105,17 @@ class RigNodesNodeTree(bpy.types.NodeTree):
while to_visit: while to_visit:
node = to_visit.popleft() node = to_visit.popleft()
if node in visited:
# Nested nodes are added to visited nodes as they get executed in advance.
# A Node can also be visited when a node has inputs from two different
# nodes; both of those will list this node as 'successor'.
continue
if isinstance(node, LoopInputNode) and node != input_node: if isinstance(node, LoopInputNode) and node != input_node:
# Find the nested loop. # Find the nested loop.
nested_output_node = node._get_connection_node() nested_output_node = node._get_connection_node()
nested_loop = deque(nested_output_node._get_loop_nodes(node)) nested_loop = deque(nested_output_node._get_innern_loop_nodes(node))
nested_loop.appendleft(node)
nested_loop.append(nested_output_node) nested_loop.append(nested_output_node)
# Don't revisit nested nodes in the parent loop. # Don't revisit nested nodes in the parent loop.
@ -123,12 +127,6 @@ class RigNodesNodeTree(bpy.types.NodeTree):
self._run_loop(node, depsgraph, nested_loop.copy(), depth + 1) self._run_loop(node, depsgraph, nested_loop.copy(), depth + 1)
continue continue
if node in visited:
# Nested nodes are added to visited nodes as they get executed in advance.
# A Node can also be visited when a node has inputs from two different
# nodes; both of those will list this node as 'successor'.
continue
print(f"{indent}\tRunning: {node}, remaining runs: {node.iterations}") print(f"{indent}\tRunning: {node}, remaining runs: {node.iterations}")
visited.add(node) visited.add(node)
node.run(depsgraph) node.run(depsgraph)
@ -164,17 +162,17 @@ class RigNodesNodeTree(bpy.types.NodeTree):
# Find the loop. # Find the loop.
output_node = node._get_connection_node() output_node = node._get_connection_node()
loop = deque(output_node._get_loop_nodes(node)) innern_loop = deque(output_node._get_innern_loop_nodes(node))
loop.append(output_node)
# Add (nested) loop nodes to visited. # Add (nested) loop nodes to visited.
for loop_node in loop: visited.add(node)
visited.add(output_node)
for loop_node in innern_loop:
visited.add(loop_node) visited.add(loop_node)
# Run all iterations of the found loop (recursive if nested). # Run all iterations of the found loop (recursive if nested).
for _ in range(0, output_node.num_socks): for _ in range(0, output_node.num_socks):
self._run_loop(node, depsgraph, to_visit.copy()) self._run_loop(node, depsgraph, to_visit.copy())
continue
# Everything that this node depends on has run, so time to run it # Everything that this node depends on has run, so time to run it
# itself. It can be taken off the queue. # itself. It can be taken off the queue.
@ -300,16 +298,23 @@ class AbstractRigNodesNode(bpy.types.Node):
def _outputs(self): def _outputs(self):
return self.outputs return self.outputs
def _get_loop_nodes( def _get_innern_loop_nodes(
self: "LoopOutputNode", input_node: "LoopInputNode" self: "LoopOutputNode", input_node: "LoopInputNode",
) -> Iterable["AbstractRigNodesNode"]: ) -> Iterable["AbstractRigNodesNode"]:
"""Generator, yields nodes that should be executed before the loop output node.""" """Generator, yields nodes between the loop input and the loop output node"""
nodes = list(self._connected_nodes(self._inputs, "from_socket")) visited: Set["AbstractRigNodesNode"] = set()
to_visit = [self]
if not input_node in nodes and len(nodes) > 0: while to_visit:
from_node = nodes[-1] from_node = to_visit.pop()
yield from from_node._get_loop_nodes(input_node) for node in from_node.exec_order_prerequisites():
yield from nodes if node in visited:
continue
visited.add(node)
if node != input_node:
to_visit.append(node)
yield node
def exec_order_prerequisites(self) -> Iterable["AbstractRigNodesNode"]: def exec_order_prerequisites(self) -> Iterable["AbstractRigNodesNode"]:
"""Generator, yields the nodes that should be executed before this one.""" """Generator, yields the nodes that should be executed before this one."""
@ -593,7 +598,6 @@ class LoopInputNode(AbstractRigNodesNode):
raise RuntimeError( raise RuntimeError(
"Loop Output Node is required to execute a loop." "Loop Output Node is required to execute a loop."
) )
if isinstance(connected_socket.node, LoopOutputNode): if isinstance(connected_socket.node, LoopOutputNode):
return connected_socket.node return connected_socket.node
raise RuntimeError("Loop Output Node is required to execute a loop.") raise RuntimeError("Loop Output Node is required to execute a loop.")
@ -607,6 +611,7 @@ class LoopInputNode(AbstractRigNodesNode):
def reset_run(self): def reset_run(self):
super().reset_run() super().reset_run()
self.index = 0 self.index = 0
self.iterations = self.num_socks
def init(self, context: bpy.types.Context) -> None: def init(self, context: bpy.types.Context) -> None:
for index in range(self.num_socks): for index in range(self.num_socks):

Binary file not shown.