schema_solve.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. from .utilities import (prRed, prGreen, prPurple, prWhite,
  2. prOrange,
  3. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  4. wrapOrange,)
  5. from .utilities import init_connections, init_dependencies
  6. from .utilities import class_for_mantis_prototype_node
  7. from .base_definitions import SchemaUINode, GraphError, replace_types, custom_props_types
  8. from .node_container_common import setup_custom_props_from_np
  9. # a class that solves Schema nodes
  10. class SchemaSolver:
  11. def __init__(self, schema_dummy, nodes, prototype, signature=None,):
  12. self.all_nodes = nodes # this is the parsed tree from Mantis
  13. self.node = schema_dummy
  14. self.tree = prototype.node_tree
  15. self.uuid = self.node.uuid
  16. if signature:
  17. self.signature = signature
  18. else:
  19. self.signature = self.node.signature
  20. self.schema_nodes={}
  21. self.solved_nodes = {}
  22. self.incoming_connections = {}
  23. self.outgoing_connections = {}
  24. self.constant_in = {}
  25. self.constant_out = {}
  26. self.array_input_connections = {}
  27. self.array_output_connections = {}
  28. self.nested_schemas = {}
  29. self.autogenerated_nodes = {}
  30. self.held_links = []
  31. self.tree_path_names = [*self.node.signature] # same tree as the schema node
  32. self.autogen_path_names = ['SCHEMA_AUTOGENERATED', *self.node.signature[1:]]
  33. self.index_link = self.node.inputs['Schema Length'].links[0]
  34. self.solve_length = self.node.evaluate_input("Schema Length")
  35. # I'm making this a property of the solver because the solver's data is modified as it solves each iteration
  36. self.index = 0
  37. self.init_schema_links()
  38. self.set_index_strings()
  39. # Sort the multi-input nodes in reverse order of ID, this ensures that they are
  40. # read in the order they were created
  41. for inp in self.node.inputs.values():
  42. inp.links.sort(key=lambda a : -a.multi_input_sort_id)
  43. for ui_node in self.tree.nodes:
  44. if isinstance(ui_node, SchemaUINode):
  45. # first we need to fill the parameters of the schema nodes.
  46. # we use the bl_idname because all schema nodes should be single-instance
  47. signature = (*self.tree_path_names, ui_node.bl_idname)
  48. # We use the solver's signature here because it represents the "original" signature of the schema UI group node
  49. # since this schema solver may be in a nested schema, and its node's signature may have uuid/index attached.
  50. get_sig = (*self.signature, ui_node.bl_idname)
  51. if not (mantis_node := self.all_nodes.get(get_sig)): raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
  52. self.schema_nodes[signature] = mantis_node
  53. mantis_node.fill_parameters(ui_node)
  54. def set_index_strings(self):
  55. self.index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index).zfill(4)
  56. self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index-1).zfill(4)
  57. def init_schema_links(self,):
  58. """ Sort and store the links to/from the Schema group node."""
  59. for item in self.tree.interface.items_tree:
  60. if item.item_type == 'PANEL': continue
  61. if not item.parent:
  62. raise GraphError("ERROR: Schema tree has inputs that are not in categories. This is not supported")
  63. match item.parent.name:
  64. case 'Connection':
  65. if item.in_out == 'INPUT':
  66. if incoming_links := self.node.inputs[item.identifier].links:
  67. self.incoming_connections[item.name] = incoming_links[0]
  68. else:
  69. self.incoming_connections[item.name] = None
  70. else: # OUTPUT
  71. if outgoing_links := self.node.outputs[item.identifier].links:
  72. self.outgoing_connections[item.name] = outgoing_links.copy()
  73. else:
  74. self.outgoing_connections[item.name] = []
  75. case 'Constant':
  76. if item.in_out == 'INPUT':
  77. if constant_in_links := self.node.inputs[item.identifier].links:
  78. self.constant_in[item.name] = constant_in_links[0]
  79. else:
  80. self.constant_in[item.name] = None
  81. else: # OUTPUT
  82. if constant_out_links := self.node.outputs[item.identifier].links:
  83. self.constant_out[item.name] = constant_out_links.copy()
  84. else:
  85. self.constant_out[item.name] = []
  86. case 'Array':
  87. if item.in_out == 'INPUT':
  88. if item.identifier not in self.array_input_connections.keys():
  89. self.array_input_connections[item.identifier]=[]
  90. if in_links := self.node.inputs[item.identifier].links:
  91. self.array_input_connections[item.identifier]=in_links.copy()
  92. else: # OUTPUT
  93. if item.identifier not in self.array_output_connections.keys():
  94. self.array_output_connections[item.identifier]=[]
  95. if out_links := self.node.outputs[item.identifier].links:
  96. self.array_output_connections[item.identifier] = out_links.copy()
  97. def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
  98. for prototype_ui_node in self.tree.nodes:
  99. if isinstance(prototype_ui_node, SchemaUINode):
  100. continue # IGNORE the schema interface nodes, we already made them in __init__()
  101. # they are reused for each iteration.
  102. elif prototype_ui_node.bl_idname in ['NodeFrame', 'NodeReroute']:
  103. continue # IGNORE stuff that is purely UI - frames, reroutes.
  104. signature = (*self.autogen_path_names, prototype_ui_node.name+self.index_str())
  105. prototype_mantis_node = self.all_nodes[(*self.signature, prototype_ui_node.name)]
  106. # the prototype_mantis_node was generated inside the schema when we parsed the tree.
  107. # it is the prototype of the mantis node which we make for this iteration
  108. # for Schema sub-nodes ... they need a prototype to init.
  109. if prototype_mantis_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
  110. from .utilities import get_node_prototype
  111. ui_node = get_node_prototype(prototype_mantis_node.signature, prototype_mantis_node.base_tree)
  112. if prototype_mantis_node.node_type == 'DUMMY_SCHEMA':
  113. mantis_node = prototype_mantis_node.__class__(
  114. signature, prototype_mantis_node.base_tree, prototype=ui_node,
  115. natural_signature = (*self.node.signature, ui_node.name) )
  116. else:
  117. mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree, prototype=ui_node)
  118. else:
  119. mantis_node = prototype_mantis_node.__class__(signature, prototype_mantis_node.base_tree)
  120. frame_mantis_nodes[mantis_node.signature] = mantis_node
  121. if mantis_node.prepared == False:
  122. unprepared.append(mantis_node)
  123. if mantis_node.__class__.__name__ in custom_props_types:
  124. setup_custom_props_from_np(mantis_node, prototype_ui_node)
  125. mantis_node.fill_parameters(prototype_ui_node)
  126. def handle_link_from_index_input(self, index, frame_mantis_nodes, ui_link):
  127. from .utilities import get_link_in_out
  128. _from_name, to_name = get_link_in_out(ui_link)
  129. to_node = frame_mantis_nodes[ (*self.autogen_path_names, to_name+self.index_str()) ]
  130. if to_node.node_type in ['DUMMY', 'DUMMY_SCHEMA']:
  131. from .utilities import gen_nc_input_for_data
  132. nc_cls = gen_nc_input_for_data(ui_link.from_socket)
  133. if (nc_cls): #HACK
  134. sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], self.index_str(), ui_link.from_socket.name, ui_link.from_socket.identifier)
  135. nc_from = nc_cls(sig, self.node.base_tree)
  136. # ugly! maybe even a HACK!
  137. nc_from.inputs = {}
  138. from .base_definitions import NodeSocket
  139. nc_from.outputs = {ui_link.from_socket.name:NodeSocket(name = ui_link.from_socket.name, node=nc_from)}
  140. from .base_definitions import get_socket_value
  141. nc_from.parameters = {ui_link.from_socket.name:index}
  142. frame_mantis_nodes[sig]=nc_from
  143. from_node = nc_from
  144. self.solved_nodes[sig]=from_node
  145. _connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=ui_link.to_socket.identifier)
  146. return
  147. # Since the index is already determined, it is safe to remove the socket and just keep the value.
  148. to_node.parameters[ui_link.to_socket.name] = index
  149. del to_node.inputs[ui_link.to_socket.name]
  150. def handle_link_from_schema_length_input(self, frame_mantis_nodes, ui_link):
  151. from .utilities import get_link_in_out
  152. # see, here I can just use the schema node
  153. _from_name, to_name = get_link_in_out(ui_link)
  154. to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
  155. # this self.index_link is only used here?
  156. if (self.index_link.from_node):
  157. connection = self.index_link.from_node.outputs[self.index_link.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
  158. # otherwise we can autogen an int input I guess...?
  159. else:
  160. raise RuntimeError("I was expecting there to be an incoming connection here for Schema Length")
  161. def handle_link_from_incoming_connection_input(self, frame_mantis_nodes, ui_link):
  162. from .utilities import get_link_in_out
  163. incoming = self.incoming_connections[ui_link.from_socket.name]
  164. from_node = incoming.from_node
  165. _from_name, to_name = get_link_in_out(ui_link)
  166. to_node = frame_mantis_nodes[ (*self.autogen_path_names, to_name+self.index_str()) ]
  167. connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
  168. init_connections(from_node)
  169. def handle_link_from_constant_input(self, frame_mantis_nodes, ui_link, to_ui_node):
  170. from .utilities import get_link_in_out
  171. incoming = self.constant_in[ui_link.from_socket.name]
  172. from_node = incoming.from_node
  173. to_name = get_link_in_out(ui_link)[1]
  174. to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
  175. to_socket=ui_link.to_socket.name
  176. from .base_definitions import SchemaGroup
  177. if isinstance(to_ui_node, SchemaGroup):
  178. to_socket=ui_link.to_socket.identifier
  179. connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
  180. init_connections(from_node)
  181. def handle_link_to_array_input_get(self, frame_mantis_nodes, ui_link, index):
  182. from .utilities import get_link_in_out
  183. from_name, to_name = get_link_in_out(ui_link)
  184. from_nc = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str())]
  185. to_nc = self.schema_nodes[(*self.tree_path_names, to_name)]
  186. # this only needs to be done once:
  187. if index == 0: # BUG? HACK? TODO find out what is going on here.
  188. # Kill the link between the schema node group and the node connecting to it
  189. old_nc = self.all_nodes[(*self.tree_path_names, from_name)]
  190. # I am not sure about this!
  191. existing_link = old_nc.outputs[ui_link.from_socket.name].links[0]
  192. existing_link.die()
  193. #
  194. connection = from_nc.outputs[ui_link.from_socket.name].connect(node=to_nc, socket=ui_link.to_socket.name)
  195. def handle_link_from_array_input(self, frame_mantis_nodes, ui_link, index):
  196. from .utilities import get_link_in_out
  197. get_index = index
  198. try:
  199. incoming = self.array_input_connections[ui_link.from_socket.identifier][get_index]
  200. except IndexError:
  201. if len(self.array_input_connections[ui_link.from_socket.identifier]) > 0:
  202. incoming = self.array_input_connections[ui_link.from_socket.identifier][0]
  203. # prOrange(incoming.from_node.node_type)
  204. if incoming.from_node.node_type not in ['DUMMY_SCHEMA']:
  205. raise NotImplementedError(wrapRed("dev: make it so Mantis checks if there are enough Array inputs."))
  206. else: # do nothing
  207. return
  208. else:
  209. raise RuntimeError(wrapRed("make it so Mantis checks if there are enough Array inputs!"))
  210. to_name = get_link_in_out(ui_link)[1]
  211. to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
  212. connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
  213. init_connections(incoming.from_node)
  214. def handle_link_to_constant_output(self, frame_mantis_nodes, index, ui_link, to_ui_node):
  215. from .utilities import get_link_in_out
  216. to_node = self.schema_nodes[(*self.tree_path_names, to_ui_node.bl_idname)]
  217. expose_when = to_node.evaluate_input('Expose when N==')
  218. if index == expose_when:
  219. for outgoing in self.constant_out[ui_link.to_socket.name]:
  220. to_node = outgoing.to_node
  221. from_name = get_link_in_out(ui_link)[0]
  222. from_node = frame_mantis_nodes[(*self.autogen_path_names, from_name+self.index_str()) ]
  223. connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
  224. # WTF is even happening here?? TODO BUG HACK
  225. 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!
  226. from .utilities import get_link_in_out
  227. to_node = self.schema_nodes[(*self.tree_path_names, to_ui_node.bl_idname)] # get it by [], we want a KeyError if this fails
  228. for outgoing in self.array_output_connections[ui_link.to_socket.identifier]:
  229. # print (type(outgoing))
  230. from .schema_containers import SchemaIndex
  231. from_name = get_link_in_out(ui_link)[0]
  232. from_node = frame_mantis_nodes[ (*self.autogen_path_names, from_name+self.index_str()) ]
  233. if not from_node:
  234. from_node = self.schema_nodes[(*self.tree_path_names, from_ui_node.bl_idname)]
  235. to_node = outgoing.to_node
  236. if isinstance(from_node, SchemaIndex): # I think I need to dedup this stuff
  237. # print("INDEX")
  238. from .utilities import gen_nc_input_for_data
  239. nc_cls = gen_nc_input_for_data(ui_link.from_socket)
  240. if (nc_cls): #HACK
  241. sig = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], self.index_str(), ui_link.from_socket.name, ui_link.from_socket.identifier)
  242. nc_from = nc_cls(sig, self.node.base_tree)
  243. # ugly! maybe even a HACK!
  244. nc_from.inputs = {}
  245. from .node_container_common import NodeSocket
  246. nc_from.outputs = {ui_link.from_socket.name:NodeSocket(name = ui_link.from_socket.name, node=nc_from)}
  247. from .node_container_common import get_socket_value
  248. if ui_link.from_socket.name in ['Index']:
  249. nc_from.parameters = {ui_link.from_socket.name:index}
  250. else:
  251. nc_from.parameters = {ui_link.from_socket.name:self.solve_length}
  252. frame_mantis_nodes[sig]=nc_from
  253. from_node = nc_from
  254. self.solved_nodes[sig]=from_node
  255. # I have a feeling that something bad will happen if both of these conditions (above and below) are true
  256. if to_node.node_type == 'DUMMY_SCHEMA' and to_node.prepared:
  257. other_stem = ('SCHEMA_AUTOGENERATED', *to_node.signature[1:])
  258. from .utilities import get_node_prototype
  259. other_schema_np = get_node_prototype(to_node.signature, to_node.base_tree)
  260. other_schema_tree = other_schema_np.node_tree
  261. for n in other_schema_tree.nodes:
  262. if n.bl_idname not in ["SchemaArrayInput", "SchemaArrayInputGet"]:
  263. continue
  264. out = n.outputs[outgoing.to_socket]
  265. for l in out.links:
  266. other_index_str = lambda : '.'+str(to_node.uuid)+'.'+str(index).zfill(4)
  267. # get it by [], we want a KeyError if this fails
  268. try:
  269. out_node = self.all_nodes[(*other_stem, l.to_node.name+other_index_str())]
  270. except KeyError as e:
  271. for n in self.all_nodes:
  272. if len(n) > len(other_stem)+1: break
  273. for elem in other_stem:
  274. if elem not in n: break
  275. else:
  276. print(n)
  277. raise e
  278. connection = from_node.outputs[ui_link.from_socket.name].connect(node=out_node, socket=l.to_socket.name)
  279. else:
  280. connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
  281. def handle_link_from_array_input_get(self, frame_mantis_nodes, index, ui_link, from_ui_node ):
  282. from .utilities import get_link_in_out
  283. get_index = index
  284. from_node = self.schema_nodes[(*self.tree_path_names, from_ui_node.bl_idname)]
  285. from .utilities import cap, wrap
  286. get_index = from_node.evaluate_input("Index", index)
  287. oob = from_node.evaluate_input("OoB Behaviour")
  288. # we must assume that the array has sent the correct number of links
  289. if oob == 'WRAP':
  290. get_index = wrap(get_index, len(self.array_input_connections[ui_link.from_socket.identifier])-1, 0)
  291. if oob == 'HOLD':
  292. get_index = cap(get_index, len(self.array_input_connections[ui_link.from_socket.identifier])-1)
  293. try:
  294. incoming = self.array_input_connections[ui_link.from_socket.identifier][get_index]
  295. except IndexError:
  296. 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!"))
  297. to_name = get_link_in_out(ui_link)[1]
  298. to_node = frame_mantis_nodes[(*self.autogen_path_names, to_name+self.index_str())]
  299. connection = incoming.from_node.outputs[incoming.from_socket].connect(node=to_node, socket=ui_link.to_socket.name)
  300. init_connections(incoming.from_node)
  301. def prepare_nodes(self, unprepared):
  302. # At this point, we've already run a pretty exhaustive preperation phase to prep the schema's dependencies
  303. # So we should not need to add any new dependencies unless there is a bug elsewhere.
  304. # and in fact, I could skip this in some cases, and should investigate if profiling reveals a slowdown here.
  305. while unprepared:
  306. nc = unprepared.pop()
  307. if sum([dep.prepared for dep in nc.hierarchy_dependencies]) == len(nc.hierarchy_dependencies):
  308. nc.bPrepare()
  309. if nc.node_type == 'DUMMY_SCHEMA':
  310. self.solve_nested_schema(nc)
  311. else: # Keeping this for-loop as a fallback, it should never add dependencies though
  312. for dep in nc.hierarchy_dependencies:
  313. if not dep.prepared and dep not in unprepared:
  314. prOrange(f"Adding dependency... {dep}")
  315. unprepared.appendleft(dep)
  316. unprepared.appendleft(nc) # just rotate them until they are ready.
  317. def solve_iteration(self):
  318. """ Solve an iteration of the schema.
  319. - 1 Create the Mantis Node instances that represent this iteration of the schema
  320. - 2 Connect the links from the entrypoint or previous iteration.
  321. - 3 Connect the constant and array links, and any link between nodes entirely within the tree
  322. - 4 Prepare the nodes that modify data (in case of e.g. array get index or nested schema length input)
  323. - 5 Connect the final prepared nodes
  324. and return the nodes that were created in this schema iteration (frame).
  325. This function also adds to held_links to pass data between iterations.
  326. """
  327. from .schema_definitions import (SchemaIndex,
  328. SchemaArrayInput,
  329. SchemaArrayInputGet,
  330. SchemaArrayOutput,
  331. SchemaConstInput,
  332. SchemaConstOutput,
  333. SchemaOutgoingConnection,
  334. SchemaIncomingConnection,)
  335. from .utilities import clear_reroutes
  336. from .utilities import get_link_in_out, link_node_containers
  337. self.set_index_strings()
  338. frame_mantis_nodes = {}
  339. # Later we have to run bPrepare() on these guys, so we make the deque and fill it now.
  340. from collections import deque
  341. unprepared= deque()
  342. self.gen_solve_iteration_mantis_nodes(frame_mantis_nodes, unprepared)
  343. # This is where we handle node connections BETWEEN frames
  344. while(self.held_links):
  345. ui_link = self.held_links.pop()
  346. to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
  347. if isinstance(to_ui_node, SchemaOutgoingConnection):
  348. mantis_incoming_node = self.schema_nodes[*self.tree_path_names, 'SchemaIncomingConnection']
  349. for mantis_link in mantis_incoming_node.outputs[ui_link.to_socket.name].links:
  350. to_mantis_node, to_mantis_socket = mantis_link.to_node, mantis_link.to_socket
  351. from_name = get_link_in_out(ui_link)[0]
  352. from_mantis_node = self.solved_nodes[ (*self.autogen_path_names, from_name+self.prev_index_str()) ]
  353. to_mantis_node = frame_mantis_nodes[ (*self.autogen_path_names, to_mantis_node.signature[-1]+self.index_str()) ]
  354. connection = from_mantis_node.outputs[ui_link.from_socket.name].connect(node=to_mantis_node, socket=to_mantis_socket)
  355. # We want to delete the links from the tree into the schema node.
  356. # TODO: this is not robust enough and I do not feel sure this is doing the right thing.
  357. if existing_link := self.incoming_connections[ui_link.to_socket.name]:
  358. if existing_link.to_node == self.node:
  359. print ("Deleting...", existing_link)
  360. if self.node.signature[-1] in existing_link.to_node.signature:
  361. existing_link.die()
  362. # BUG may exist here.
  363. self.incoming_connections[ui_link.to_socket.name] = connection
  364. # Get the rerouted links from the graph. We don't really need to do this every iteration.
  365. # TODO: use profiling to determine if this is slow; if so: copy & reuse the data, refactor the pop()'s out.
  366. ui_links = clear_reroutes(list(self.tree.links))
  367. # Now we handle ui_links in the current frame, including those ui_links between Schema nodes and "real" nodes
  368. awaiting_prep_stage = []
  369. for ui_link in ui_links:
  370. to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
  371. if isinstance(from_ui_node, SchemaIndex):
  372. if ui_link.from_socket.name == "Index":
  373. self.handle_link_from_index_input(self.index, frame_mantis_nodes, ui_link)
  374. elif ui_link.from_socket.name == "Schema Length":
  375. self.handle_link_from_schema_length_input(frame_mantis_nodes, ui_link)
  376. continue
  377. if isinstance(from_ui_node, SchemaIncomingConnection):
  378. if ui_link.from_socket.name in self.incoming_connections.keys():
  379. self.handle_link_from_incoming_connection_input(frame_mantis_nodes, ui_link)
  380. continue
  381. if isinstance(from_ui_node, SchemaConstInput):
  382. if ui_link.from_socket.name in self.constant_in.keys():
  383. self.handle_link_from_constant_input( frame_mantis_nodes, ui_link, to_ui_node)
  384. continue
  385. if isinstance(to_ui_node, SchemaArrayInputGet):
  386. self.handle_link_to_array_input_get( frame_mantis_nodes, ui_link, self.index)
  387. continue
  388. if isinstance(from_ui_node, SchemaArrayInput):
  389. self.handle_link_from_array_input(frame_mantis_nodes, ui_link, self.index)
  390. continue
  391. # HOLD these links to the next iteration:
  392. if isinstance(to_ui_node, SchemaOutgoingConnection):
  393. self.held_links.append(ui_link)
  394. continue
  395. # HOLD these links until prep is done a little later
  396. if isinstance(to_ui_node, SchemaConstOutput) or isinstance(to_ui_node, SchemaArrayOutput) or \
  397. isinstance(from_ui_node, SchemaArrayInputGet):
  398. awaiting_prep_stage.append(ui_link)
  399. continue
  400. # for any of the special cases, we hit a 'continue' block. So this connection is not special, and is made here.
  401. connection = link_node_containers(self.autogen_path_names, ui_link, frame_mantis_nodes, from_suffix=self.index_str(), to_suffix=self.index_str())
  402. for k,v in frame_mantis_nodes.items():
  403. self.solved_nodes[k]=v
  404. init_dependencies(v) # it is hard to overstate how important this single line of code is
  405. self.prepare_nodes(unprepared)
  406. while(awaiting_prep_stage):
  407. ui_link = awaiting_prep_stage.pop()
  408. to_ui_node = ui_link.to_socket.node; from_ui_node = ui_link.from_socket.node
  409. if isinstance(to_ui_node, SchemaConstOutput):
  410. self.handle_link_to_constant_output(frame_mantis_nodes, self.index, ui_link, to_ui_node)
  411. if isinstance(to_ui_node, SchemaArrayOutput):
  412. self.handle_link_to_array_output(frame_mantis_nodes, self.index, ui_link, to_ui_node, from_ui_node)
  413. if isinstance(from_ui_node, SchemaArrayInputGet):
  414. self.handle_link_from_array_input_get(frame_mantis_nodes, self.index, ui_link, from_ui_node )
  415. # end seciton
  416. return frame_mantis_nodes
  417. def solve_nested_schema(self, schema_nc):
  418. """ Solves all schema node groups found in this Schema. This is a recursive function, which will
  419. solve all levels of nested schema - since this function is called by solver.solve().
  420. """
  421. if schema_nc.prepared == False:
  422. all_nodes = self.all_nodes.copy()
  423. ui_node = schema_nc.prototype
  424. length = schema_nc.evaluate_input("Schema Length")
  425. tree = ui_node.node_tree
  426. prOrange(f"Expanding schema {tree.name} in node {schema_nc} with length {length}.")
  427. solver = SchemaSolver(schema_nc, all_nodes, ui_node, schema_nc.natural_signature)
  428. solved_nodes = solver.solve()
  429. schema_nc.prepared = True
  430. for k,v in solved_nodes.items():
  431. self.solved_nodes[k]=v
  432. def finalize(self, frame_nc):
  433. from .schema_definitions import (SchemaOutgoingConnection,)
  434. for i in range(len(self.held_links)):
  435. link = self.held_links.pop()
  436. to_np = link.to_socket.node; from_np = link.from_socket.node
  437. if isinstance(to_np, SchemaOutgoingConnection):
  438. if link.to_socket.name in self.outgoing_connections.keys():
  439. if (outgoing_links := self.outgoing_connections[link.to_socket.name]) is None: continue
  440. for outgoing in outgoing_links:
  441. if outgoing:
  442. to_node = outgoing.to_node
  443. from_node =frame_nc[(*self.autogen_path_names, from_np.name+self.index_str()) ]
  444. connection = from_node.outputs[link.from_socket.name].connect(node=to_node, socket=outgoing.to_socket)
  445. # we need to kill the link between the Schema itself and the next node and update the deps. Otherwise:confusing bugs.
  446. outgoing.die(); init_dependencies(to_node)
  447. # else: # the node just isn't connected out this socket.
  448. # # solve all unsolved nested schemas...
  449. for schema_sig, schema_nc in self.nested_schemas.items():
  450. self.solve_nested_schema(schema_nc)
  451. for n in self.autogenerated_nodes.values():
  452. init_connections(n)
  453. for c in n.connections:
  454. init_dependencies(c)
  455. all_outgoing_links = []
  456. for conn in self.outgoing_connections.values():
  457. for outgoing in conn:
  458. all_outgoing_links.append(outgoing)
  459. for conn in self.constant_out.values():
  460. for outgoing in conn:
  461. all_outgoing_links.append(outgoing)
  462. for conn in self.array_output_connections.values():
  463. for outgoing in conn:
  464. all_outgoing_links.append(outgoing)
  465. for outgoing in all_outgoing_links:
  466. to_node = outgoing.to_node
  467. for l in to_node.inputs[outgoing.to_socket].links:
  468. if self.node == l.from_node:
  469. l.die()
  470. for inp in self.node.inputs.values():
  471. for l in inp.links:
  472. init_connections(l.from_node) # to force it to have hierarchy connections with the new nodes.
  473. def solve(self):
  474. for index in range(self.solve_length):
  475. self.index = index
  476. frame_mantis_nodes = self.solve_iteration()
  477. for sig, nc in frame_mantis_nodes.items():
  478. if nc.node_type == 'DUMMY_SCHEMA':
  479. self.nested_schemas[sig] = nc
  480. self.finalize(frame_mantis_nodes)
  481. self.node.solver = self
  482. self.node.prepared = True
  483. return self.solved_nodes