|
|
@@ -4,220 +4,461 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
|
|
|
wrapOrange,)
|
|
|
from .utilities import init_connections, init_dependencies
|
|
|
from .utilities import class_for_mantis_prototype_node
|
|
|
-from .base_definitions import SchemaUINode, replace_types, custom_props_types
|
|
|
+from .base_definitions import SchemaUINode, GraphError, replace_types, custom_props_types
|
|
|
from .node_container_common import setup_custom_props_from_np
|
|
|
# a class that solves Schema nodes
|
|
|
-from uuid import uuid4
|
|
|
-
|
|
|
-# currently this is fairly ugly and a lot of cooupling but it is at least within the class
|
|
|
-
|
|
|
-# basically tho the idea is to make solving happen one iteration at time so that I can have nested structures
|
|
|
-# ultimately it will be much less messy, because I can simplify it
|
|
|
-# the initializer __init__ does all the necessary initialization
|
|
|
-# then solve_iteration should both solve the iteration and do all the cleanup
|
|
|
-# so the interface should be able to solve_iteration on this node until it is done
|
|
|
-
|
|
|
-# actually.. that wasn't necesary. Maybe the current solution is even... better?
|
|
|
-# I donno. Maybe doing one iteration at a time would be a better way to do things.
|
|
|
-# Now that it works, I can very easily write a second class and replace this one
|
|
|
-
|
|
|
-#example UUID and index
|
|
|
-#9e6df689-3d71-425e-be6c-fe768e7417ec.0000
|
|
|
-
|
|
|
-# def strip_uuid(signature):
|
|
|
-# # prOrange("strip uuid",signature)
|
|
|
-# ret_sig=[]
|
|
|
-# for name in signature:
|
|
|
-# if name is None:
|
|
|
-# ret_sig.append(None)
|
|
|
-# continue # this is normal, first element
|
|
|
-# ret_sig.append(strip_uuid_string(name))
|
|
|
-# return tuple(ret_sig)
|
|
|
-
|
|
|
-# def strip_uuid_string(string):
|
|
|
-# import re
|
|
|
-# split = re.split("\.[a-z,A-Z,0-9]{8}-[a-z,A-Z,0-9]{4}-[a-z,A-Z,0-9]{4}-[a-z,A-Z,0-9]{4}-[a-z,A-Z,0-9]{12}\.[0-9]{4}", string)
|
|
|
-# prRed(string, split)
|
|
|
-# return split[0]
|
|
|
-
|
|
|
-# to get this working with groups.... ensure that group node sockets and such are all working (names, identifiers...)
|
|
|
-# current error seems to occur only when schema are nested
|
|
|
-# error is probably caused by the lack of auto-get nodes for Schema Group inputs that are not connected
|
|
|
-# but it may simply be caused by bad routing
|
|
|
-# both bugs should be fixed (if it is two bugs and not one)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
class SchemaSolver:
|
|
|
def __init__(self, schema_dummy, nodes, prototype, signature=None):
|
|
|
self.all_nodes = nodes # this is the parsed tree from Mantis
|
|
|
self.node = schema_dummy
|
|
|
- # ugly.. but we are getting the Schema node's prototype, then its node tree
|
|
|
- from .utilities import get_node_prototype
|
|
|
- self.tree = prototype.node_tree# get_node_prototype(self.node.signature, self.node.base_tree).node_tree
|
|
|
+ self.tree = prototype.node_tree
|
|
|
self.uuid = self.node.uuid
|
|
|
+ if signature:
|
|
|
+ self.signature = signature
|
|
|
+ else:
|
|
|
+ self.signature = self.node.signature
|
|
|
self.schema_nodes={}
|
|
|
self.solved_nodes = {}
|
|
|
- # self.out_nodes = {}
|
|
|
-
|
|
|
self.incoming_connections = {}
|
|
|
self.outgoing_connections = {}
|
|
|
self.constant_in = {}
|
|
|
self.constant_out = {}
|
|
|
self.array_input_connections = {}
|
|
|
self.array_output_connections = {}
|
|
|
-
|
|
|
- # prGreen(self.node.signature[:-1])
|
|
|
-
|
|
|
- # This singature/natural_signature thing is so, so bad and stupid and wrong... but it works so I won't change it
|
|
|
- if signature:
|
|
|
- self.natural_signature=signature
|
|
|
- # print (signature)
|
|
|
- else:
|
|
|
- self.natural_signature=self.node.signature
|
|
|
-
|
|
|
- self.tree_path_names = [*self.node.signature[:-1]] # same tree as the schema node
|
|
|
- self.autogen_path_names = ['SCHEMA_AUTOGENERATED', *self.node.signature[1:-1]]
|
|
|
+ self.nested_schemas = {}
|
|
|
+ self.autogenerated_nodes = {}
|
|
|
+ self.held_links = []
|
|
|
+ self.tree_path_names = [*self.node.signature] # same tree as the schema node
|
|
|
+ self.autogen_path_names = ['SCHEMA_AUTOGENERATED', *self.node.signature[1:]]
|
|
|
self.index_link = self.node.inputs['Schema Length'].links[0]
|
|
|
+ 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
|
|
|
+ # So
|
|
|
+ self.index = 0
|
|
|
|
|
|
- # TODO UNBREAK ME FIXME NOW
|
|
|
- # identifiers are unfortunately not somethign I can set or predict in the items_tree
|
|
|
- # but I can set them in the node.
|
|
|
- # I either need to set them properly in the node so they always match
|
|
|
- # or: I need to ignore the names and get the identifiers correctly
|
|
|
- # self.node is a NC
|
|
|
-
|
|
|
- # node_identifier_map_in = []
|
|
|
- # node_identifier_map_out = []
|
|
|
+ self.init_schema_links()
|
|
|
+ self.set_index_strings()
|
|
|
|
|
|
+ for ui_node in self.tree.nodes:
|
|
|
+ if isinstance(ui_node, SchemaUINode):
|
|
|
+ # first we need to fill the parameters of the schema nodes.
|
|
|
+ # we use the bl_idname because all schema nodes should be single-instance
|
|
|
+ signature = (*self.tree_path_names, ui_node.bl_idname)
|
|
|
+ # We use the solver's signature here because it represents 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 uuid/index attached.
|
|
|
+ get_sig = (*self.signature, ui_node.bl_idname)
|
|
|
+ if not (mantis_node := self.all_nodes.get(get_sig)): raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
|
|
|
+ self.schema_nodes[signature] = mantis_node
|
|
|
+ mantis_node.fill_parameters(ui_node)
|
|
|
+
|
|
|
+ def set_index_strings(self):
|
|
|
+ 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)
|
|
|
+
|
|
|
+ def init_schema_links(self,):
|
|
|
+ """ Sort and store the links to/from the Schema group node."""
|
|
|
for item in self.tree.interface.items_tree:
|
|
|
if item.item_type == 'PANEL': continue
|
|
|
- if item.parent and item.parent.name == 'Connection':
|
|
|
- if item.in_out == 'INPUT':
|
|
|
- if incoming_links := self.node.inputs[item.identifier].links:
|
|
|
- self.incoming_connections[item.name] = incoming_links[0]
|
|
|
- else:
|
|
|
- self.incoming_connections[item.name] = None # it isn't linked
|
|
|
- # print (self.node)
|
|
|
- # prRed("candidates...", self.node.inputs)
|
|
|
- # for k,v in self.node.inputs.items():
|
|
|
- # print (k,v)
|
|
|
- # print(self.node.outputs)
|
|
|
- # print (self.node.parameters)
|
|
|
- # raise RuntimeError(f"Cannot find incoming connection \"{item.identifier}\" .")
|
|
|
- else:
|
|
|
- if outgoing_links := self.node.outputs[item.identifier].links:
|
|
|
- self.outgoing_connections[item.name] = outgoing_links.copy()
|
|
|
- else:
|
|
|
- self.outgoing_connections[item.name] = []
|
|
|
- if item.parent and item.parent.name == 'Constant':
|
|
|
- if item.in_out == 'INPUT':
|
|
|
- if constant_in_links := self.node.inputs[item.identifier].links:
|
|
|
- self.constant_in[item.name] = constant_in_links[0]
|
|
|
- else:
|
|
|
- self.constant_in[item.name] = None
|
|
|
+ if not item.parent:
|
|
|
+ raise GraphError("ERROR: Schema tree has inputs that are not in categories. This is not supported")
|
|
|
+ match item.parent.name:
|
|
|
+ case 'Connection':
|
|
|
+ if item.in_out == 'INPUT':
|
|
|
+ if incoming_links := self.node.inputs[item.identifier].links:
|
|
|
+ self.incoming_connections[item.name] = incoming_links[0]
|
|
|
+ else:
|
|
|
+ self.incoming_connections[item.name] = None
|
|
|
+ else: # OUTPUT
|
|
|
+ if outgoing_links := self.node.outputs[item.identifier].links:
|
|
|
+ self.outgoing_connections[item.name] = outgoing_links.copy()
|
|
|
+ else:
|
|
|
+ self.outgoing_connections[item.name] = []
|
|
|
+ case 'Constant':
|
|
|
+ if item.in_out == 'INPUT':
|
|
|
+ if constant_in_links := self.node.inputs[item.identifier].links:
|
|
|
+ self.constant_in[item.name] = constant_in_links[0]
|
|
|
+ else:
|
|
|
+ self.constant_in[item.name] = None
|
|
|
+ else: # OUTPUT
|
|
|
+ if constant_out_links := self.node.outputs[item.identifier].links:
|
|
|
+ self.constant_out[item.name] = constant_out_links.copy()
|
|
|
+ else:
|
|
|
+ self.constant_out[item.name] = []
|
|
|
+ case 'Array':
|
|
|
+ if item.in_out == 'INPUT':
|
|
|
+ if item.identifier not in self.array_input_connections.keys():
|
|
|
+ self.array_input_connections[item.identifier]=[]
|
|
|
+ if in_links := self.node.inputs[item.identifier].links:
|
|
|
+ self.array_input_connections[item.identifier]=in_links.copy()
|
|
|
+ else: # OUTPUT
|
|
|
+ if item.identifier not in self.array_output_connections.keys():
|
|
|
+ self.array_output_connections[item.identifier]=[]
|
|
|
+ if out_links := self.node.outputs[item.identifier].links:
|
|
|
+ self.array_output_connections[item.identifier] = out_links.copy()
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
|
|
|
+ for prototype_ui_node in self.tree.nodes:
|
|
|
+ if isinstance(prototype_ui_node, SchemaUINode):
|
|
|
+ continue # IGNORE the schema interface nodes, we already made them in __init__()
|
|
|
+ # they are reused for each iteration.
|
|
|
+ elif prototype_ui_node.bl_idname in ['NodeFrame', 'NodeReroute']:
|
|
|
+ continue # IGNORE stuff that is purely UI - frames, reroutes.
|
|
|
+ signature = (*self.autogen_path_names, prototype_ui_node.name+self.index_str())
|
|
|
+ prototype_mantis_node = self.all_nodes[(*self.signature, prototype_ui_node.name)]
|
|
|
+ # the prototype_mantis_node was generated inside the schema when we parsed the tree.
|
|
|
+ # it is the prototype of the mantis node which we make for this iteration
|
|
|
+ # for Schema sub-nodes ... they need a prototype to init.
|
|
|
+ if prototype_mantis_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
|
|
|
+ from .utilities import get_node_prototype
|
|
|
+ ui_node = get_node_prototype(prototype_mantis_node.signature, prototype_mantis_node.base_tree)
|
|
|
+ if prototype_mantis_node.node_type == 'DUMMY_SCHEMA':
|
|
|
+ mantis_node = prototype_mantis_node.__class__(
|
|
|
+ signature, prototype_mantis_node.base_tree, prototype=ui_node,
|
|
|
+ natural_signature = (*self.node.signature, ui_node.name) )
|
|
|
else:
|
|
|
- if constant_out_links := self.node.outputs[item.identifier].links:
|
|
|
- self.constant_out[item.name] = constant_out_links.copy()
|
|
|
+ mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree, prototype=ui_node)
|
|
|
+ else:
|
|
|
+ mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree)
|
|
|
+ frame_mantis_nodes[mantis_node.signature] = mantis_node
|
|
|
+ if mantis_node.prepared == False:
|
|
|
+ unprepared.append(mantis_node)
|
|
|
+ if mantis_node.__class__.__name__ in custom_props_types:
|
|
|
+ setup_custom_props_from_np(mantis_node, prototype_ui_node)
|
|
|
+ mantis_node.fill_parameters(prototype_ui_node)
|
|
|
+
|
|
|
+
|
|
|
+ def handle_link_from_index_input(self, index, frame_mantis_nodes, ui_link):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ _from_name, to_name = get_link_in_out(ui_link)
|
|
|
+ to_node = frame_mantis_nodes[ (*self.autogen_path_names, to_name+self.index_str()) ]
|
|
|
+ if to_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
|
|
|
+ from .utilities import gen_nc_input_for_data
|
|
|
+ nc_cls = gen_nc_input_for_data(ui_link.from_socket)
|
|
|
+ if (nc_cls): #HACK
|
|
|
+ sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], self.index_str(), ui_link.from_socket.name, ui_link.from_socket.identifier)
|
|
|
+ nc_from = nc_cls(sig, self.node.base_tree)
|
|
|
+ # ugly! maybe even a HACK!
|
|
|
+ nc_from.inputs = {}
|
|
|
+ from .base_definitions import NodeSocket
|
|
|
+ nc_from.outputs = {ui_link.from_socket.name:NodeSocket(name = ui_link.from_socket.name, node=nc_from)}
|
|
|
+ from .base_definitions import get_socket_value
|
|
|
+ nc_from.parameters = {ui_link.from_socket.name:index}
|
|
|
+ frame_mantis_nodes[sig]=nc_from
|
|
|
+ from_node = nc_from
|
|
|
+ self.solved_nodes[sig]=from_node
|
|
|
+ _connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=ui_link.to_socket.identifier)
|
|
|
+ return
|
|
|
+ # 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
|
|
|
+ del to_node.inputs[ui_link.to_socket.name]
|
|
|
+
|
|
|
+ def handle_link_from_schema_length_input(self, frame_mantis_nodes, ui_link):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ # see, here I can just use the schema node
|
|
|
+ _from_name, to_name = get_link_in_out(ui_link)
|
|
|
+ to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
|
|
|
+ # this self.index_link is only used here?
|
|
|
+ if (self.index_link.from_node):
|
|
|
+ connection = self.index_link.from_node.outputs[self.index_link.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
|
|
|
+ # otherwise we can autogen an int input I guess...?
|
|
|
+ else:
|
|
|
+ raise RuntimeError("I was expecting there to be an incoming connection here for Schema Length")
|
|
|
+
|
|
|
+ def handle_link_from_incoming_connection_input(self, frame_mantis_nodes, ui_link):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ incoming = self.incoming_connections[ui_link.from_socket.name]
|
|
|
+ from_node = incoming.from_node
|
|
|
+ _from_name, to_name = get_link_in_out(ui_link)
|
|
|
+ to_node = frame_mantis_nodes[ (*self.autogen_path_names, to_name+self.index_str()) ]
|
|
|
+ connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
|
|
|
+ init_connections(from_node)
|
|
|
+
|
|
|
+ def handle_link_from_constant_input(self, frame_mantis_nodes, ui_link, to_ui_node):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ incoming = self.constant_in[ui_link.from_socket.name]
|
|
|
+ from_node = incoming.from_node
|
|
|
+ to_name = get_link_in_out(ui_link)[1]
|
|
|
+ to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
|
|
|
+ to_socket=ui_link.to_socket.name
|
|
|
+ from .base_definitions import SchemaGroup
|
|
|
+ if isinstance(to_ui_node, SchemaGroup):
|
|
|
+ to_socket=ui_link.to_socket.identifier
|
|
|
+ connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
|
|
|
+ init_connections(from_node)
|
|
|
+
|
|
|
+ def handle_link_to_array_input_get(self, frame_mantis_nodes, ui_link, index):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ from_name, to_name = get_link_in_out(ui_link)
|
|
|
+ from_nc = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str())]
|
|
|
+ to_nc = self.schema_nodes[(*self.tree_path_names, to_name)]
|
|
|
+ # this only needs to be done once:
|
|
|
+ if index == 0: # BUG? HACK? TODO find out what is going on here.
|
|
|
+ # Kill the link between the schema node group and the node connecting to it
|
|
|
+ old_nc = self.all_nodes[(*self.tree_path_names, from_name)]
|
|
|
+ # I am not sure about this!
|
|
|
+ existing_link = old_nc.outputs[ui_link.from_socket.name].links[0]
|
|
|
+ existing_link.die()
|
|
|
+ #
|
|
|
+ connection = from_nc.outputs[ui_link.from_socket.name].connect(node=to_nc, socket=ui_link.to_socket.name)
|
|
|
+
|
|
|
+ def handle_link_from_array_input(self, frame_mantis_nodes, ui_link, index):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ get_index = index
|
|
|
+ try:
|
|
|
+ incoming = self.array_input_connections[ui_link.from_socket.identifier][get_index]
|
|
|
+ except IndexError:
|
|
|
+ if len(self.array_input_connections[ui_link.from_socket.identifier]) > 0:
|
|
|
+ incoming = self.array_input_connections[ui_link.from_socket.identifier][0]
|
|
|
+ # prOrange(incoming.from_node.node_type)
|
|
|
+ if incoming.from_node.node_type not in ['DUMMY_SCHEMA']:
|
|
|
+ raise NotImplementedError(wrapRed("dev: make it so Mantis checks if there are enough Array inputs."))
|
|
|
+ else: # do nothing
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ raise RuntimeError(wrapRed("make it so Mantis checks if there are enough Array inputs!"))
|
|
|
+ to_name = get_link_in_out(ui_link)[1]
|
|
|
+ to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
|
|
|
+ connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
|
|
|
+ init_connections(incoming.from_node)
|
|
|
+
|
|
|
+ def handle_link_to_constant_output(self, frame_mantis_nodes, index, ui_link, to_ui_node):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ to_node = self.schema_nodes[(*self.tree_path_names, to_ui_node.bl_idname)]
|
|
|
+ expose_when = to_node.evaluate_input('Expose when N==')
|
|
|
+ if index == expose_when:
|
|
|
+ for outgoing in self.constant_out[ui_link.to_socket.name]:
|
|
|
+ to_node = outgoing.to_node
|
|
|
+ from_name = get_link_in_out(ui_link)[0]
|
|
|
+ from_node = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str()) ]
|
|
|
+ connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
|
|
|
+
|
|
|
+ # WTF is even happening here?? TODO BUG HACK
|
|
|
+ def handle_link_to_array_output(self, frame_mantis_nodes, index, ui_link, to_ui_node, from_ui_node):# if this duplicated code works, dedupe!
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ to_node = self.schema_nodes[(*self.tree_path_names, to_ui_node.bl_idname)] # get it by [], we want a KeyError if this fails
|
|
|
+ for outgoing in self.array_output_connections[ui_link.to_socket.identifier]:
|
|
|
+ # print (type(outgoing))
|
|
|
+ from .schema_containers import SchemaIndex
|
|
|
+ from_name = get_link_in_out(ui_link)[0]
|
|
|
+ from_node = frame_mantis_nodes[ (*self.autogen_path_names, from_name+self.index_str()) ]
|
|
|
+ if not from_node:
|
|
|
+ from_node = self.schema_nodes[(*self.tree_path_names, from_ui_node.bl_idname)]
|
|
|
+ to_node = outgoing.to_node
|
|
|
+
|
|
|
+ if isinstance(from_node, SchemaIndex): # I think I need to dedup this stuff
|
|
|
+ # print("INDEX")
|
|
|
+ from .utilities import gen_nc_input_for_data
|
|
|
+ nc_cls = gen_nc_input_for_data(ui_link.from_socket)
|
|
|
+ if (nc_cls): #HACK
|
|
|
+ sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], self.index_str(), ui_link.from_socket.name, ui_link.from_socket.identifier)
|
|
|
+ nc_from = nc_cls(sig, self.node.base_tree)
|
|
|
+ # ugly! maybe even a HACK!
|
|
|
+ nc_from.inputs = {}
|
|
|
+ from .node_container_common import NodeSocket
|
|
|
+ nc_from.outputs = {ui_link.from_socket.name:NodeSocket(name = ui_link.from_socket.name, node=nc_from)}
|
|
|
+ from .node_container_common import get_socket_value
|
|
|
+ if ui_link.from_socket.name in ['Index']:
|
|
|
+ nc_from.parameters = {ui_link.from_socket.name:index}
|
|
|
else:
|
|
|
- self.constant_out[item.name] = []
|
|
|
- if item.parent and item.parent.name == 'Array':
|
|
|
- if item.in_out == 'INPUT':
|
|
|
- if item.identifier not in self.array_input_connections.keys():
|
|
|
- self.array_input_connections[item.identifier]=[]
|
|
|
- if in_links := self.node.inputs[item.identifier].links:
|
|
|
- self.array_input_connections[item.identifier]=in_links.copy()
|
|
|
- if item.in_out == 'OUTPUT':
|
|
|
- if item.identifier not in self.array_output_connections.keys():
|
|
|
- self.array_output_connections[item.identifier]=[]
|
|
|
- if out_links := self.node.outputs[item.identifier].links:
|
|
|
- self.array_output_connections[item.identifier] = out_links.copy()
|
|
|
+ nc_from.parameters = {ui_link.from_socket.name:self.solve_length}
|
|
|
+ frame_mantis_nodes[sig]=nc_from
|
|
|
+ from_node = nc_from
|
|
|
+ self.solved_nodes[sig]=from_node
|
|
|
+
|
|
|
+ # I have a feeling that something bad will happen if both of these conditions (above and below) are true
|
|
|
+ if to_node.node_type == 'DUMMY_SCHEMA' and to_node.prepared:
|
|
|
+ other_stem = ('SCHEMA_AUTOGENERATED', *to_node.signature[1:])
|
|
|
+ from .utilities import get_node_prototype
|
|
|
+ other_schema_np = get_node_prototype(to_node.signature, to_node.base_tree)
|
|
|
+ other_schema_tree = other_schema_np.node_tree
|
|
|
+ for n in other_schema_tree.nodes:
|
|
|
+ if n.bl_idname not in ["SchemaArrayInput", "SchemaArrayInputGet"]:
|
|
|
+ continue
|
|
|
+ out = n.outputs[outgoing.to_socket]
|
|
|
+ for l in out.links:
|
|
|
+ other_index_str = lambda : '.'+str(to_node.uuid)+'.'+str(index).zfill(4)
|
|
|
+ # get it by [], we want a KeyError if this fails
|
|
|
+ try:
|
|
|
+ out_node = self.all_nodes[(*other_stem, l.to_node.name+other_index_str())]
|
|
|
+ except KeyError as e:
|
|
|
+ for n in self.all_nodes:
|
|
|
+ if len(n) > len(other_stem)+1: break
|
|
|
+ for elem in other_stem:
|
|
|
+ if elem not in n: break
|
|
|
+ else:
|
|
|
+ print(n)
|
|
|
+ raise e
|
|
|
+ connection = from_node.outputs[ui_link.from_socket.name].connect(node=out_node, socket=l.to_socket.name)
|
|
|
+ else:
|
|
|
+ connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
|
|
|
+
|
|
|
+ def handle_link_from_array_input_get(self, frame_mantis_nodes, index, ui_link, from_ui_node ):
|
|
|
+ from .utilities import get_link_in_out
|
|
|
+ get_index = index
|
|
|
+ from_node = self.schema_nodes[(*self.tree_path_names, from_ui_node.bl_idname)]
|
|
|
+ from .utilities import cap, wrap
|
|
|
+ get_index = from_node.evaluate_input("Index", index)
|
|
|
+ oob = from_node.evaluate_input("OoB Behaviour")
|
|
|
+ # we must assume that the array has sent the correct number of links
|
|
|
+ if oob == 'WRAP':
|
|
|
+ get_index = wrap(get_index, len(self.array_input_connections[ui_link.from_socket.identifier])-1, 0)
|
|
|
+ if oob == 'HOLD':
|
|
|
+ get_index = cap(get_index, len(self.array_input_connections[ui_link.from_socket.identifier])-1)
|
|
|
+ try:
|
|
|
+ incoming = self.array_input_connections[ui_link.from_socket.identifier][get_index]
|
|
|
+ except IndexError:
|
|
|
+ raise RuntimeError(wrapRed("Dummy! You need to make it so Mantis checks if there are enough Array inputs! It should probably have a Get Index!"))
|
|
|
+ to_name = get_link_in_out(ui_link)[1]
|
|
|
+ to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
|
|
|
+ connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
|
|
|
+ init_connections(incoming.from_node)
|
|
|
+
|
|
|
|
|
|
- self.held_links = []
|
|
|
|
|
|
- # just define them for now... we redefine them properly later when they are needed. THis is messy.
|
|
|
- self.index_str = lambda : '.'+str(self.uuid)+'.'+str(0).zfill(4)
|
|
|
- self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(0-1).zfill(4)
|
|
|
+ def solve_iteration(self):
|
|
|
+ """ Solve an iteration of the schema.
|
|
|
+ - 1 Create the Mantis Node instances that represent this iteration of the schema
|
|
|
+ - 2 Connect the links from the entrypoint or previous iteration.
|
|
|
+ - 3 Connect the constant and array links, and any link between nodes entirely within the tree
|
|
|
+ - 4 Prepare the nodes that modify data (in case of e.g. array get index or nested schema length input)
|
|
|
+ - 5 Connect the final prepared nodes
|
|
|
+ and return the nodes that were created in this schema iteration (frame).
|
|
|
+ This function also adds to held_links to pass data between iterations.
|
|
|
+ """
|
|
|
|
|
|
- self.nested_schemas={}
|
|
|
+ from .schema_definitions import (SchemaIndex,
|
|
|
+ SchemaArrayInput,
|
|
|
+ SchemaArrayInputGet,
|
|
|
+ SchemaArrayOutput,
|
|
|
+ SchemaConstInput,
|
|
|
+ SchemaConstOutput,
|
|
|
+ SchemaOutgoingConnection,
|
|
|
+ SchemaIncomingConnection,)
|
|
|
+
|
|
|
+ from .utilities import clear_reroutes
|
|
|
+ from .utilities import get_link_in_out, link_node_containers
|
|
|
|
|
|
- self.autogenerated_nodes = {} # this is a bad ugly HACK, but I think I need to mark these and deal with them later
|
|
|
+ self.set_index_strings()
|
|
|
+ frame_mantis_nodes = {}
|
|
|
|
|
|
- # Create the Schema Nodes
|
|
|
- # prGreen(self.tree_path_names)
|
|
|
+ # Later we have to run bPrepare() on these guys, so we make the deque and fill it now.
|
|
|
+ from collections import deque
|
|
|
+ unprepared= deque()
|
|
|
+ self.gen_solve_iteration_mantis_nodes(frame_mantis_nodes, unprepared)
|
|
|
|
|
|
- for n in self.tree.nodes:
|
|
|
- if isinstance(n, SchemaUINode):
|
|
|
- # first we need to fill the parameters of the schema nodes.
|
|
|
- # the node is actually in the Schema group so we include the schema_dummy name
|
|
|
- # and we use the bl_idname because I think all schema nodes should be single-instance
|
|
|
- signature = (*self.tree_path_names, self.node.signature[-1], n.bl_idname)
|
|
|
- # get_sig = [*self.tree_path_names, strip_uuid_string(self.node.signature[-1]), n.bl_idname]
|
|
|
- # get_sig[0] = None; get_sig = tuple(get_sig) # this is so dumb haha
|
|
|
- get_sig = (*self.natural_signature, n.bl_idname)
|
|
|
-
|
|
|
- if not (nc := self.all_nodes.get(get_sig)): raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
|
|
|
- self.schema_nodes[signature] = nc
|
|
|
- # nc.signature = signature # I don't really intend for this value to be mutable... but... HACK
|
|
|
- # there is no need to mutate this. also I may need to reuse it later
|
|
|
- nc.fill_parameters(n)
|
|
|
- # print (nc)
|
|
|
+ # This is where we handle node connections BETWEEN frames
|
|
|
+ while(self.held_links):
|
|
|
+ ui_link = self.held_links.pop()
|
|
|
+ to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
|
|
|
+ if isinstance(to_ui_node, SchemaOutgoingConnection):
|
|
|
+ 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:
|
|
|
+ to_mantis_node, to_mantis_socket = mantis_link.to_node, mantis_link.to_socket
|
|
|
+ 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()) ]
|
|
|
+ to_mantis_node = frame_mantis_nodes[ (*self.autogen_path_names, to_mantis_node.signature[-1]+self.index_str()) ]
|
|
|
+ connection = from_mantis_node.outputs[ui_link.from_socket.name].connect(node=to_mantis_node, socket=to_mantis_socket)
|
|
|
+ # 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.
|
|
|
+ if existing_link := self.incoming_connections[ui_link.to_socket.name]:
|
|
|
+ if existing_link.to_node == self.node:
|
|
|
+ print ("Deleting...", existing_link)
|
|
|
+ if self.node.signature[-1] in existing_link.to_node.signature:
|
|
|
+ existing_link.die()
|
|
|
+ # BUG may exist here.
|
|
|
+ self.incoming_connections[ui_link.to_socket.name] = connection
|
|
|
+
|
|
|
+ # Get the rerouted links from the graph. We don't really need to do this every iteration.
|
|
|
+ # TODO: use profiling to determine if this is slow; if so: copy & reuse the data, refactor the pop()'s out.
|
|
|
+ ui_links = clear_reroutes(list(self.tree.links))
|
|
|
+
|
|
|
+ # Now we handle ui_links in the current frame, including those ui_links between Schema nodes and "real" nodes
|
|
|
+ awaiting_prep_stage = []
|
|
|
+ for ui_link in ui_links:
|
|
|
+ to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
|
|
|
+ if isinstance(from_ui_node, SchemaIndex):
|
|
|
+ if ui_link.from_socket.name == "Index":
|
|
|
+ self.handle_link_from_index_input(self.index, frame_mantis_nodes, ui_link)
|
|
|
+ elif ui_link.from_socket.name == "Schema Length":
|
|
|
+ self.handle_link_from_schema_length_input(frame_mantis_nodes, ui_link)
|
|
|
+ continue
|
|
|
+ if isinstance(from_ui_node, SchemaIncomingConnection):
|
|
|
+ if ui_link.from_socket.name in self.incoming_connections.keys():
|
|
|
+ self.handle_link_from_incoming_connection_input(frame_mantis_nodes, ui_link)
|
|
|
+ continue
|
|
|
+ if isinstance(from_ui_node, SchemaConstInput):
|
|
|
+ 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)
|
|
|
+ continue
|
|
|
+ if isinstance(to_ui_node, SchemaArrayInputGet):
|
|
|
+ self.handle_link_to_array_input_get( frame_mantis_nodes, ui_link, self.index)
|
|
|
+ continue
|
|
|
+ if isinstance(from_ui_node, SchemaArrayInput):
|
|
|
+ self.handle_link_from_array_input(frame_mantis_nodes, ui_link, self.index)
|
|
|
+ continue
|
|
|
+ # HOLD these links to the next iteration:
|
|
|
+ if isinstance(to_ui_node, SchemaOutgoingConnection):
|
|
|
+ self.held_links.append(ui_link)
|
|
|
+ continue
|
|
|
+ # HOLD these links until prep is done a little later
|
|
|
+ if isinstance(to_ui_node, SchemaConstOutput) or isinstance(to_ui_node, SchemaArrayOutput) or \
|
|
|
+ isinstance(from_ui_node, SchemaArrayInputGet):
|
|
|
+ awaiting_prep_stage.append(ui_link)
|
|
|
+ continue
|
|
|
+ # 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, frame_mantis_nodes, from_suffix=self.index_str(), to_suffix=self.index_str())
|
|
|
+ for k,v in frame_mantis_nodes.items():
|
|
|
+ self.solved_nodes[k]=v
|
|
|
+ init_dependencies(v) # it is hard to overstate how important this single line of code is
|
|
|
|
|
|
-
|
|
|
- def solve(self, schema_length):
|
|
|
- import time
|
|
|
- start_time = time.time()
|
|
|
-
|
|
|
- # from .schema_containers import SchemaIndex
|
|
|
- # for nc in self.schema_nodes.values():
|
|
|
- # if isinstance(nc, SchemaIndex):
|
|
|
- # #HACK? I thought for sure this was being done elsewhere...
|
|
|
- # nc.parameters["Schema Length"]=schema_length
|
|
|
- # prRed(nc.parameters)
|
|
|
- frame_nc={}
|
|
|
-
|
|
|
- for index in range(schema_length):
|
|
|
- frame_nc = self.solve_iteration(index, schema_length)
|
|
|
- for sig, nc in frame_nc.items():
|
|
|
+ # This while-loop is a little scary, but in my testing it has never been a problem.
|
|
|
+ # At this point, we've already run a pretty exhaustive preperation phase to prep the schema's dependencies
|
|
|
+ # So at this point, if this while loop hangs it is because of an error elsewhere.
|
|
|
+ while unprepared:
|
|
|
+ nc = unprepared.pop()
|
|
|
+ if sum([dep.prepared for dep in nc.hierarchy_dependencies]) == len(nc.hierarchy_dependencies):
|
|
|
+ nc.bPrepare()
|
|
|
if nc.node_type == 'DUMMY_SCHEMA':
|
|
|
- self.nested_schemas[sig] = nc
|
|
|
+ self.solve_nested_schema(nc)
|
|
|
+ else:
|
|
|
+ unprepared.appendleft(nc) # just rotate them until they are ready.
|
|
|
|
|
|
- # prRed (self.array_output_connections)
|
|
|
- # for k,v in self.array_output_connections.items():
|
|
|
- # prRed(k,v)
|
|
|
-
|
|
|
- self.finalize(frame_nc)
|
|
|
-
|
|
|
- # prRed (self.array_output_connections)
|
|
|
- # for k,v in self.array_output_connections.items():
|
|
|
- # prRed(k,v)
|
|
|
-
|
|
|
-
|
|
|
- return self.solved_nodes
|
|
|
-
|
|
|
+ while(awaiting_prep_stage):
|
|
|
+ ui_link = awaiting_prep_stage.pop()
|
|
|
+ to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
|
|
|
+ if isinstance(to_ui_node, SchemaConstOutput):
|
|
|
+ self.handle_link_to_constant_output(frame_mantis_nodes, self.index, ui_link, to_ui_node)
|
|
|
+ if isinstance(to_ui_node, SchemaArrayOutput):
|
|
|
+ self.handle_link_to_array_output(frame_mantis_nodes, self.index, ui_link, to_ui_node, from_ui_node)
|
|
|
+ if isinstance(from_ui_node, SchemaArrayInputGet):
|
|
|
+ self.handle_link_from_array_input_get(frame_mantis_nodes, self.index, ui_link, from_ui_node )
|
|
|
+ # end seciton
|
|
|
+ return frame_mantis_nodes
|
|
|
+
|
|
|
def solve_nested_schema(self, schema_nc):
|
|
|
+ """ 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().
|
|
|
+ """
|
|
|
if schema_nc.prepared == False:
|
|
|
all_nodes = self.all_nodes.copy()
|
|
|
- # for k,v in self.solved_nodes.items():
|
|
|
- # all_nodes[k]=v
|
|
|
-
|
|
|
- # from .utilities import get_node_prototype
|
|
|
-
|
|
|
- np = schema_nc.prototype
|
|
|
- # for n in self.node.base_tree.nodes:
|
|
|
- # print (n.name)
|
|
|
- # print (schema_nc.signature[-1])
|
|
|
- from .schema_solve import SchemaSolver
|
|
|
+ ui_node = schema_nc.prototype
|
|
|
length = schema_nc.evaluate_input("Schema Length")
|
|
|
-
|
|
|
- tree = np.node_tree
|
|
|
+ tree = ui_node.node_tree
|
|
|
prOrange(f"Expanding schema {tree.name} in node {schema_nc} with length {length}.")
|
|
|
-
|
|
|
- solver = SchemaSolver(schema_nc, all_nodes, np, schema_nc.natural_signature)
|
|
|
- solved_nodes = solver.solve(length)
|
|
|
+ solver = SchemaSolver(schema_nc, all_nodes, ui_node, schema_nc.natural_signature)
|
|
|
+ solved_nodes = solver.solve()
|
|
|
schema_nc.prepared = True
|
|
|
-
|
|
|
for k,v in solved_nodes.items():
|
|
|
self.solved_nodes[k]=v
|
|
|
|
|
|
+
|
|
|
def finalize(self, frame_nc):
|
|
|
from .schema_definitions import (SchemaOutgoingConnection,)
|
|
|
for i in range(len(self.held_links)):
|
|
|
@@ -229,7 +470,7 @@ class SchemaSolver:
|
|
|
for outgoing in outgoing_links:
|
|
|
if outgoing:
|
|
|
to_node = outgoing.to_node
|
|
|
- from_node =frame_nc.get( (*self.autogen_path_names, from_np.name+self.index_str()) )
|
|
|
+ from_node =frame_nc[(*self.autogen_path_names, from_np.name+self.index_str()) ]
|
|
|
connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
|
|
|
# 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)
|
|
|
@@ -260,8 +501,6 @@ class SchemaSolver:
|
|
|
for outgoing in all_outgoing_links:
|
|
|
to_node = outgoing.to_node
|
|
|
for l in to_node.inputs[outgoing.to_socket].links:
|
|
|
- other = l.to_node
|
|
|
- other_input = l.to_socket
|
|
|
if self.node == l.from_node:
|
|
|
l.die()
|
|
|
|
|
|
@@ -270,385 +509,14 @@ class SchemaSolver:
|
|
|
init_connections(l.from_node) # to force it to have hierarchy connections with the new nodes.
|
|
|
|
|
|
|
|
|
-
|
|
|
- def solve_iteration(self, index, schema_length):
|
|
|
-
|
|
|
- from .schema_definitions import (SchemaIndex,
|
|
|
- SchemaArrayInput,
|
|
|
- SchemaArrayInputGet,
|
|
|
- SchemaArrayOutput,
|
|
|
- SchemaConstInput,
|
|
|
- SchemaConstOutput,
|
|
|
- SchemaOutgoingConnection,
|
|
|
- SchemaIncomingConnection,)
|
|
|
- from bpy.types import (NodeFrame)
|
|
|
-
|
|
|
- from .utilities import clear_reroutes
|
|
|
- from .utilities import get_link_in_out, link_node_containers
|
|
|
-
|
|
|
- # if index_nc:
|
|
|
- # index_nc.parameters['Index']=index
|
|
|
-
|
|
|
- self.index_str = lambda : '.'+str(self.uuid)+'.'+str(index).zfill(4)
|
|
|
- # index_str = str(index).zfill(4)
|
|
|
- self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(index-1).zfill(4)
|
|
|
- frame_nc = {}
|
|
|
- # At this point, GENERATE all the nodes for the frame
|
|
|
- for n in self.tree.nodes:
|
|
|
- if isinstance(n, SchemaUINode) or isinstance(n, NodeFrame):
|
|
|
- continue
|
|
|
- if n.bl_idname in ['NodeReroute']:
|
|
|
- continue
|
|
|
- # this is the N.C. which is a prototype of the NC we actually want to make...
|
|
|
- signature = (*self.autogen_path_names, n.name+self.index_str())
|
|
|
-
|
|
|
- # proto_nc = self.all_nodes.get((*self.tree_path_names, self.node.signature[-1], n.name))
|
|
|
- proto_nc = self.all_nodes.get((*self.natural_signature, n.name))
|
|
|
- # this proto_nc was generated inside the schema when we parsed the tree.
|
|
|
- if not proto_nc:
|
|
|
- raise RuntimeError(f"Node not found: {(*self.tree_path_names, self.node.signature[-1], n.name)}")
|
|
|
- # for Schema sub-nodes ... they need a prototype to init.
|
|
|
- if proto_nc.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
|
|
|
- from .utilities import get_node_prototype
|
|
|
- np = get_node_prototype(proto_nc.signature, proto_nc.base_tree)
|
|
|
- # assert np is not None
|
|
|
- if proto_nc.node_type == 'DUMMY_SCHEMA':
|
|
|
- nat_sig = (*self.node.signature, np.name)
|
|
|
- nc = proto_nc.__class__(signature, proto_nc.base_tree, prototype=np, natural_signature=nat_sig)
|
|
|
- else:
|
|
|
- nc = proto_nc.__class__(signature, proto_nc.base_tree, prototype=np)
|
|
|
- else:
|
|
|
- # try:
|
|
|
- nc = proto_nc.__class__(signature, proto_nc.base_tree)
|
|
|
- # except AttributeError:
|
|
|
- # from .utilities import get_node_prototype
|
|
|
- # np = get_node_prototype(proto_nc.signature, proto_nc.base_tree)
|
|
|
- # nc = proto_nc.__class__(signature, proto_nc.base_tree, prototype=np)
|
|
|
-
|
|
|
- frame_nc[nc.signature] = nc
|
|
|
- #
|
|
|
- if nc.__class__.__name__ in custom_props_types:
|
|
|
- setup_custom_props_from_np(nc, n)
|
|
|
- nc.fill_parameters(n) # this is the best place to do this..
|
|
|
-
|
|
|
-
|
|
|
- # This is where we handle node connections BETWEEN frames
|
|
|
- for i in range(len(self.held_links)):
|
|
|
- link = self.held_links.pop()
|
|
|
- to_np = link.to_socket.node; from_np = link.from_socket.node
|
|
|
- if isinstance(to_np, SchemaOutgoingConnection):
|
|
|
- # incoming connection tells us where to take this.
|
|
|
- incoming_node = self.schema_nodes[*self.tree_path_names, self.node.signature[-1], 'SchemaIncomingConnection']
|
|
|
- for l in incoming_node.outputs[link.to_socket.name].links:
|
|
|
- to_node, to_socket = l.to_node, l.to_socket
|
|
|
- from_name = get_link_in_out(link)[0]
|
|
|
- from_node = self.solved_nodes.get( (*self.autogen_path_names, from_name+self.prev_index_str()) )
|
|
|
- #
|
|
|
- to_node = frame_nc.get( (*self.autogen_path_names, to_node.signature[-1]+self.index_str()) )
|
|
|
- # the to_node and to_socket don't really matter, the incoming connection will handle this part
|
|
|
-
|
|
|
- connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=to_socket)
|
|
|
- if existing_link := self.incoming_connections[link.to_socket.name]:
|
|
|
- # if index == 0:
|
|
|
- if self.node.signature[-1] in existing_link.to_node.signature:
|
|
|
- # not sure this is helping
|
|
|
- existing_link.die()
|
|
|
- # could be better to make it a dummy link?
|
|
|
- self.incoming_connections[link.to_socket.name] = connection
|
|
|
-
|
|
|
- links = clear_reroutes(list(self.tree.links))
|
|
|
-
|
|
|
-
|
|
|
- # frame_nc_with_schema = frame_nc.copy()
|
|
|
- # for k,v in self.schema_nodes.items(): frame_nc_with_schema[k]=v
|
|
|
-
|
|
|
- # Now we handle links in the current frame, including those links between Schema nodes and "real" nodes
|
|
|
- # this gets really complicated :\
|
|
|
- awaiting_prep_stage = []
|
|
|
- for link in links:
|
|
|
- # at THIS POINT I should make a buncha dummy links to deal
|
|
|
- # with the schema node connections...
|
|
|
- to_np = link.to_socket.node; from_np = link.from_socket.node
|
|
|
- if isinstance(to_np, SchemaConstOutput) or isinstance(to_np, SchemaArrayOutput) or \
|
|
|
- isinstance(from_np, SchemaArrayInputGet):# or isinstance(from_np, SchemaArrayInput):
|
|
|
- awaiting_prep_stage.append(link)
|
|
|
- continue
|
|
|
- if isinstance(from_np, SchemaIndex):
|
|
|
- if link.from_socket.name == "Index":
|
|
|
- _from_name, to_name = get_link_in_out(link)
|
|
|
- to_node = frame_nc.get( (*self.autogen_path_names, to_name+self.index_str()) )
|
|
|
-
|
|
|
- if to_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
|
|
|
- # prRed("This is causing the problem, right?")
|
|
|
- from .utilities import gen_nc_input_for_data
|
|
|
- nc_cls = gen_nc_input_for_data(link.from_socket)
|
|
|
- if (nc_cls): #HACK
|
|
|
- sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:], self.index_str(), link.from_socket.name, link.from_socket.identifier)
|
|
|
- nc_from = nc_cls(sig, self.node.base_tree)
|
|
|
- # ugly! maybe even a HACK!
|
|
|
- nc_from.inputs = {}
|
|
|
- from .base_definitions import NodeSocket
|
|
|
- nc_from.outputs = {link.from_socket.name:NodeSocket(name = link.from_socket.name, node=nc_from)}
|
|
|
- from .base_definitions import get_socket_value
|
|
|
- nc_from.parameters = {link.from_socket.name:index}
|
|
|
- frame_nc[sig]=nc_from
|
|
|
- from_node = nc_from
|
|
|
- # self.autogenerated_nodes[sig]=from_node
|
|
|
- self.solved_nodes[sig]=from_node
|
|
|
-
|
|
|
- connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=link.to_socket.identifier)
|
|
|
- # prGreen(connection)
|
|
|
- continue
|
|
|
-
|
|
|
- # this actually seems to work. I could use Autogen nodes
|
|
|
- # but maybe this is actually better since it results in fewer nodes.
|
|
|
- # if this never causes any problems, then I will do it in other places
|
|
|
- # HACK Here Be Danger HACK
|
|
|
- to_node.parameters[link.to_socket.name] = index
|
|
|
- del to_node.inputs[link.to_socket.name]
|
|
|
- # so I have tried to implement this as connections and actually this is the best way because otherwise I have
|
|
|
- # to do something very similar on the input node.
|
|
|
- # HACK
|
|
|
- elif link.from_socket.name == "Schema Length":
|
|
|
- # # see, here I can just use the schema node
|
|
|
- _from_name, to_name = get_link_in_out(link)
|
|
|
- to_node = frame_nc.get( (*self.autogen_path_names, to_name+self.index_str()) )
|
|
|
- # this self.index_link is only used here?
|
|
|
- if (self.index_link.from_node):
|
|
|
- connection = self.index_link.from_node.outputs[self.index_link.from_socket].connect(node=to_node, socket=link.to_socket.name)
|
|
|
- # otherwise we can autogen an int input I guess...?
|
|
|
- else:
|
|
|
- raise RuntimeError("I was expecting there to be an incoming connection here for Schema Length")
|
|
|
- continue
|
|
|
- if isinstance(from_np, SchemaIncomingConnection):
|
|
|
- if link.from_socket.name in self.incoming_connections.keys():
|
|
|
- incoming = self.incoming_connections[link.from_socket.name]
|
|
|
- # if incoming is None:
|
|
|
- # print (link.from_socket.name)
|
|
|
- from_node = incoming.from_node
|
|
|
- _from_name, to_name = get_link_in_out(link)
|
|
|
- to_node = frame_nc.get( (*self.autogen_path_names, to_name+self.index_str()) )
|
|
|
- # try:
|
|
|
- connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=link.to_socket.name)
|
|
|
- # prGreen(connection)
|
|
|
- # except KeyError as e:
|
|
|
- # prRed(f"busted: {from_node}:{incoming.from_socket} --> {to_node}:{link.to_socket.name},{link.to_socket.identifier}")
|
|
|
- # for output in from_node.outputs:
|
|
|
- # prOrange(output)
|
|
|
- # for sinput in from_node.inputs:
|
|
|
- # prPurple(sinput)
|
|
|
- # raise e
|
|
|
- init_connections(from_node)
|
|
|
- continue
|
|
|
- if isinstance(to_np, SchemaOutgoingConnection):
|
|
|
- self.held_links.append(link)
|
|
|
- continue
|
|
|
- if isinstance(from_np, SchemaConstInput):
|
|
|
- if link.from_socket.name in self.constant_in.keys():
|
|
|
- incoming = self.constant_in[link.from_socket.name]
|
|
|
- from_node = incoming.from_node
|
|
|
- to_name = get_link_in_out(link)[1]
|
|
|
- to_node = frame_nc.get( (*self.autogen_path_names, to_name+self.index_str()) )
|
|
|
-
|
|
|
- # print(from_node, incoming.from_socket, "==>", to_node, link.to_socket.identifier)
|
|
|
- # print (to_node.inputs)
|
|
|
- # for k,v in from_node.outputs.items():
|
|
|
- # print (k,v)
|
|
|
- # print (from_node.outputs[incoming.from_socket])
|
|
|
- to_socket=link.to_socket.name
|
|
|
- from .base_definitions import SchemaGroup
|
|
|
- if isinstance(to_np, SchemaGroup):
|
|
|
- to_socket=link.to_socket.identifier
|
|
|
- connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
|
|
|
- init_connections(from_node)
|
|
|
- continue
|
|
|
- if isinstance(to_np, SchemaArrayInputGet):
|
|
|
- from_name, to_name = get_link_in_out(link)
|
|
|
- from_nc = frame_nc.get( (*self.autogen_path_names, from_name+self.index_str()))
|
|
|
- to_nc = self.schema_nodes.get((*self.tree_path_names, self.node.signature[-1], to_name))
|
|
|
- # this only needs to be done once.
|
|
|
- if index == 0:
|
|
|
- old_nc = self.all_nodes.get((*self.tree_path_names, self.node.signature[-1], from_name))
|
|
|
- # I am not sure about this!
|
|
|
- existing_link = old_nc.outputs[link.from_socket.name].links[0]
|
|
|
- existing_link.die()
|
|
|
- #
|
|
|
- connection = from_nc.outputs[link.from_socket.name].connect(node=to_nc, socket=link.to_socket.name)
|
|
|
- continue
|
|
|
- if isinstance(from_np, SchemaArrayInput):
|
|
|
- get_index = index
|
|
|
- try:
|
|
|
- incoming = self.array_input_connections[link.from_socket.identifier][get_index]
|
|
|
- except IndexError:
|
|
|
- if len(self.array_input_connections[link.from_socket.identifier]) > 0:
|
|
|
- incoming = self.array_input_connections[link.from_socket.identifier][0]
|
|
|
- # prOrange(incoming.from_node.node_type)
|
|
|
- if incoming.from_node.node_type not in ['DUMMY_SCHEMA']:
|
|
|
- raise RuntimeError(wrapRed("You need to make it so Mantis checks if there are enough Array inputs."))
|
|
|
- else: # do nothing
|
|
|
- continue
|
|
|
- else:
|
|
|
- raise RuntimeError(wrapRed("make it so Mantis checks if there are enough Array inputs!"))
|
|
|
- to_name = get_link_in_out(link)[1]
|
|
|
- to_node = frame_nc.get((*self.autogen_path_names, to_name+self.index_str()))
|
|
|
- connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=link.to_socket.name)
|
|
|
- init_connections(incoming.from_node)
|
|
|
- continue
|
|
|
- link_path_names = self.tree_path_names[1:]
|
|
|
- # use this line to debug, usually I don't need it
|
|
|
- connection = link_node_containers(self.autogen_path_names, link, frame_nc, from_suffix=self.index_str(), to_suffix=self.index_str())
|
|
|
- for k,v in frame_nc.items():
|
|
|
- self.solved_nodes[k]=v
|
|
|
- init_dependencies(v) # it is hard to overstate how important this single line of code is
|
|
|
- # for node in v.hierarchy_dependencies:
|
|
|
- # init_connections(node)
|
|
|
-
|
|
|
- # done with the link handling.
|
|
|
-
|
|
|
-
|
|
|
- # Start Section: This is the place where we solve dependencies and continue
|
|
|
- # we need to sort the nodes in solved_nc and prepare whatever can be preepared.
|
|
|
- from collections import deque
|
|
|
- unprepared= deque()
|
|
|
- for nc in frame_nc.values():
|
|
|
- if nc.prepared == False:
|
|
|
- unprepared.append(nc)
|
|
|
- # this is potentially a little inneficient but it isn't a big deal.
|
|
|
- # since the only extra preparations I am doing is nodes that don't yet have outgoing connections
|
|
|
- # but I may add them in the next step anyways, then I need to prepare them! so this is simpler.
|
|
|
- # at this point it should be possible to sort unprepared to avoid the while loop
|
|
|
- # but I don't really care. this will work since we have guarenteed solved all the
|
|
|
- # schema_dummy's dependencies.... I hope. lol.
|
|
|
-
|
|
|
- while unprepared: # DANGER.
|
|
|
- # raise NotImplementedError
|
|
|
- # prRed("this part!")
|
|
|
- nc = unprepared.pop()
|
|
|
- # print(nc)
|
|
|
- if sum([dep.prepared for dep in nc.hierarchy_dependencies]) == len(nc.hierarchy_dependencies):
|
|
|
- nc.bPrepare()
|
|
|
+ def solve(self):
|
|
|
+ for index in range(self.solve_length):
|
|
|
+ self.index = index
|
|
|
+ frame_mantis_nodes = self.solve_iteration()
|
|
|
+ for sig, nc in frame_mantis_nodes.items():
|
|
|
if nc.node_type == 'DUMMY_SCHEMA':
|
|
|
- self.solve_nested_schema(nc)
|
|
|
-
|
|
|
- else:
|
|
|
- # print (sum([dep.prepared for dep in nc.hierarchy_dependencies]), len(nc.hierarchy_dependencies))
|
|
|
- # for dep in nc.hierarchy_dependencies:
|
|
|
- # if not dep.prepared:
|
|
|
- # prOrange(dep)
|
|
|
- unprepared.appendleft(nc)
|
|
|
-
|
|
|
- for i in range(len(awaiting_prep_stage)): #why is this a for loop and not a while loop?? # FIXME?
|
|
|
- link = awaiting_prep_stage.pop()
|
|
|
- to_np = link.to_socket.node; from_np = link.from_socket.node
|
|
|
- if isinstance(to_np, SchemaConstOutput):
|
|
|
- to_node = self.schema_nodes.get((*self.tree_path_names, self.node.signature[-1], to_np.bl_idname))
|
|
|
- expose_when = to_node.evaluate_input('Expose when N==')
|
|
|
- if index == expose_when:
|
|
|
- for outgoing in self.constant_out[link.to_socket.name]:
|
|
|
- to_node = outgoing.to_node
|
|
|
- from_name = get_link_in_out(link)[0]
|
|
|
- from_node = frame_nc.get( (*self.autogen_path_names, from_name+self.index_str()) )
|
|
|
- connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
|
|
|
- if isinstance(to_np, SchemaArrayOutput): # if this duplicated code works, dedupe!
|
|
|
- to_node = self.schema_nodes.get((*self.tree_path_names, self.node.signature[-1], to_np.bl_idname))
|
|
|
- for outgoing in self.array_output_connections[link.to_socket.identifier]:
|
|
|
- # print (type(outgoing))
|
|
|
- from .schema_containers import SchemaIndex
|
|
|
- from_name = get_link_in_out(link)[0]
|
|
|
- from_node = frame_nc.get( (*self.autogen_path_names, from_name+self.index_str()) )
|
|
|
- if not from_node:
|
|
|
- from_node = self.schema_nodes.get( (*self.tree_path_names, self.node.signature[-1], from_np.bl_idname) )
|
|
|
- if not from_node:
|
|
|
- raise RuntimeError()
|
|
|
- to_node = outgoing.to_node
|
|
|
-
|
|
|
- if isinstance(from_node, SchemaIndex): # I think I need to dedup this stuff
|
|
|
- # print("INDEX")
|
|
|
- from .utilities import gen_nc_input_for_data
|
|
|
- nc_cls = gen_nc_input_for_data(link.from_socket)
|
|
|
- if (nc_cls): #HACK
|
|
|
- sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:], self.index_str(), link.from_socket.name, link.from_socket.identifier)
|
|
|
- nc_from = nc_cls(sig, self.node.base_tree)
|
|
|
- # ugly! maybe even a HACK!
|
|
|
- nc_from.inputs = {}
|
|
|
- from .node_container_common import NodeSocket
|
|
|
- nc_from.outputs = {link.from_socket.name:NodeSocket(name = link.from_socket.name, node=nc_from)}
|
|
|
- from .node_container_common import get_socket_value
|
|
|
- if link.from_socket.name in ['Index']:
|
|
|
- nc_from.parameters = {link.from_socket.name:index}
|
|
|
- else:
|
|
|
- nc_from.parameters = {link.from_socket.name:schema_length}
|
|
|
- frame_nc[sig]=nc_from
|
|
|
- from_node = nc_from
|
|
|
- self.solved_nodes[sig]=from_node
|
|
|
-
|
|
|
- # I have a feeling that something bad will happen if both of these conditions (above and below) are true
|
|
|
- if to_node.node_type == 'DUMMY_SCHEMA' and to_node.prepared:
|
|
|
- other_stem = ('SCHEMA_AUTOGENERATED', *to_node.signature[1:-1])
|
|
|
- from .utilities import get_node_prototype
|
|
|
- other_schema_np = get_node_prototype(to_node.signature, to_node.base_tree)
|
|
|
- other_schema_tree = other_schema_np.node_tree
|
|
|
- for n in other_schema_tree.nodes:
|
|
|
- if n.bl_idname not in ["SchemaArrayInput", "SchemaArrayInputGet"]:
|
|
|
- continue
|
|
|
- out = n.outputs[outgoing.to_socket]
|
|
|
- for l in out.links:
|
|
|
- other_index_str = lambda : '.'+str(to_node.uuid)+'.'+str(index).zfill(4)
|
|
|
- out_node = self.all_nodes.get((*other_stem, l.to_node.name+other_index_str()))
|
|
|
- connection = from_node.outputs[link.from_socket.name].connect(node=out_node, socket=l.to_socket.name)
|
|
|
- else:
|
|
|
- connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
|
|
|
-
|
|
|
-
|
|
|
- if isinstance(from_np, SchemaArrayInputGet): # or isinstance(from_np, SchemaArrayInput) or
|
|
|
- get_index = index
|
|
|
- if isinstance(from_np, SchemaArrayInputGet):
|
|
|
- from_node = self.schema_nodes.get((*self.tree_path_names, self.node.signature[-1], from_np.bl_idname))
|
|
|
- from .utilities import cap, wrap
|
|
|
- get_index = from_node.evaluate_input("Index", index)
|
|
|
- oob = from_node.evaluate_input("OoB Behaviour")
|
|
|
- # we must assume that the array has sent the correct number of links
|
|
|
- if oob == 'WRAP':
|
|
|
- get_index = wrap(get_index, len(self.array_input_connections[link.from_socket.identifier])-1, 0)
|
|
|
- if oob == 'HOLD':
|
|
|
- get_index = cap(get_index, len(self.array_input_connections[link.from_socket.identifier])-1)
|
|
|
- try:
|
|
|
- incoming = self.array_input_connections[link.from_socket.identifier][get_index]
|
|
|
- except IndexError:
|
|
|
- raise RuntimeError(wrapRed("Dummy! You need to make it so Mantis checks if there are enough Array inputs! It should probably have a Get Index!"))
|
|
|
- to_name = get_link_in_out(link)[1]
|
|
|
- to_node = frame_nc.get((*self.autogen_path_names, to_name+self.index_str()))
|
|
|
- connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=link.to_socket.name)
|
|
|
- init_connections(incoming.from_node)
|
|
|
- # end seciton
|
|
|
- return frame_nc
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-# TODO: figure out why the tree is sorting wrong when using arrays!
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-# despite a lot of ugly hacks, things are going well
|
|
|
-# current TODO:
|
|
|
-# - get nested schema working when nodes in the parent tree depend on them
|
|
|
-# - eventually I can clean all this up by re-designing the solver code to solve one iteration at a time instead of all at once
|
|
|
-
|
|
|
-# - get schema-in-group working
|
|
|
-# - groups need constants and arrays
|
|
|
-# maybe the simplest solution is for groups to be schema always
|
|
|
-# but I don't want to do that
|
|
|
-
|
|
|
-# anyways the next milestone is for the spine to be a group or schema output
|
|
|
-
|
|
|
-# note that schemas will send out arrays and such. So I don't necessarily want groups to have array in/out
|
|
|
-# instead, groups are simple
|
|
|
-# they can contain schema, but schema can't have arrays from the group interface
|
|
|
-# groups are good for large, abstract components
|
|
|
-# fundamentally they are similar but groups are cleaner since they don't require dependency solving
|
|
|
-
|
|
|
-# anyways... schema with nesting and groups is good enough for now
|
|
|
-# I don't need it to be stable. I just need to rig my human model with Mantis and iterate from there. Getting it to rig a human character will find and fix most bugs.
|
|
|
+ self.nested_schemas[sig] = nc
|
|
|
+ self.finalize(frame_mantis_nodes)
|
|
|
+ self.node.solver = self
|
|
|
+ return self.solved_nodes
|
|
|
+
|