schema_solve.py 30 KB

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