from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange, wrapRed, wrapGreen, wrapPurple, wrapWhite, 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 .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.uuid = self.node.uuid 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.index_link = self.node.inputs['Schema Length'].links[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 = [] 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 else: 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] = [] 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() 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) self.nested_schemas={} self.autogenerated_nodes = {} # this is a bad ugly HACK, but I think I need to mark these and deal with them later # Create the Schema Nodes # prGreen(self.tree_path_names) 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) 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(): if nc.node_type == 'DUMMY_SCHEMA': self.nested_schemas[sig] = nc # 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 def solve_nested_schema(self, schema_nc): 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 length = schema_nc.evaluate_input("Schema Length") tree = np.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) 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)): link = self.held_links.pop() to_np = link.to_socket.node; from_np = link.from_socket.node if isinstance(to_np, SchemaOutgoingConnection): if link.to_socket.name in self.outgoing_connections.keys(): if (outgoing_links := self.outgoing_connections[link.to_socket.name]) is None: continue 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()) ) 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) # else: # the node just isn't connected out this socket. # # solve all unsolved nested schemas... for schema_sig, schema_nc in self.nested_schemas.items(): self.solve_nested_schema(schema_nc) for n in self.autogenerated_nodes.values(): init_connections(n) for c in n.connections: init_dependencies(c) all_outgoing_links = [] for conn in self.outgoing_connections.values(): for outgoing in conn: all_outgoing_links.append(outgoing) for conn in self.constant_out.values(): for outgoing in conn: all_outgoing_links.append(outgoing) for conn in self.array_output_connections.values(): for outgoing in conn: all_outgoing_links.append(outgoing) 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() for inp in self.node.inputs.values(): for l in inp.links: 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() 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.