Просмотр исходного кода

Fix: Correctly Sort Links to Group Arrays

Look at the comment in readtree to understand this commit
Special note: this really needs more robust testing
but since I have tested stuff that I understand the only
test cases I am unsure about are those which are too hard
for me to imagine ahead of time. So probably I fix it if/when
someone actually encounters it.
Joseph Brandenburg 2 недель назад
Родитель
Сommit
38acf70638
5 измененных файлов с 46 добавлено и 27 удалено
  1. 8 5
      base_definitions.py
  2. 2 1
      misc_nodes.py
  3. 0 1
      node_container_common.py
  4. 32 17
      readtree.py
  5. 4 3
      schema_solve.py

+ 8 - 5
base_definitions.py

@@ -13,6 +13,7 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
 from .utilities import get_socket_maps, relink_socket_map, do_relink
 
 FLOAT_EPSILON=0.0001 # used to check against floating point inaccuracy
+links_sort_key= lambda a : (-a.multi_input_sort_id, -a.sub_sort_id)
 
 def TellClasses():
     #Why use a function to do this? Because I don't need every class to register.
@@ -1006,7 +1007,7 @@ class NodeLink:
     to_node = None
     to_socket = None
 
-    def __init__(self, from_node, from_socket, to_node, to_socket, multi_input_sort_id=0):
+    def __init__(self, from_node, from_socket, to_node, to_socket, multi_input_sort_id=0, sub_sort_id=0):
         if from_node.signature == to_node.signature:
             raise RuntimeError("Cannot connect a node to itself.")
         self.from_node = from_node
@@ -1015,7 +1016,8 @@ class NodeLink:
         self.to_socket = to_socket
         self.from_node.outputs[self.from_socket].links.append(self)
         # it is the responsibility of the node that uses these links to sort them correctly based on the sort_id
-        self.multi_input_sort_id = multi_input_sort_id
+        self.multi_input_sort_id = multi_input_sort_id # this is the sort_id of the link in the UI
+        self.sub_sort_id = sub_sort_id # this is for sorting within a  bundled link (one link in the UI)
         self.to_node.inputs[self.to_socket].links.append(self)
         self.is_hierarchy = detect_hierarchy_link(from_node, from_socket, to_node, to_socket,)
         self.is_alive = True
@@ -1064,7 +1066,7 @@ class NodeSocket:
         if (traverse_target):
             self.can_traverse = True
 
-    def connect(self, node, socket, sort_id=0):
+    def connect(self, node, socket, sort_id=0, sub_sort_id=0):
         if  (self.is_input):
             to_node   = self.node; from_node = node
             to_socket = self.name; from_socket = socket
@@ -1081,7 +1083,8 @@ class NodeSocket:
                 from_socket,
                 to_node,
                 to_socket,
-                sort_id)
+                sort_id,
+                sub_sort_id)
         return new_link
 
     def set_traverse_target(self, traverse_target):
@@ -1091,7 +1094,7 @@ class NodeSocket:
     def flush_links(self):
         """ Removes dead links from this socket."""
         self.links = [l for l in self.links if l.is_alive]
-        self.links.sort(key=lambda a : -a.multi_input_sort_id)
+        self.links.sort(key=links_sort_key)
         self.is_linked = bool(self.links)
 
     @property

+ 2 - 1
misc_nodes.py

@@ -1935,13 +1935,14 @@ class UtilityArrayGet(MantisNode):
         self.rerouted=[]
 
     def bPrepare(self, bContext = None,):
+        from .base_definitions import links_sort_key
         if len(self.rerouted)>0:
             self.prepared, self.executed = True, True
             return #Either it is already done or it doesn't matter.
         elif self.prepared == False:
             # sort the array entries
             for inp in self.inputs.values():
-                inp.links.sort(key=lambda a : -a.multi_input_sort_id)
+                inp.links.sort(key=links_sort_key)
             oob   = self.evaluate_input("OoB Behaviour")
             index = self.evaluate_input("Index")
 

+ 0 - 1
node_container_common.py

@@ -19,7 +19,6 @@ def get_socket_value(node_socket):
 
 # TODO: modify this to work with multi-input nodes
 def trace_single_line(node_container, input_name, link_index=0):
-    # DO: refactor this for new link class
     """Traces a line to its input."""
     nodes = [node_container]
     # Trace a single line

+ 32 - 17
readtree.py

@@ -2,32 +2,47 @@ from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
                         wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
 
 
-
-# we need to reroute the incoming links to a new GroupInterface node
-# then we need to reroute the outgoing links from the GroupInterface
-# down into the tree -  or visa versa (NodeGroupOutput input to Group output)
+# this function is kind of confusing and is very important,
+# so it bears a full explanation: its purpose is to connect
+# the links going into a group to the nodes in that group.
+# FIRST we connect all the incoming links into the Group Node to
+# a Group Interface node that does nothing but mark the entrance.
+# Then, we connect all the outgoing links back to the nodes
+# that had incoming links, so the nodes OUTSIDE the Node Group
+# are connected directly to BOTH the GroupInterface and the
+# nodes INSIDE the node group.
+# we give the GroupInterface nodes an obscenely high
+# multi_input_sort_id so that they are always last.
+# but since these links are going IN, they shouldn't cause any problems.
+# the sub_sort_id is set here in case there are UI links which represent
+# multiple Mantis links - the mantis links are sorted within the UI links
+# and the UI links are sorted as normal, so all the links are in the right
+# order.... probably. BUG here?
+# I want the Group Interface nodes to be part of the hierarchy...
+# but I want to cut the links. hmmm what to do? Fix it if it causes problems.
+# solution to hypothetical BUG could be to do traversal on the links
+# instead of the sockets.
 def grp_node_reroute_common(in_node, out_node, interface):
+    from .base_definitions import links_sort_key
     for in_node_input in in_node.inputs:
+        i = 0
+        if len(in_node_input.links)>1: # sort here to ensure correct sub_sort_id
+            in_node_input.links.sort(key=links_sort_key)
         while (in_node_input.links):
             in_link = in_node_input.links.pop()
             from_node = in_link.from_node; from_socket = in_link.from_socket
-            # the inputs/outputs on the group and in/out nodes are IDs
-            from_node.outputs[from_socket].connect(
-                interface,in_node_input.name, sort_id = in_link.multi_input_sort_id)
-            in_link.die()
+            link = from_node.outputs[from_socket].connect(
+                interface,in_node_input.name, sort_id = 2**16, sub_sort_id=i)
+            i += 1; in_link.die()
     for out_node_output in out_node.outputs:
         while (out_node_output.links):
             out_link = out_node_output.links.pop()
             to_node = out_link.to_node; to_socket = out_link.to_socket
-            # LARGE CHALLENGE: handling sort id's when there are arrays of various kinds
-            # - going to an array as a single joined link (bundle) from the input
-            # - many single inputs going to an array
-            # - mixture of the two
-            # - we need to sort them by the ui_link's sort id and their own sort id, too
-            # I think I have to use multiple sort ID variables here. need to think.
-            for l in interface.inputs[out_node_output.name].links:
-                interface.outputs[out_node_output.name].connect(
-                    to_node, to_socket, sort_id = l.multi_input_sort_id)
+            for j, l in enumerate(interface.inputs[out_node_output.name].links):
+                # we are connecting the link from the ORIGINAL output to the FINAL input.
+                link = l.from_node.outputs[l.from_socket].connect(
+                    to_node, to_socket, sort_id = out_link.multi_input_sort_id)
+                link.sub_sort_id = j
             out_link.die()
 
 def reroute_links_grp(group, all_nodes):

+ 4 - 3
schema_solve.py

@@ -4,7 +4,7 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
                               wrapOrange,)
 from .utilities import init_connections, init_dependencies, get_link_in_out
 from .base_definitions import (SchemaUINode, custom_props_types, \
-    MantisNodeGroup, SchemaGroup, replace_types, GraphError)
+    MantisNodeGroup, SchemaGroup, replace_types, GraphError, links_sort_key)
 from .node_container_common import setup_custom_props_from_np
 # a class that solves Schema nodes
 from bpy.types import NodeGroupInput, NodeGroupOutput
@@ -61,7 +61,7 @@ class SchemaSolver:
         # Sort the multi-input nodes in reverse order of ID, this ensures that they are
         #   read in the order they were created
         for inp in self.node.inputs.values():
-            inp.links.sort(key=lambda a : -a.multi_input_sort_id)
+            inp.links.sort(key=links_sort_key)
 
         from bpy.types import NodeGroupInput, NodeGroupOutput
         for ui_node in self.tree.nodes:
@@ -386,7 +386,8 @@ class SchemaSolver:
             to_socket_name=ui_link.to_socket.name
             if to_node.node_type in ['DUMMY_SCHEMA']:
                 to_socket_name=ui_link.to_socket.identifier
-            connection = NodeLink(l.from_node, l.from_socket, to_node, to_socket_name, l.multi_input_sort_id)
+            connection = NodeLink(l.from_node, l.from_socket, to_node, to_socket_name,
+                                  l.multi_input_sort_id, l.sub_sort_id)
             to_node.flush_links()
 
     def handle_link_to_constant_output(self, frame_mantis_nodes, index, ui_link,  to_ui_node):