Browse Source

Fix Schema fail when Group linked to In/Outgoing

The problem was that I was attempting to get the next node
but it hadn't been generated yet because it was in a node group
There was never a problem with outgoing connection after all
it was only handling the held link from the outgoing connection
that was messed up, and only then if the next node was a group!

oddly, it did work in some situations with the old code
because the node groups were generated.

this code maybe could be cleaned up by forcing the node group
to generate as soon as it is encountered
but I think this solution, though less elegant, is better
since I can then deal with the connection to the node group
which is already handled well elsewhere. So this doesn't add
complexity or modify the schema solve as a system.
Brandenburg 3 weeks ago
parent
commit
a11e4be7da
1 changed files with 50 additions and 39 deletions
  1. 50 39
      schema_solve.py

+ 50 - 39
schema_solve.py

@@ -51,7 +51,7 @@ class SchemaSolver:
         self.solve_length = self.node.evaluate_input("Schema Length")
         self.solve_length = self.node.evaluate_input("Schema Length")
         # I'm making this a property of the solver because the solver's data is modified as it solves each iteration
         # I'm making this a property of the solver because the solver's data is modified as it solves each iteration
         self.index = 0
         self.index = 0
-        
+
         prWhite(f"\nExpanding schema {self.tree.name} in node {self.node.signature}"
         prWhite(f"\nExpanding schema {self.tree.name} in node {self.node.signature}"
                  f" with length {self.solve_length}.")
                  f" with length {self.solve_length}.")
 
 
@@ -66,14 +66,14 @@ class SchemaSolver:
         from bpy.types import NodeGroupInput, NodeGroupOutput
         from bpy.types import NodeGroupInput, NodeGroupOutput
         for ui_node in self.tree.nodes:
         for ui_node in self.tree.nodes:
             # first we need to fill the parameters of the schema nodes.
             # first we need to fill the parameters of the schema nodes.
-            # we use the bl_idname because all schema nodes should be single-instance 
+            # we use the bl_idname because all schema nodes should be single-instance
             signature = (*self.tree_path_names, ui_node.bl_idname)
             signature = (*self.tree_path_names, ui_node.bl_idname)
             if isinstance(ui_node, (SchemaUINode, NodeGroupInput, NodeGroupOutput)):
             if isinstance(ui_node, (SchemaUINode, NodeGroupInput, NodeGroupOutput)):
                 # We use the schema node's "natural signature" here because it represents
                 # We use the schema node's "natural signature" here because it represents
                 # the "original" signature of the schema UI group node since this schema
                 # the "original" signature of the schema UI group node since this schema
                 # solver may be in a nested schema, and its node's signature may have
                 # solver may be in a nested schema, and its node's signature may have
                 # uuid/index attached.
                 # uuid/index attached.
-                get_sig = (*self.node.ui_signature, ui_node.bl_idname) 
+                get_sig = (*self.node.ui_signature, ui_node.bl_idname)
                 if not (mantis_node := self.all_nodes.get(get_sig)):
                 if not (mantis_node := self.all_nodes.get(get_sig)):
                     raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
                     raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
                 self.schema_nodes[signature] = mantis_node
                 self.schema_nodes[signature] = mantis_node
@@ -89,14 +89,14 @@ class SchemaSolver:
                 mantis_node = SchemaConstOutput(signature=signature, base_tree=self.node.base_tree, parent_schema_node=self.node)
                 mantis_node = SchemaConstOutput(signature=signature, base_tree=self.node.base_tree, parent_schema_node=self.node)
                 self.schema_nodes[signature] = mantis_node
                 self.schema_nodes[signature] = mantis_node
                 mantis_node.fill_parameters(ui_node)
                 mantis_node.fill_parameters(ui_node)
-    
+
     def set_index_strings(self):
     def set_index_strings(self):
         self.index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index).zfill(4)
         self.index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index).zfill(4)
         self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index-1).zfill(4)
         self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index-1).zfill(4)
         if self.is_node_group:
         if self.is_node_group:
             self.index_str=lambda : ''
             self.index_str=lambda : ''
             self.prev_index_str=lambda : ''
             self.prev_index_str=lambda : ''
-    
+
     def init_schema_links(self,):
     def init_schema_links(self,):
         """ Sort and store the links to/from the Schema group node."""
         """ Sort and store the links to/from the Schema group node."""
         for item in self.tree.interface.items_tree:
         for item in self.tree.interface.items_tree:
@@ -110,7 +110,7 @@ class SchemaSolver:
                         if incoming_links := self.node.inputs[item.identifier].links:
                         if incoming_links := self.node.inputs[item.identifier].links:
                             self.incoming_connections[item.name] = incoming_links[0]
                             self.incoming_connections[item.name] = incoming_links[0]
                         else:
                         else:
-                            self.incoming_connections[item.name] = None 
+                            self.incoming_connections[item.name] = None
                     else: # OUTPUT
                     else: # OUTPUT
                         if outgoing_links := self.node.outputs[item.identifier].links:
                         if outgoing_links := self.node.outputs[item.identifier].links:
                             self.outgoing_connections[item.name] = outgoing_links.copy()
                             self.outgoing_connections[item.name] = outgoing_links.copy()
@@ -143,9 +143,11 @@ class SchemaSolver:
                             self.array_output_connections[item.identifier]=[]
                             self.array_output_connections[item.identifier]=[]
                         if out_links := self.node.outputs[item.identifier].links:
                         if out_links := self.node.outputs[item.identifier].links:
                             self.array_output_connections[item.identifier] = out_links.copy()
                             self.array_output_connections[item.identifier] = out_links.copy()
-    
 
 
-    
+
+
+    def is_node_deeper_nested(self, queried_node, compare_node):
+        return len(compare_node.signature) < len(queried_node.signature)
 
 
     def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
     def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
         for prototype_ui_node in self.tree.nodes:
         for prototype_ui_node in self.tree.nodes:
@@ -213,17 +215,17 @@ class SchemaSolver:
             signature = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], unique_name)
             signature = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], unique_name)
             from_node = self.all_nodes.get(signature)
             from_node = self.all_nodes.get(signature)
             if not from_node:
             if not from_node:
-                from_node = autogen_node(self.node.base_tree, ui_link.from_socket, 
+                from_node = autogen_node(self.node.base_tree, ui_link.from_socket,
                                 signature=signature, mContext=self.node.mContext)
                                 signature=signature, mContext=self.node.mContext)
                 from_node.parameters = {ui_link.from_socket.name:index}
                 from_node.parameters = {ui_link.from_socket.name:index}
                 frame_mantis_nodes[signature]=from_node; self.solved_nodes[signature]=from_node
                 frame_mantis_nodes[signature]=from_node; self.solved_nodes[signature]=from_node
                 self.all_nodes[signature]=from_node
                 self.all_nodes[signature]=from_node
             _connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=ui_link.to_socket.identifier)
             _connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=ui_link.to_socket.identifier)
-            return 
+            return
         # Since the index is already determined, it is safe to remove the socket and just keep the value.
         # Since the index is already determined, it is safe to remove the socket and just keep the value.
         to_node.parameters[ui_link.to_socket.name] = index
         to_node.parameters[ui_link.to_socket.name] = index
         del to_node.inputs[ui_link.to_socket.name]
         del to_node.inputs[ui_link.to_socket.name]
-    
+
     def handle_link_from_schema_length_input(self, frame_mantis_nodes, ui_link):
     def handle_link_from_schema_length_input(self, frame_mantis_nodes, ui_link):
         # see, here I can just use the schema node
         # see, here I can just use the schema node
         _from_name, to_name = get_link_in_out(ui_link)
         _from_name, to_name = get_link_in_out(ui_link)
@@ -256,14 +258,31 @@ class SchemaSolver:
     def handle_link_to_outgoing_connection_output(self, frame_mantis_nodes, ui_link,):
     def handle_link_to_outgoing_connection_output(self, frame_mantis_nodes, ui_link,):
         mantis_incoming_node = self.schema_nodes[*self.tree_path_names,  'SchemaIncomingConnection']
         mantis_incoming_node = self.schema_nodes[*self.tree_path_names,  'SchemaIncomingConnection']
         for mantis_link in mantis_incoming_node.outputs[ui_link.to_socket.name].links:
         for mantis_link in mantis_incoming_node.outputs[ui_link.to_socket.name].links:
-            to_mantis_node, to_mantis_socket = mantis_link.to_node, mantis_link.to_socket
+            to_mantis_node, to_socket_name = mantis_link.to_node, mantis_link.to_socket
             from_name = get_link_in_out(ui_link)[0]
             from_name = get_link_in_out(ui_link)[0]
             from_mantis_node = self.solved_nodes[ (*self.autogen_path_names, from_name+self.prev_index_str()) ]
             from_mantis_node = self.solved_nodes[ (*self.autogen_path_names, from_name+self.prev_index_str()) ]
-            to_mantis_node = frame_mantis_nodes[ (*self.autogen_path_names, to_mantis_node.signature[-1]+self.index_str()) ]
+
+            to_mantis_node_signature = ( *self.autogen_path_names,
+                            to_mantis_node.signature[-1] + self.index_str() )
+
+            # we need to detect if the next node is in a group.
+            # REMEMBER: at this point, nested groups haven't been solved yet.
+            if to_mantis_node_signature not in frame_mantis_nodes.keys() and \
+                    self.is_node_deeper_nested(to_mantis_node, from_mantis_node):
+                to_mantis_node = frame_mantis_nodes[ (
+                    *self.autogen_path_names, # the SCHEMA_AUTOGENERATED string
+                    to_mantis_node.signature[-2]+ self.index_str() ) ]
+                # this connection was forbidden before, right? so this should be a safe assumption.
+                assert to_mantis_node.node_type == 'DUMMY_SCHEMA', "I expected this to be a group/schema"
+                # we need to get the identifier of the named socket now.
+                to_socket_name = to_mantis_node.prototype.inputs[to_socket_name].identifier
+            else:
+                to_mantis_node = frame_mantis_nodes[ to_mantis_node_signature ] # add the index string
+
             from_socket_name = ui_link.from_socket.name
             from_socket_name = ui_link.from_socket.name
             if from_mantis_node.node_type in ['DUMMY_SCHEMA']:
             if from_mantis_node.node_type in ['DUMMY_SCHEMA']:
                 from_socket_name = ui_link.from_socket.identifier
                 from_socket_name = ui_link.from_socket.identifier
-            connection = from_mantis_node.outputs[from_socket_name].connect(node=to_mantis_node, socket=to_mantis_socket)
+            connection = from_mantis_node.outputs[from_socket_name].connect(node=to_mantis_node, socket=to_socket_name)
             # We want to delete the links from the tree into the schema node.
             # We want to delete the links from the tree into the schema node.
             # TODO: this is not robust enough and I do not feel sure this is doing the right thing.
             # TODO: this is not robust enough and I do not feel sure this is doing the right thing.
             if existing_link := self.incoming_connections[ui_link.to_socket.name]:
             if existing_link := self.incoming_connections[ui_link.to_socket.name]:
@@ -273,7 +292,7 @@ class SchemaSolver:
                         existing_link.die()
                         existing_link.die()
             # BUG may exist here.
             # BUG may exist here.
             self.incoming_connections[ui_link.to_socket.name] = connection
             self.incoming_connections[ui_link.to_socket.name] = connection
-        
+
 
 
     def handle_link_from_constant_input(self, frame_mantis_nodes, ui_link, to_ui_node):
     def handle_link_from_constant_input(self, frame_mantis_nodes, ui_link, to_ui_node):
         incoming = self.constant_in[ui_link.from_socket.name]
         incoming = self.constant_in[ui_link.from_socket.name]
@@ -288,7 +307,7 @@ class SchemaSolver:
             to_socket=ui_link.to_socket.identifier
             to_socket=ui_link.to_socket.identifier
         connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
         connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
         init_connections(from_node)
         init_connections(from_node)
-    
+
     def handle_link_from_array_input_get(self, frame_mantis_nodes, ui_link ):
     def handle_link_from_array_input_get(self, frame_mantis_nodes, ui_link ):
         from_ui_node = ui_link.from_socket.node
         from_ui_node = ui_link.from_socket.node
         from_node = self.schema_nodes[(*self.node.ui_signature, from_ui_node.bl_idname)]
         from_node = self.schema_nodes[(*self.node.ui_signature, from_ui_node.bl_idname)]
@@ -478,7 +497,7 @@ class SchemaSolver:
         # connected up correctly. between the output and the schema's generated nodes.
         # connected up correctly. between the output and the schema's generated nodes.
         # so seek BACK from the output node and grab the from-node that is connected to
         # so seek BACK from the output node and grab the from-node that is connected to
         #  the link. then modify the ui_link to point to that node.
         #  the link. then modify the ui_link to point to that node.
-        from .base_definitions import DummyLink 
+        from .base_definitions import DummyLink
         if not isinstance(ui_link, DummyLink): # make it a Dummy so i can modify it
         if not isinstance(ui_link, DummyLink): # make it a Dummy so i can modify it
             ui_link = DummyLink(ui_link.from_socket, ui_link.to_socket,
             ui_link = DummyLink(ui_link.from_socket, ui_link.to_socket,
                                 multi_input_sort_id=ui_link.multi_input_sort_id)
                                 multi_input_sort_id=ui_link.multi_input_sort_id)
@@ -591,7 +610,7 @@ class SchemaSolver:
             if isinstance(from_ui_node, (SchemaConstInput, NodeGroupInput)):
             if isinstance(from_ui_node, (SchemaConstInput, NodeGroupInput)):
                 if ui_link.from_socket.name in self.constant_in.keys():
                 if ui_link.from_socket.name in self.constant_in.keys():
                     self.handle_link_from_constant_input( frame_mantis_nodes, ui_link, to_ui_node)
                     self.handle_link_from_constant_input( frame_mantis_nodes, ui_link, to_ui_node)
-                continue 
+                continue
             if isinstance(to_ui_node, SchemaArrayInputGet):
             if isinstance(to_ui_node, SchemaArrayInputGet):
                 self.handle_link_to_array_input_get( frame_mantis_nodes, ui_link)
                 self.handle_link_to_array_input_get( frame_mantis_nodes, ui_link)
                 continue
                 continue
@@ -604,16 +623,9 @@ class SchemaSolver:
             # HOLD these links to the next iteration:
             # HOLD these links to the next iteration:
             if isinstance(to_ui_node, SchemaOutgoingConnection):
             if isinstance(to_ui_node, SchemaOutgoingConnection):
                 if isinstance(from_ui_node, (MantisNodeGroup, SchemaGroup)):
                 if isinstance(from_ui_node, (MantisNodeGroup, SchemaGroup)):
-                    e = NotImplementedError(
-                        "You have connected a Node Group or Schema directly into an Outgoing Connection node"
-                        " inside another Schema. This is not currently supported. Try using a Constant Output" \
-                        f" instead. Affected node: {from_ui_node.name}"
-                        )
-                    e = execution_error_cleanup(self.node, e, show_error=self.error_popups)
-                    raise e # always raise this error because it is not implemented.
                     self.handle_link_from_subschema_to_output(frame_mantis_nodes, ui_link, to_ui_node)
                     self.handle_link_from_subschema_to_output(frame_mantis_nodes, ui_link, to_ui_node)
-                self.held_links.append(ui_link)
-                continue 
+                self.held_links.append(ui_link) # is this wise? Why am I doing this?
+                continue
             # HOLD these links until prep is done a little later
             # HOLD these links until prep is done a little later
             if isinstance(to_ui_node, (SchemaConstOutput, NodeGroupOutput)) or isinstance(to_ui_node, SchemaArrayOutput):
             if isinstance(to_ui_node, (SchemaConstOutput, NodeGroupOutput)) or isinstance(to_ui_node, SchemaArrayOutput):
                 if isinstance(from_ui_node, (MantisNodeGroup, SchemaGroup)):
                 if isinstance(from_ui_node, (MantisNodeGroup, SchemaGroup)):
@@ -630,12 +642,12 @@ class SchemaSolver:
             if isinstance(from_ui_node, SchemaArrayInputGet):
             if isinstance(from_ui_node, SchemaArrayInputGet):
                 array_input_get_link.append(ui_link)
                 array_input_get_link.append(ui_link)
                 continue
                 continue
-            
+
             # for any of the special cases, we hit a 'continue' block. So this connection is not special, and is made here.
             # for any of the special cases, we hit a 'continue' block. So this connection is not special, and is made here.
             connection = link_node_containers(self.autogen_path_names, ui_link,
             connection = link_node_containers(self.autogen_path_names, ui_link,
                                 frame_mantis_nodes, from_suffix=self.index_str(),
                                 frame_mantis_nodes, from_suffix=self.index_str(),
                                 to_suffix=self.index_str())
                                 to_suffix=self.index_str())
-        
+
         for signature, node in frame_mantis_nodes.items():
         for signature, node in frame_mantis_nodes.items():
             self.solved_nodes[signature]=node
             self.solved_nodes[signature]=node
             if node.node_type == "DUMMY_SCHEMA":
             if node.node_type == "DUMMY_SCHEMA":
@@ -651,15 +663,15 @@ class SchemaSolver:
                 init_schema_dependencies(node, self.all_nodes)
                 init_schema_dependencies(node, self.all_nodes)
             else:
             else:
                 init_dependencies(node) # it is hard to overstate how important this single line of code is
                 init_dependencies(node) # it is hard to overstate how important this single line of code is
-    
+
         # We have to prepare the nodes leading to Schema Length
         # We have to prepare the nodes leading to Schema Length
         unprepared=deque()
         unprepared=deque()
-        for node in frame_mantis_nodes.values(): 
+        for node in frame_mantis_nodes.values():
             if node.node_type == 'DUMMY_SCHEMA' and (schema_len_in := node.inputs.get("Schema Length")):
             if node.node_type == 'DUMMY_SCHEMA' and (schema_len_in := node.inputs.get("Schema Length")):
                 for l in schema_len_in.links:
                 for l in schema_len_in.links:
                     unprepared.append(l.from_node)
                     unprepared.append(l.from_node)
             self.prepare_nodes(unprepared)
             self.prepare_nodes(unprepared)
-        
+
         # We have to prepare the nodes leading to Array Input Get
         # We have to prepare the nodes leading to Array Input Get
         for ui_link in array_input_get_link:
         for ui_link in array_input_get_link:
             from_name = get_link_in_out(ui_link)[0]
             from_name = get_link_in_out(ui_link)[0]
@@ -706,7 +718,7 @@ class SchemaSolver:
                 else:
                 else:
                     self.handle_link_to_array_output(frame_mantis_nodes, self.index, ui_link, to_ui_node, from_ui_node)
                     self.handle_link_to_array_output(frame_mantis_nodes, self.index, ui_link, to_ui_node, from_ui_node)
         return frame_mantis_nodes
         return frame_mantis_nodes
-    
+
     def solve_nested_schema(self, schema_nc):
     def solve_nested_schema(self, schema_nc):
         """ Solves all schema node groups found in this Schema. This is a recursive function, which will
         """ Solves all schema node groups found in this Schema. This is a recursive function, which will
             solve all levels of nested schema - since this function is called by solver.solve().
             solve all levels of nested schema - since this function is called by solver.solve().
@@ -748,7 +760,7 @@ class SchemaSolver:
                             # we need to kill the link between the Schema itself and the next node and update the deps. Otherwise:confusing bugs.
                             # we need to kill the link between the Schema itself and the next node and update the deps. Otherwise:confusing bugs.
                             outgoing.die(); init_dependencies(to_node)
                             outgoing.die(); init_dependencies(to_node)
                     # else: # the node just isn't connected out this socket.
                     # else: # the node just isn't connected out this socket.
-        
+
 
 
         # # solve all unsolved nested schemas...
         # # solve all unsolved nested schemas...
         for schema_sig, schema_nc in self.nested_schemas.items():
         for schema_sig, schema_nc in self.nested_schemas.items():
@@ -770,18 +782,18 @@ class SchemaSolver:
         for conn in self.array_output_connections.values():
         for conn in self.array_output_connections.values():
             for outgoing in conn:
             for outgoing in conn:
                 all_outgoing_links.append(outgoing)
                 all_outgoing_links.append(outgoing)
-        
+
         for outgoing in all_outgoing_links:
         for outgoing in all_outgoing_links:
             to_node = outgoing.to_node
             to_node = outgoing.to_node
             for l in to_node.inputs[outgoing.to_socket].links:
             for l in to_node.inputs[outgoing.to_socket].links:
                 if self.node == l.from_node:
                 if self.node == l.from_node:
                     l.die()
                     l.die()
-        
+
         for inp in self.node.inputs.values():
         for inp in self.node.inputs.values():
             for l in inp.links:
             for l in inp.links:
                 init_connections(l.from_node) # to force it to have hierarchy connections with the new nodes.
                 init_connections(l.from_node) # to force it to have hierarchy connections with the new nodes.
-                    
-        
+
+
     def solve(self):
     def solve(self):
         if self.solve_length < 1:
         if self.solve_length < 1:
             from .base_definitions import GraphError
             from .base_definitions import GraphError
@@ -802,4 +814,3 @@ class SchemaSolver:
         self.node.prepared = True
         self.node.prepared = True
         prGreen(f"Schema declared {len(self.solved_nodes)} nodes.\n")
         prGreen(f"Schema declared {len(self.solved_nodes)} nodes.\n")
         return self.solved_nodes
         return self.solved_nodes
-