readtree.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
  2. wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
  3. from .utilities import get_node_prototype, class_for_mantis_prototype_node, \
  4. gen_nc_input_for_data
  5. # BAD NAMES ahead, as these have nothing to do with NodeReroute nodes.
  6. def reroute_common(nc, nc_to, all_nc):
  7. # we need to do this: go to the to-node
  8. # then reroute the link in the to_node all the way to the beginning
  9. # so that the number of links in "real" nodes is unchanged
  10. # then the links in the dummy nodes need to be deleted
  11. # watch=False
  12. # if nc.signature[-1] == 'NodeGroupOutput': watch=True
  13. for inp_name, inp in nc.inputs.items():
  14. # assume each input socket only has one input for now
  15. if inp.is_connected:
  16. while (inp.links):
  17. in_link = inp.links.pop()
  18. from_nc = in_link.from_node
  19. from_socket = in_link.from_socket
  20. links = []
  21. from_links = from_nc.outputs[from_socket].links.copy()
  22. while(from_links): # This is a weird way to do this HACK
  23. from_link = from_links.pop()
  24. if from_link == in_link:
  25. from_link.die()
  26. continue # DELETE the dummy node link
  27. links.append(from_link)
  28. from_nc.outputs[from_socket].links = links
  29. down = nc_to.outputs[inp_name]
  30. for downlink in down.links:
  31. downlink.from_node = from_nc
  32. downlink.from_socket = from_socket
  33. from_nc.outputs[from_socket].links.append(downlink)
  34. if hasattr(downlink.to_node, "reroute_links"):
  35. # Recurse!
  36. downlink.to_node.reroute_links(downlink.to_node, all_nc)
  37. in_link.die()
  38. def reroute_links_grp(nc, all_nc):
  39. if (nc_to := all_nc.get( ( *nc.signature, "NodeGroupInput") )):
  40. reroute_common(nc, nc_to, all_nc)
  41. else:
  42. raise RuntimeError("Cannot read graph for some goshblamed son of a reason")
  43. def reroute_links_grpout(nc, all_nc):
  44. if (nc_to := all_nc.get( ( *nc.signature[:-1],) )):
  45. reroute_common(nc, nc_to, all_nc)
  46. else:
  47. raise RuntimeError("error leaving a node group (maybe you are running the tree from inside a node group?)... TODO: this should still work")
  48. def reroute_links_grpin(nc, all_nc):
  49. pass
  50. # FIXME I don't think these signatures are unique.
  51. def insert_lazy_parents(nc):
  52. from .link_containers import LinkInherit
  53. from .xForm_containers import xFormRoot, xFormArmature
  54. from .node_container_common import NodeLink
  55. inherit_nc = None
  56. if nc.inputs["Relationship"].is_connected:
  57. link = nc.inputs["Relationship"].links[0]
  58. # print(nc)
  59. from_nc = link.from_node
  60. if isinstance(from_nc, xFormRoot):
  61. return
  62. if from_nc.node_type in ["XFORM"] and link.from_socket in ["xForm Out"]:
  63. inherit_nc = LinkInherit(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_INHERIT"), nc.base_tree)
  64. for from_link in from_nc.outputs["xForm Out"].links:
  65. if from_link.to_node == nc and from_link.to_socket == "Relationship":
  66. break # this is it
  67. from_link.to_node = inherit_nc; from_link.to_socket="Parent"
  68. links=[]
  69. while (nc.inputs["Relationship"].links):
  70. to_link = nc.inputs["Relationship"].links.pop()
  71. if to_link.from_node == from_nc and to_link.from_socket == "xForm Out":
  72. continue # don't keep this one
  73. links.append(to_link)
  74. nc.inputs["Relationship"].links=links
  75. link=NodeLink(from_node=inherit_nc, from_socket="Inheritance", to_node=nc, to_socket="Relationship")
  76. inherit_nc.inputs["Parent"].links.append(from_link)
  77. inherit_nc.parameters = {
  78. "Parent":None,
  79. "Inherit Rotation":True,
  80. "Inherit Scale":'FULL',
  81. "Connected":False,
  82. }
  83. # because the from node may have already been done.
  84. init_connections(from_nc)
  85. init_dependencies(from_nc)
  86. init_connections(inherit_nc)
  87. init_dependencies(inherit_nc)
  88. # and the inherit node never was
  89. # this doesn't seem to be necessary or helpful.
  90. # elif isinstance(nc, xFormArmature):
  91. # inherit_nc = xFormRoot(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_ROOT"), nc.base_tree)
  92. # init_connections(inherit_nc) # we need to do this here because parse_tree expects a link not a xForm
  93. return inherit_nc
  94. from_name_filter = ["Driver", ]
  95. to_name_filter = [
  96. "Custom Object xForm Override",
  97. "Custom Object",
  98. "Deform Bones"
  99. ]
  100. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  101. # DATA FROM NODES #
  102. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  103. from .base_definitions import replace_types
  104. # TODO: investigate whether I can set the properties in the downstream nodes directly.
  105. # I am doing this in Schema Solver and it seems to work quite efficiently.
  106. def make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, np):
  107. from .node_container_common import NodeSocket
  108. nc_to = local_nc[(None, *tree_path_names, np.name)]
  109. for inp in np.inputs:
  110. nc_from = None
  111. if inp.bl_idname in ['WildcardSocket']:
  112. continue # it isn't a real input so I don't think it is good to check it.
  113. to_s = inp.identifier
  114. if not inp.is_linked: # make an autogenerated NC for the inputs of the group node
  115. from .node_container_common import get_socket_value
  116. nc_cls = gen_nc_input_for_data(inp)
  117. if (nc_cls):
  118. sig = ("MANTIS_AUTOGENERATED", *tree_path_names, np.name, inp.name, inp.identifier)
  119. nc_from = nc_cls(sig, base_tree)
  120. # ugly! maybe even a HACK!
  121. nc_from.inputs = {}
  122. nc_from.outputs = {inp.name:NodeSocket(name = inp.name, node=nc_from)}
  123. nc_from.parameters = {inp.name:get_socket_value(inp)}
  124. #
  125. local_nc[sig] = nc_from; all_nc[sig] = nc_from
  126. from_s = inp.name
  127. else: # should this be an error instead?
  128. prRed("No available auto-generated class for input", *tree_path_names, np.name, inp.name)
  129. nc_from.outputs[from_s].connect(node=nc_to, socket=to_s, sort_id=0)
  130. def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_nc, dummy_nodes, group_nodes, schema_nodes ):
  131. from .internal_containers import DummyNode
  132. from .base_definitions import SchemaNode
  133. for np in current_tree.nodes:
  134. # TODO: find out why I had to add this in. these should be taken care of already? BUG
  135. if np.bl_idname in ["NodeFrame", "NodeReroute"]:
  136. continue # not a Mantis Node
  137. if (nc_cls := class_for_mantis_prototype_node(np)):
  138. sig = (None, *tree_path_names, np.name)
  139. # but I will probably choose to handle this elsewhere
  140. # if isinstance(np, SchemaNode):
  141. # continue # we won't do this one here.
  142. if np.bl_idname in replace_types:
  143. # prPurple(np.bl_idname)
  144. sig = (None, *tree_path_names, np.bl_idname)
  145. if local_nc.get(sig):
  146. continue # already made
  147. nc = nc_cls( sig , base_tree)
  148. local_nc[sig] = nc; all_nc[sig] = nc
  149. # if np.bl_idname in ['UtilityMatricesFromCurve', 'UtilityBreakArray']:
  150. # schema_nodes[sig]=nc
  151. elif np.bl_idname in ["NodeGroupInput", "NodeGroupOutput"]: # make a Dummy Node
  152. # we only want ONE dummy in/out per tree_path, so use the bl_idname
  153. sig = (None, *tree_path_names, np.bl_idname)
  154. if not local_nc.get(sig):
  155. nc = DummyNode( signature=sig , base_tree=base_tree, prototype=np )
  156. local_nc[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
  157. if np.bl_idname in ["NodeGroupOutput"]:
  158. nc.reroute_links = reroute_links_grpout
  159. if np.bl_idname in ["NodeGroupInput"]:
  160. nc.reroute_links = reroute_links_grpin
  161. # else:
  162. # nc = local_nc.get(sig)
  163. elif np.bl_idname in ["MantisNodeGroup", "MantisSchemaGroup"]:
  164. nc = DummyNode( signature= (sig := (None, *tree_path_names, np.name) ), base_tree=base_tree, prototype=np )
  165. local_nc[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
  166. make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, np)
  167. if np.bl_idname == "MantisNodeGroup":
  168. group_nodes.append(nc)
  169. nc.reroute_links = reroute_links_grp
  170. else:
  171. group_nodes.append(nc)
  172. schema_nodes[sig] = nc
  173. else:
  174. nc = None
  175. prRed(f"Can't make nc for.. {np.bl_idname}")
  176. # this should be done at init
  177. if nc.signature[0] not in ['MANTIS_AUTOGENERATED'] and nc.node_type not in ['SCHEMA', 'DUMMY', 'DUMMY_SCHEMA']:
  178. nc.fill_parameters()
  179. def data_from_tree(base_tree, tree_path, dummy_nodes, all_nc, all_schema):
  180. # TODO: it should be realtively easy to make this use a while loop instead of recursion.
  181. local_nc, group_nodes = {}, []
  182. tree_path_names = [tree.name for tree in tree_path if hasattr(tree, "name")]
  183. if tree_path[-1]:
  184. current_tree = tree_path[-1].node_tree
  185. else:
  186. current_tree = base_tree
  187. #
  188. from .utilities import clear_reroutes
  189. links = clear_reroutes(list(current_tree.links))
  190. gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_nc, dummy_nodes, group_nodes, all_schema)
  191. from .utilities import link_node_containers
  192. for link in links:
  193. link_node_containers((None, *tree_path_names), link, local_nc)
  194. # Now, descend into the Node Groups and recurse
  195. for nc in group_nodes:
  196. # ng = get_node_prototype(nc.signature, base_tree)
  197. data_from_tree(base_tree, tree_path+[nc.prototype], dummy_nodes, all_nc, all_schema)
  198. return dummy_nodes, all_nc, all_schema
  199. from .utilities import check_and_add_root, init_connections, init_dependencies, init_schema_dependencies
  200. def delete_nc(nc):
  201. return
  202. # this doesn't seem to work actually
  203. for socket in nc.inputs.values():
  204. for l in socket.links:
  205. if l is not None:
  206. l.__del__()
  207. for socket in nc.outputs.values():
  208. for l in socket.links:
  209. if l is not None:
  210. l.__del__()
  211. def is_signature_in_other_signature(sig_a, sig_b):
  212. # this is the easiest but not the best way to do this:
  213. # this function is hideous but it does not seem to have any significant effect on timing
  214. # tested it with profiling on a full character rig.
  215. sig_a = list(sig_a)
  216. sig_a = ['MANTIS_NONE' if val is None else val for val in sig_a]
  217. sig_b = list(sig_b)
  218. sig_b = ['MANTIS_NONE' if val is None else val for val in sig_b]
  219. string_a = "".join(sig_a)
  220. string_b = "".join(sig_b)
  221. return string_a in string_b
  222. def solve_schema_to_tree(nc, all_nc, roots=[]):
  223. from .utilities import get_node_prototype
  224. np = get_node_prototype(nc.signature, nc.base_tree)
  225. # if not hasattr(np, 'node_tree'):
  226. # nc.bPrepare()
  227. # nc.prepared=True
  228. # return {}
  229. from .schema_solve import SchemaSolver
  230. length = nc.evaluate_input("Schema Length")
  231. tree = np.node_tree
  232. prOrange(f"Expanding schema {tree.name} in node {nc} with length {length}.")
  233. solver = SchemaSolver(nc, all_nc, np)
  234. solved_nodes = solver.solve(length)
  235. # prGreen(f"Finished solving schema {tree.name} in node {nc}.")
  236. prWhite(f"Schema declared {len(solved_nodes)} nodes.")
  237. nc.prepared = True
  238. # TODO this should be handled by the schema's finalize() function
  239. del_me = []
  240. for k, v in all_nc.items():
  241. # delete all the schema's internal nodes. The links have already been deleted by the solver.
  242. if v.signature[0] not in ['MANTIS_AUTOGENERATED'] and is_signature_in_other_signature(nc.signature, k):
  243. # print (wrapOrange("Culling: ")+wrapRed(v))
  244. delete_nc(v)
  245. del_me.append(k)
  246. for k in del_me:
  247. del all_nc[k]
  248. for k,v in solved_nodes.items():
  249. all_nc[k]=v
  250. init_connections(v)
  251. check_and_add_root(v, roots, include_non_hierarchy=True)
  252. # end TODO
  253. return solved_nodes
  254. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  255. # PARSE NODE TREE #
  256. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  257. from .utilities import get_all_dependencies
  258. def get_schema_length_dependencies(node):
  259. """ Find all of the nodes that the Schema Length input depends on. """
  260. # the deps recursively from the from_nodes connected to Schema Length
  261. deps = []
  262. # return get_all_dependencies(node)
  263. inp = node.inputs.get("Schema Length")
  264. if not inp:
  265. inp = node.inputs.get("Array")
  266. # this way we can handle Schema and Array Get nodes with one function
  267. # ... since I may add more in the future this is not a robust solution HACK
  268. for l in inp.links:
  269. deps.extend(get_all_dependencies(l.from_node))
  270. if inp := node.inputs.get("Index"):
  271. for l in inp.links:
  272. deps.extend(get_all_dependencies(l.from_node))
  273. # now get the auto-generated simple inputs. These should not really be there but I haven't figured out how to set things directly yet lol
  274. for inp in node.inputs.values():
  275. for l in inp.links:
  276. if "MANTIS_AUTOGENERATED" in l.from_node.signature:
  277. # l.from_node.bPrepare() # try this...
  278. # l.from_node.prepared = True; l.from_node.executed = True
  279. deps.extend([l.from_node]) # why we need this lol
  280. return deps
  281. def parse_tree(base_tree):
  282. from uuid import uuid4 # do this here?
  283. base_tree.execution_id = uuid4().__str__() # set this, it may be used by nodes during execution
  284. # annoyingly I have to pass in values for all of the dicts because if I initialize them in the function call
  285. # then they stick around because the function definition inits them once and keeps a reference
  286. # so instead I have to supply them to avoid ugly code or bugs elsewhere
  287. # it's REALLY confusing when you run into this sort of problem. So it warrants four entire lines of comments!
  288. import time
  289. data_start_time = time.time()
  290. dummy_nodes, all_nc, all_schema = data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_nc = {}, all_schema={})
  291. # return
  292. prGreen(f"Pulling data from tree took {time.time() - data_start_time} seconds")
  293. for sig, dummy in dummy_nodes.items():
  294. if (hasattr(dummy, "reroute_links")):
  295. dummy.reroute_links(dummy, all_nc)
  296. # TODO
  297. # MODIFY BELOW to use hierarchy_dependencies instead
  298. # SCHEMA DUMMY nodes will need to gather the hierarchy and non-hierarchy dependencies
  299. # so SCHEMA DUMMY will not make their dependencies all hierarchy
  300. # since they will need to be able to send drivers and such
  301. start_time = time.time()
  302. sig_check = (None, 'Node Group.001', 'switch_thigh')
  303. roots = []
  304. arrays = []
  305. from .misc_containers import UtilityArrayGet
  306. for nc in all_nc.values():
  307. # clean up the groups
  308. if nc.node_type in ["DUMMY"]:
  309. if nc.prototype.bl_idname in ("MantisNodeGroup", "NodeGroupOutput"):
  310. continue
  311. from .base_definitions import from_name_filter, to_name_filter
  312. init_dependencies(nc)
  313. init_connections(nc)
  314. check_and_add_root(nc, roots, include_non_hierarchy=True)
  315. if isinstance(nc, UtilityArrayGet):
  316. arrays.append(nc)
  317. from collections import deque
  318. unsolved_schema = deque()
  319. solve_only_these = []; solve_only_these.extend(list(all_schema.values()))
  320. for schema in all_schema.values():
  321. # so basically we need to check every parent node if it is a schema
  322. # this is a fairly slapdash solution but it works and I won't change it
  323. for i in range(len(schema.signature)-1): # -1, we don't want to check this node, obviously
  324. if parent := all_schema.get(schema.signature[:i+1]):
  325. solve_only_these.remove(schema)
  326. break
  327. else:
  328. init_schema_dependencies(schema, all_nc)
  329. solve_only_these.extend(get_schema_length_dependencies(schema))
  330. unsolved_schema.append(schema)
  331. for array in arrays:
  332. solve_only_these.extend(get_schema_length_dependencies(array))
  333. solve_only_these.extend(arrays)
  334. schema_solve_done = set()
  335. solve_only_these = set(solve_only_these)
  336. solve_layer = unsolved_schema.copy(); solve_layer.extend(roots)
  337. while(solve_layer):
  338. n = solve_layer.pop()
  339. if n not in solve_only_these: # removes the unneeded node from the solve-layer
  340. continue
  341. if n.signature in all_schema.keys():
  342. for dep in n.hierarchy_dependencies:
  343. if dep not in schema_solve_done and (dep in solve_only_these):
  344. solve_layer.appendleft(n)
  345. break
  346. else:
  347. solved_nodes = solve_schema_to_tree(n, all_nc, roots)
  348. unsolved_schema.remove(n)
  349. schema_solve_done.add(n)
  350. for node in solved_nodes.values():
  351. #
  352. init_dependencies(node)
  353. init_connections(node)
  354. #
  355. solve_layer.appendleft(node)
  356. for conn in n.hierarchy_connections:
  357. if conn not in schema_solve_done and conn not in solve_layer:
  358. solve_layer.appendleft(conn)
  359. else:
  360. for dep in n.hierarchy_dependencies:
  361. if dep not in schema_solve_done:
  362. break
  363. else:
  364. n.bPrepare()
  365. schema_solve_done.add(n)
  366. for conn in n.hierarchy_connections:
  367. if conn not in schema_solve_done and conn not in solve_layer:
  368. solve_layer.appendleft(conn)
  369. if unsolved_schema:
  370. raise RuntimeError("Failed to resolve all schema declarations")
  371. # I had a problem with this looping forever. I think it is resolved... but I don't know lol
  372. all_nc = list(all_nc.values()).copy()
  373. kept_nc = {}
  374. while (all_nc):
  375. nc = all_nc.pop()
  376. if nc in arrays:
  377. continue
  378. if nc.node_type in ["DUMMY"]:
  379. if nc.prototype.bl_idname in ["MantisNodeGroup", "NodeGroupOutput"]:
  380. continue
  381. # continue
  382. # cleanup autogen nodes
  383. if nc.signature[0] == "MANTIS_AUTOGENERATED" and len(nc.inputs) == 0 and len(nc.outputs) == 1:
  384. output=list(nc.outputs.values())[0]
  385. value=list(nc.parameters.values())[0] # TODO modify the dependecy get function to exclude these nodes completely
  386. for l in output.links:
  387. to_node = l.to_node; to_socket = l.to_socket
  388. l.die()
  389. to_node.parameters[to_socket] = value
  390. del to_node.inputs[to_socket]
  391. init_dependencies(to_node)
  392. # init_connections(from_node)
  393. # it seems safe, and more importantly, fast, not to update the dependencies of these nodes.
  394. continue # in my test case this reduced the time cost by 33% by removing a large number of root nodes.
  395. # it went from 18.9 seconds to 9-10 seconds
  396. if (nc.node_type in ['XFORM']) and ("Relationship" in nc.inputs.keys()):
  397. if (new_nc := insert_lazy_parents(nc)):
  398. kept_nc[new_nc.signature]=new_nc
  399. kept_nc[nc.signature]=nc
  400. prWhite(f"Parsing tree took {time.time()-start_time} seconds.")
  401. prWhite("Number of Nodes: %s" % (len(kept_nc)))
  402. return kept_nc
  403. def sort_tree_into_layers(nodes, context):
  404. from time import time
  405. from .node_container_common import (get_depth_lines,
  406. node_depth)
  407. # All this function needs to do is sort out the hierarchy and
  408. # get things working in order of their dependencies.
  409. roots, drivers = [], []
  410. start = time()
  411. for n in nodes.values():
  412. if n.node_type == 'DRIVER': drivers.append(n)
  413. # ugly but necesary to ensure that drivers are always connected.
  414. check_and_add_root(n, roots)
  415. layers, nodes_heights = {}, {}
  416. #Possible improvement: unify roots if they represent the same data
  417. all_sorted_nodes = []
  418. for root in roots:
  419. nodes_heights[root.signature] = 0
  420. depth_lines = get_depth_lines(root)[0]
  421. for n in nodes.values():
  422. if n.signature not in (depth_lines.keys()):
  423. continue #belongs to a different root
  424. d = nodes_heights.get(n.signature, 0)
  425. if (new_d := node_depth(depth_lines[n.signature])) > d:
  426. d = new_d
  427. nodes_heights[n.signature] = d
  428. for k, v in nodes_heights.items():
  429. if (layer := layers.get(v, None)):
  430. layer.append(nodes[k]) # add it to the existing layer
  431. else: layers[v] = [nodes[k]] # or make a new layer with the node
  432. all_sorted_nodes.append(nodes[k]) # add it to the sorted list
  433. # TODO: investigate whether I can treat driver conenctions as an inverted hierarchy connection
  434. # as in, a hieraarchy connection from the to_node to the from_node in the link instead of the other way around.
  435. # because it looks like I am just putting the driver node one layer higher than the other one
  436. for drv in drivers:
  437. for out in drv.outputs.values():
  438. for l in out.links:
  439. n = l.to_node
  440. if n in all_sorted_nodes: continue
  441. depth = nodes_heights[drv.signature] + 1
  442. nodes_heights[n.signature] = depth
  443. if (layer := layers.get(depth, None)):
  444. layer.append(n)
  445. else: layers[v] = [n]
  446. #
  447. #
  448. prGreen("Sorting depth for %d nodes finished in %s seconds" %
  449. (len(nodes), time() - start))
  450. keys = list(layers.keys())
  451. keys.sort()
  452. num_nodes=0
  453. print_missed=nodes.copy()
  454. for i in keys:
  455. print_layer = [l_item for l_item in layers[i]]
  456. for k in print_layer:
  457. if k.node_type == "DUMMY":
  458. print (k, k.prototype.bl_idname, i)
  459. if (False): # True to print the layers
  460. for i in keys:
  461. # print_layer = [l_item for l_item in layers[i] if l_item.node_type in ["XFORM",]]# "LINK", "DRIVER"]]
  462. print_layer = [l_item for l_item in layers[i]]
  463. for k in print_layer:
  464. num_nodes+=1
  465. del print_missed[k.signature]
  466. print(wrapGreen("%d: " % i), wrapWhite("%s" % print_layer))
  467. prWhite(f"Final node count: {num_nodes}")
  468. prOrange("The following nodes have been culled:")
  469. for p in print_missed.values():
  470. prWhite (p, p.dependencies)
  471. return layers
  472. def visualize_tree(nodes, base_tree, context):
  473. # first create a MantisVisualizeTree
  474. import bpy
  475. tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
  476. # HACK but OK since this isn't really "public" code
  477. all_links = set()
  478. for n in nodes.values():
  479. vis = tree.nodes.new('MantisVisualizeNode')
  480. vis.gen_data(n, get_node_prototype(n.signature, base_tree))
  481. for inp in n.inputs.values():
  482. for l in inp.links:
  483. all_links.add(l)
  484. for out in n.outputs.values():
  485. for l in out.links:
  486. all_links.add(l)
  487. for l in all_links:
  488. if l.to_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY'] or \
  489. l.from_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY']:
  490. pass
  491. n_name_in = '.'.join(l.from_node.signature[1:])
  492. s_name_in = l.from_socket
  493. n_name_out = '.'.join(l.to_node.signature[1:])
  494. s_name_out = l.to_socket
  495. try:
  496. tree.links.new(
  497. input=tree.nodes.get(n_name_in).outputs.get(s_name_in),
  498. output=tree.nodes.get(n_name_out).inputs.get(s_name_out),
  499. )
  500. except Exception as e:
  501. print (type(e))
  502. prRed(f"Could not make link {n_name_in}:{s_name_in}-->{n_name_out}:{s_name_out}")
  503. print(e)
  504. # raise e
  505. # at this point not all links are in the tree yet!
  506. from .utilities import SugiyamaGraph
  507. SugiyamaGraph(tree, 16)
  508. #execute tree is really slow overall, but still completes 1000s of nodes in only
  509. def execute_tree(nodes, base_tree, context):
  510. # return
  511. import bpy
  512. from time import time
  513. from .node_container_common import GraphError
  514. from uuid import uuid4
  515. original_active = context.view_layer.objects.active
  516. start_execution_time = time()
  517. from collections import deque
  518. xForm_pass = deque()
  519. for nc in nodes.values():
  520. nc.prepared = False
  521. nc.executed = False
  522. check_and_add_root(nc, xForm_pass)
  523. execute_pass = xForm_pass.copy()
  524. # exe_order = {}; i=0
  525. executed = []
  526. # check for cycles here by keeping track of the number of times a node has been visited.
  527. visited={}
  528. check_max_len=len(nodes)**2 # seems too high but safe. In a well-ordered graph, I guess this number should be less than the number of nodes.
  529. while(xForm_pass):
  530. n = xForm_pass.pop()
  531. if visited.get(n.signature):
  532. visited[n.signature]+=1
  533. else:
  534. visited[n.signature]=0
  535. if visited[n.signature] > check_max_len:
  536. prRed("There is a cycle in the graph somewhere. Fix it!")
  537. # we're trying to solve the halting problem at this point.. don't do that.
  538. # TODO find a better way! there are algo's for this but they will require using a different solving algo, too
  539. if n.prepared:
  540. continue
  541. if n.node_type not in ['XFORM', 'UTILITY']:
  542. for dep in n.hierarchy_dependencies:
  543. if not dep.prepared:
  544. xForm_pass.appendleft(n) # hold it
  545. break
  546. else:
  547. n.prepared=True
  548. executed.append(n)
  549. for conn in n.hierarchy_connections:
  550. if not conn.prepared:
  551. xForm_pass.appendleft(conn)
  552. else:
  553. for dep in n.hierarchy_dependencies:
  554. if not dep.prepared:
  555. break
  556. else:
  557. n.bPrepare(context)
  558. if not n.executed:
  559. n.bExecute(context)
  560. n.prepared=True
  561. executed.append(n)
  562. for conn in n.hierarchy_connections:
  563. if not conn.prepared:
  564. xForm_pass.appendleft(conn)
  565. active = None
  566. switch_me = []
  567. for n in nodes.values():
  568. # if it is a armature, switch modes
  569. # total hack #kinda dumb
  570. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  571. try:
  572. ob = n.bGetObject()
  573. except KeyError: # for bones
  574. ob = None
  575. # TODO this will be a problem if and when I add mesh/curve stuff
  576. if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
  577. switch_me.append(ob)
  578. active = ob
  579. context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
  580. # we override selected_objects to prevent anyone else from mode-switching
  581. # TODO it's possible but unlikely that the user will try to run a
  582. # graph with no armature nodes in it.
  583. if (active):
  584. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  585. bpy.ops.object.mode_set(mode='POSE')
  586. for n in executed:
  587. n.bPrepare(context)
  588. if not n.executed:
  589. n.bExecute(context)
  590. for n in executed:
  591. n.bFinalize(context)
  592. for n in nodes.values(): # if it is a armature, switch modes
  593. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  594. if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
  595. switch_me.append(ob)
  596. active = ob
  597. if (active):
  598. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  599. bpy.ops.object.mode_set(mode='OBJECT')
  600. for ob in switch_me:
  601. ob.data.pose_position = 'POSE'
  602. tot_time = (time() - start_execution_time)
  603. prGreen(f"Executed tree of {len(executed)} nodes in {tot_time} seconds")
  604. if (original_active):
  605. context.view_layer.objects.active = original_active
  606. original_active.select_set(True)
  607. def execute_tree_old(nodes, base_tree, context):
  608. # execute_tree_nosort(nodes, base_tree, context); return
  609. import bpy
  610. from time import time
  611. from .node_container_common import GraphError
  612. from uuid import uuid4
  613. start_time = time()
  614. original_active = context.view_layer.objects.active
  615. # if we solve Schema trees here, then we don't need to worry about parsed_tree getting more complex
  616. # however, at this point, we are going to have to do some re-connections
  617. # and I am not sure this won't mess up the dependency solving.
  618. # worst case scenario, I think I can run the init_connections()
  619. # bit again after swapping the dummy-node for the solved schema tree
  620. layers = sort_tree_into_layers(nodes, context)
  621. start_execution_time = time()
  622. # raise NotImplementedError
  623. # Execute the first pass (xForm, Utility) #
  624. for i in range(len(layers)):
  625. for node in layers[i]:
  626. if (node.node_type in ['XFORM', 'UTILITY']):
  627. try:
  628. # ex_start_time = time()
  629. # if not node.prepared:
  630. # we have to do this over again oof
  631. node.bPrepare(context)
  632. if not node.executed:
  633. node.bExecute(context)
  634. # prPurple(f"Execution step took time... {time() - ex_start_time}")
  635. except Exception as e:
  636. # prRed(node)
  637. # for dep in node.dependencies:
  638. # prOrange(" ", dep, dep.prepared&dep.executed)
  639. # for dep in dep.dependencies:
  640. # prWhite(" ", dep, dep.prepared&dep.executed)
  641. # for dep in dep.dependencies:
  642. # prPurple(" ", dep, dep.prepared&dep.executed)
  643. # for dep in dep.dependencies:
  644. # prRed(" ", dep, dep.prepared&dep.executed)
  645. prRed("Execution failed at %s" % node); raise e
  646. # Switch to Pose Mode #
  647. active = None
  648. switch_me = []
  649. for n in nodes.values():
  650. # if it is a armature, switch modes
  651. # total hack #kinda dumb
  652. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  653. try:
  654. ob = n.bGetObject()
  655. except KeyError: # for bones
  656. ob = None
  657. # TODO this will be a problem if and when I add mesh/curve stuff
  658. if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
  659. switch_me.append(ob)
  660. active = ob
  661. context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
  662. # we override selected_objects to prevent anyone else from mode-switching
  663. # TODO it's possible but unlikely that the user will try to run a
  664. # graph with no armature nodes in it.
  665. if (active):
  666. for ob in switch_me:
  667. ob.pose_position='REST'
  668. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  669. bpy.ops.object.mode_set(mode='POSE')
  670. # Execute second pass (Link, Driver) #
  671. for i in range(len(layers)):
  672. for n in layers[i]:
  673. # Now do the Link & Driver nodes during the second pass.
  674. if (n.node_type in ['LINK', 'DRIVER']):
  675. try:
  676. if not n.prepared:
  677. n.bPrepare(context)
  678. if not n.executed:
  679. n.bExecute(context)
  680. except Exception as e:
  681. prRed("Execution failed at %s" % n); raise e
  682. # except GraphError:
  683. # pass
  684. # except Exception as e:
  685. # prRed (n); raise e
  686. # Finalize #
  687. for i in range(len(layers)):
  688. for node in layers[i]:
  689. if (hasattr(node, "bFinalize")):
  690. node.bFinalize(context)
  691. for n in nodes.values():
  692. # if it is a armature, switch modes
  693. # total hack #kinda dumb
  694. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  695. try:
  696. ob = n.bGetObject()
  697. except KeyError: # for bones
  698. ob = None
  699. # TODO this will be a problem if and when I add mesh/curve stuff
  700. if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
  701. switch_me.append(ob)
  702. active = ob
  703. if (active):
  704. for ob in switch_me:
  705. ob.pose_position='POSE'
  706. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  707. bpy.ops.object.mode_set(mode='OBJECT')
  708. prGreen("Executed Tree in %s seconds" % (time() - start_execution_time))
  709. prGreen("Finished executing tree in %f seconds" % (time() - start_time))
  710. if (original_active):
  711. context.view_layer.objects.active = original_active
  712. original_active.select_set(True)
  713. # TODO right now:
  714. # I am working on getting schema to ignore non-hierarchy dependencies
  715. # I need to ensure that the links are being made between nodes
  716. # so I need to check the OUTPUTS of the arrays on the DUMMY_SCHEMA of the node that is sending
  717. # backwards connections out
  718. # then I need to make sure that they are going to the right place
  719. # also it is probably eventually necessary to break schema solve into schema solve_step
  720. # which solves one step and decrements the length by one
  721. # and holds on to the solver for next time