Browse Source

Separate Execution Sorting and Execution

Joseph Brandenburg 2 months ago
parent
commit
688e96c51f
1 changed files with 76 additions and 72 deletions
  1. 76 72
      readtree.py

+ 76 - 72
readtree.py

@@ -484,6 +484,58 @@ def execution_error_cleanup(node, exception, switch_objects = [], show_error=Fal
     prRed(f"Error: {exception} in node {ui_sig}")
     return exception
 
+def sort_execution(nodes, xForm_pass):
+    execution_failed=False
+    sorted_nodes = []
+    from .node_container_common import GraphError
+    # check for cycles here by keeping track of the number of times a node has been visited.
+    visited={}
+    check_max_len=len(nodes)**2 # seems too high but safe. In a well-ordered graph, I guess this number should be less than the number of nodes.
+    max_iterations = len(nodes)**2
+    i = 0
+    while(xForm_pass):
+        if execution_failed: break
+        if i >= max_iterations:
+            execution_failed = True
+            raise GraphError("There is probably a cycle somewhere in the graph. "
+                                "Or a connection missing in a Group/Schema Input")
+        i+=1    
+        n = xForm_pass.pop()
+        if visited.get(n.signature) is not None:
+            visited[n.signature]+=1
+        else:
+            visited[n.signature]=0
+        if visited[n.signature] > check_max_len:
+            execution_failed = True
+            raise GraphError("There is a probably a cycle in the graph somewhere. "
+                                "Or a connection missing in a Group/Schema Input")
+            # we're trying to solve the halting problem at this point.. don't do that.
+            # TODO find a better way! there are algo's for this but they will require using a different solving algo, too
+        if n.execution_prepared:
+            continue
+        if n.node_type not in ['XFORM', 'UTILITY']:
+            for dep in n.hierarchy_dependencies:
+                if not dep.execution_prepared:
+                    xForm_pass.appendleft(n) # hold it
+                    break
+            else:
+                n.execution_prepared=True
+                sorted_nodes.append(n)
+                for conn in n.hierarchy_connections:
+                    if  not conn.execution_prepared:
+                        xForm_pass.appendleft(conn)
+        else:
+            for dep in n.hierarchy_dependencies:
+                if not dep.execution_prepared:
+                    break
+            else:
+                n.execution_prepared=True
+                sorted_nodes.append(n)
+                for conn in n.hierarchy_connections:
+                    if  not conn.execution_prepared:
+                        xForm_pass.appendleft(conn)
+    return sorted_nodes, execution_failed
+
 def execute_tree(nodes, base_tree, context, error_popups = False):
     assert nodes is not None, "Failed to parse tree."
     assert len(nodes) > 0, "No parsed nodes for execution."\
@@ -494,7 +546,6 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
     original_active = context.view_layer.objects.active
     start_execution_time = time()
     mContext = None
-
     from collections import deque
     xForm_pass = deque()
     for nc in nodes.values():
@@ -504,82 +555,35 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         nc.reset_execution()
         check_and_add_root(nc, xForm_pass)
     mContext.execution_failed = False
-    executed = []
 
-    # check for cycles here by keeping track of the number of times a node has been visited.
-    visited={}
-    check_max_len=len(nodes)**2 # seems too high but safe. In a well-ordered graph, I guess this number should be less than the number of nodes.
-    max_iterations = len(nodes)**2
-    i = 0
     switch_me = [] # switch the mode on these objects
     active = None # only need it for switching modes
     select_me = []
-    execution_failed=False
     try:
-        while(xForm_pass):
-            if execution_failed: break
-            if i >= max_iterations:
-                execution_failed = True
-                raise GraphError("There is probably a cycle somewhere in the graph. "
-                                 "Or a connection missing in a Group/Schema Input")
-            i+=1    
-            n = xForm_pass.pop()
-            if visited.get(n.signature) is not None:
-                visited[n.signature]+=1
-            else:
-                visited[n.signature]=0
-            if visited[n.signature] > check_max_len:
-                execution_failed = True
-                raise GraphError("There is a probably a cycle in the graph somewhere. "
-                                 "Or a connection missing in a Group/Schema Input")
-                # we're trying to solve the halting problem at this point.. don't do that.
-                # TODO find a better way! there are algo's for this but they will require using a different solving algo, too
-            if n.execution_prepared:
-                continue
-            if n.node_type not in ['XFORM', 'UTILITY']:
-                for dep in n.hierarchy_dependencies:
-                    if not dep.execution_prepared:
-                        xForm_pass.appendleft(n) # hold it
-                        break
-                else:
-                    n.execution_prepared=True
-                    executed.append(n)
-                    for conn in n.hierarchy_connections:
-                        if  not conn.execution_prepared:
-                            xForm_pass.appendleft(conn)
-            else:
-                for dep in n.hierarchy_dependencies:
-                    if not dep.execution_prepared:
-                        break
-                else:
-                    try:
-                        if not n.prepared:
-                            n.bPrepare(context)
-                        if not n.executed:
-                            n.bTransformPass(context)
-                        if (n.__class__.__name__ == "xFormArmature" ):
-                            ob = n.bGetObject()
-                            switch_me.append(ob)
-                            active = ob
-                        if not (n.__class__.__name__ == "xFormBone" ) and hasattr(n, "bGetObject"):
-                            ob = n.bGetObject()
-                            if isinstance(ob, bpy.types.Object):
-                                select_me.append(ob)
-                    except Exception as e:
-                        e = execution_error_cleanup(n, e, show_error=error_popups)
-                        if error_popups == False:
-                            raise e
-                        execution_failed = True; break
-                    n.execution_prepared=True
-                    executed.append(n)
-                    for conn in n.hierarchy_connections:
-                        if  not conn.execution_prepared:
-                            xForm_pass.appendleft(conn)
-        
+        sorted_nodes, execution_failed = sort_execution(nodes, xForm_pass)
+        for n in sorted_nodes:
+            try:
+                if not n.prepared:
+                    n.bPrepare(context)
+                if not n.executed:
+                    n.bTransformPass(context)
+                if (n.__class__.__name__ == "xFormArmature" ):
+                    ob = n.bGetObject()
+                    switch_me.append(ob)
+                    active = ob
+                if not (n.__class__.__name__ == "xFormBone" ) and hasattr(n, "bGetObject"):
+                    ob = n.bGetObject()
+                    if isinstance(ob, bpy.types.Object):
+                        select_me.append(ob)
+            except Exception as e:
+                e = execution_error_cleanup(n, e, show_error=error_popups)
+                if error_popups == False:
+                    raise e
+                execution_failed = True; break
 
         switch_mode(mode='POSE', objects=switch_me)
 
-        for n in executed:
+        for n in sorted_nodes:
             try:
                 if not n.prepared:
                     n.bPrepare(context)
@@ -600,7 +604,7 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         for ob in switch_me:
             ob.data.pose_position = 'POSE'
 
-        for n in executed:
+        for n in sorted_nodes:
             try:
                 n.bFinalize(context)
             except Exception as e:
@@ -615,7 +619,7 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
             ob.data.pose_position = 'REST'
 
         # finally, apply modifiers and bind stuff
-        for n in executed:
+        for n in sorted_nodes:
             try:
                 n.bModifierApply(context)
             except Exception as e:
@@ -629,7 +633,7 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         
         tot_time = (time() - start_execution_time)
         if not execution_failed:
-            prGreen(f"Executed tree of {len(executed)} nodes in {tot_time} seconds")
+            prGreen(f"Executed tree of {len(sorted_nodes)} nodes in {tot_time} seconds")
         if (original_active):
             context.view_layer.objects.active = original_active
             original_active.select_set(True)