readtree.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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)
  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 solve_schema_to_tree(nc, all_nc, roots=[]):
  212. from .utilities import get_node_prototype
  213. np = get_node_prototype(nc.signature, nc.base_tree)
  214. # if not hasattr(np, 'node_tree'):
  215. # nc.bPrepare()
  216. # nc.prepared=True
  217. # return {}
  218. from .schema_solve import SchemaSolver
  219. length = nc.evaluate_input("Schema Length")
  220. tree = np.node_tree
  221. prOrange(f"Expanding schema {tree.name} in node {nc} with length {length}.")
  222. solver = SchemaSolver(nc, all_nc, np)
  223. solved_nodes = solver.solve(length)
  224. # prGreen(f"Finished solving schema {tree.name} in node {nc}.")
  225. prWhite(f"Schema declared {len(solved_nodes)} nodes.")
  226. nc.prepared = True
  227. # TODO this should be handled by the schema's finalize() function
  228. del_me = []
  229. for k, v in all_nc.items():
  230. # delete all the schema's internal nodes. The links have already been deleted by the solver.
  231. if v.signature[0] not in ['MANTIS_AUTOGENERATED'] and nc.signature[-1] in k:
  232. delete_nc(v)
  233. del_me.append(k)
  234. for k in del_me:
  235. del all_nc[k]
  236. for k,v in solved_nodes.items():
  237. all_nc[k]=v
  238. init_connections(v)
  239. check_and_add_root(v, roots, include_non_hierarchy=True)
  240. # end TODO
  241. return solved_nodes
  242. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  243. # PARSE NODE TREE #
  244. # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** # *** #
  245. def parse_tree(base_tree):
  246. from uuid import uuid4 # do this here?
  247. base_tree.execution_id = uuid4().__str__() # set this, it may be used by nodes during execution
  248. # annoyingly I have to pass in values for all of the dicts because if I initialize them in the function call
  249. # then they stick around because the function definition inits them once and keeps a reference
  250. # so instead I have to supply them to avoid ugly code or bugs elsewhere
  251. # it's REALLY confusing when you run into this sort of problem. So it warrants four entire lines of comments!
  252. import time
  253. data_start_time = time.time()
  254. dummy_nodes, all_nc, all_schema = data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_nc = {}, all_schema={})
  255. # return
  256. prGreen(f"Pulling data from tree took {time.time() - data_start_time} seconds")
  257. for sig, dummy in dummy_nodes.items():
  258. if (hasattr(dummy, "reroute_links")):
  259. dummy.reroute_links(dummy, all_nc)
  260. # TODO
  261. # MODIFY BELOW to use hierarchy_dependencies instead
  262. # SCHEMA DUMMY nodes will need to gather the hierarchy and non-hierarchy dependencies
  263. # so SCHEMA DUMMY will not make their dependencies all hierarchy
  264. # since they will need to be able to send drivers and such
  265. start_time = time.time()
  266. sig_check = (None, 'Node Group.001', 'switch_thigh')
  267. roots = []
  268. arrays = []
  269. from .misc_containers import UtilityArrayGet
  270. for nc in all_nc.values():
  271. # clean up the groups
  272. if nc.node_type in ["DUMMY"]:
  273. if nc.prototype.bl_idname in ("MantisNodeGroup", "NodeGroupOutput"):
  274. continue
  275. from .base_definitions import from_name_filter, to_name_filter
  276. def detect_hierarchy_link(from_node, from_socket, to_node, to_socket,):
  277. if to_node.node_type in ['DUMMY_SCHEMA', 'SCHEMA']:
  278. prRed('a")')
  279. return False
  280. if (from_socket in from_name_filter) or (to_socket in to_name_filter):
  281. prOrange('b')
  282. return False
  283. return True
  284. init_dependencies(nc)
  285. init_connections(nc)
  286. check_and_add_root(nc, roots, include_non_hierarchy=True)
  287. if isinstance(nc, UtilityArrayGet):
  288. arrays.append(nc)
  289. from .utilities import get_all_dependencies
  290. from collections import deque
  291. unsolved_schema = deque()
  292. solve_only_these = []; solve_only_these.extend(list(all_schema.values()))
  293. # ignore_these = []
  294. for schema in all_schema.values():
  295. # so basically we need to check every parent node if it is a schema
  296. # this is a fairly slapdash solution but it works and I won't change it
  297. for i in range(len(schema.signature)-1): # -1, we don't want to check this node, obviously
  298. if parent := all_schema.get(schema.signature[:i+1]):
  299. # ignore_these.append(schema.signature)
  300. solve_only_these.remove(schema)
  301. break
  302. else:
  303. init_schema_dependencies(schema, all_nc)
  304. solve_only_these.extend(get_all_dependencies(schema))
  305. unsolved_schema.append(schema)
  306. for array in arrays:
  307. solve_only_these.extend(get_all_dependencies(array))
  308. solve_only_these.extend(arrays)
  309. schema_solve_done = set()
  310. solve_only_these = set(solve_only_these)
  311. if False:
  312. # how does a three-deep while loop make any sense and how does this even work? HACK
  313. from .node_container_common import get_prepared_depth_lines
  314. while unsolved_schema:
  315. from collections import deque
  316. unsolved_layer = deque(unsolved_schema)
  317. while (roots):
  318. root = roots.pop()
  319. if root.prepared == False:
  320. root.bPrepare()
  321. root.prepared=True
  322. nc_paths = get_prepared_depth_lines(root)[-1]
  323. for nc_set in nc_paths.values():
  324. for tup in nc_set:
  325. nc = tup[-1]
  326. if nc.prepared==False and nc in solve_only_these:
  327. unsolved_layer.append(nc)
  328. while(unsolved_layer):
  329. # this is so dumb I can't believe I am doing it this way
  330. for schema in unsolved_schema:
  331. unsolved_layer.appendleft(schema)
  332. nc = unsolved_layer.pop()
  333. remove_me = False
  334. if nc.hierarchy_dependencies:
  335. for dep in nc.hierarchy_dependencies:
  336. if dep.node_type == 'SCHEMA':
  337. prRed(f"Case A: {nc}, {dep}")
  338. remove_me = True
  339. elif dep.prepared == False:
  340. remove_me = True
  341. if dep in solve_only_these:
  342. unsolved_layer.appendleft(dep)
  343. if remove_me:
  344. continue
  345. #
  346. if nc.prepared:
  347. if (nc.signature in all_schema.keys()):
  348. continue
  349. for n in nc.connections:
  350. if not n.prepared:
  351. unsolved_layer.append(n)
  352. continue
  353. #
  354. elif nc.signature in all_schema.keys() and nc.signature not in ignore_these: # ignore_these is a HACK but it should work
  355. # prOrange(nc.hierarchy_dependencies, sum([dep.prepared for dep in nc.hierarchy_dependencies]))
  356. # prOrange(nc.dependencies, sum([dep.prepared for dep in nc.dependencies]))
  357. solved_nodes = solve_schema_to_tree(nc, all_nc, roots)
  358. unsolved_schema.remove(nc)
  359. for dep in solved_nodes.values():
  360. if dep.signature in ignore_these: continue
  361. if dep.prepared or dep not in solve_only_these: continue
  362. unsolved_layer.appendleft(dep)
  363. elif hasattr(nc, "bPrepare"):
  364. try:
  365. nc.bPrepare()
  366. except Exception as e:
  367. for inp in nc.inputs.values():
  368. print (inp)
  369. for l in inp.links:
  370. print (l)
  371. raise e
  372. for n in nc.connections:
  373. if not n.prepared and n in solve_only_these:
  374. unsolved_layer.append(n)
  375. while (roots):
  376. root = roots.pop()
  377. if root.prepared == False:
  378. root.bPrepare()
  379. root.prepared=True
  380. nc_paths = get_prepared_depth_lines(root)[-1]
  381. for nc_set in nc_paths.values():
  382. for tup in nc_set:
  383. nc = tup[-1]
  384. if nc.prepared==False and nc in solve_only_these:
  385. unsolved_layer.append(nc)
  386. if unsolved_schema:
  387. prRed(unsolved_schema)
  388. raise RuntimeError("Failed to resolve all schema declarations")
  389. prWhite(f"Parsing tree took {time.time()-start_time} seconds.")
  390. else: # this works, it's a lot simpler, it's a lot faster
  391. # There is still a lot of room for optimization.
  392. solve_layer = unsolved_schema.copy(); solve_layer.extend(roots)
  393. while(solve_layer):
  394. n = solve_layer.pop()
  395. # if n in schema_solve_done:
  396. # continue
  397. if n not in solve_only_these:
  398. continue
  399. if n.signature in all_schema.keys() and n.signature: # ignore_these is a HACK but it should work
  400. for dep in n.hierarchy_dependencies:
  401. if dep not in schema_solve_done:
  402. solve_layer.appendleft(n)
  403. break
  404. else:
  405. solved_nodes = solve_schema_to_tree(n, all_nc, roots)
  406. unsolved_schema.remove(n)
  407. schema_solve_done.add(n)
  408. for node in solved_nodes.values():
  409. #
  410. init_dependencies(node)
  411. init_connections(node)
  412. #
  413. # if node.signature in ignore_these: continue
  414. solve_layer.appendleft(node)
  415. # prPurple(node)
  416. for conn in n.hierarchy_connections:
  417. if conn not in schema_solve_done and conn not in solve_layer:
  418. solve_layer.appendleft(conn)
  419. else:
  420. for dep in n.hierarchy_dependencies:
  421. if dep not in schema_solve_done:
  422. break
  423. else:
  424. n.bPrepare()
  425. schema_solve_done.add(n)
  426. for conn in n.hierarchy_connections:
  427. if conn not in schema_solve_done and conn not in solve_layer:
  428. solve_layer.appendleft(conn)
  429. if unsolved_schema:
  430. # prRed(unsolved_schema)
  431. raise RuntimeError("Failed to resolve all schema declarations")
  432. all_nc = list(all_nc.values()).copy()
  433. kept_nc = {}
  434. while (all_nc):
  435. nc = all_nc.pop()
  436. if nc in arrays:
  437. continue
  438. if nc.node_type in ["DUMMY"]:
  439. # prRed(nc.prototype.bl_idname)
  440. if nc.prototype.bl_idname in ["MantisNodeGroup", "NodeGroupOutput"]:
  441. continue
  442. # continue
  443. # cleanup autogen nodes
  444. if nc.signature[0] == "MANTIS_AUTOGENERATED" and len(nc.inputs) == 0 and len(nc.outputs) == 1:
  445. output=list(nc.outputs.values())[0]
  446. value=list(nc.parameters.values())[0] # TODO modify the dependecy get function to exclude these nodes completely
  447. for l in output.links:
  448. to_node = l.to_node; to_socket = l.to_socket
  449. l.die()
  450. to_node.parameters[to_socket] = value
  451. del to_node.inputs[to_socket]
  452. init_dependencies(to_node)
  453. # init_connections(from_node)
  454. # it seems safe, and more importantly, fast, not to update the dependencies of these nodes.
  455. continue # in my test case this reduced the time cost by 33% by removing a large number of root nodes.
  456. # it went from 18.9 seconds to 9-10 seconds
  457. if (nc.node_type in ['XFORM']) and ("Relationship" in nc.inputs.keys()):
  458. if (new_nc := insert_lazy_parents(nc)):
  459. kept_nc[new_nc.signature]=new_nc
  460. kept_nc[nc.signature]=nc
  461. # raise NotImplementedError
  462. prWhite(f"Parsing tree took {time.time()-start_time} seconds.")
  463. prWhite("Number of Nodes: %s" % (len(kept_nc)))
  464. return kept_nc
  465. def sort_tree_into_layers(nodes, context):
  466. from time import time
  467. from .node_container_common import (get_depth_lines,
  468. node_depth)
  469. # All this function needs to do is sort out the hierarchy and
  470. # get things working in order of their dependencies.
  471. roots, drivers = [], []
  472. start = time()
  473. for n in nodes.values():
  474. if n.node_type == 'DRIVER': drivers.append(n)
  475. # ugly but necesary to ensure that drivers are always connected.
  476. check_and_add_root(n, roots)
  477. layers, nodes_heights = {}, {}
  478. #Possible improvement: unify roots if they represent the same data
  479. all_sorted_nodes = []
  480. for root in roots:
  481. nodes_heights[root.signature] = 0
  482. depth_lines = get_depth_lines(root)[0]
  483. for n in nodes.values():
  484. if n.signature not in (depth_lines.keys()):
  485. continue #belongs to a different root
  486. d = nodes_heights.get(n.signature, 0)
  487. if (new_d := node_depth(depth_lines[n.signature])) > d:
  488. d = new_d
  489. nodes_heights[n.signature] = d
  490. for k, v in nodes_heights.items():
  491. if (layer := layers.get(v, None)):
  492. layer.append(nodes[k]) # add it to the existing layer
  493. else: layers[v] = [nodes[k]] # or make a new layer with the node
  494. all_sorted_nodes.append(nodes[k]) # add it to the sorted list
  495. # TODO: investigate whether I can treat driver conenctions as an inverted hierarchy connection
  496. # as in, a hieraarchy connection from the to_node to the from_node in the link instead of the other way around.
  497. # because it looks like I am just putting the driver node one layer higher than the other one
  498. for drv in drivers:
  499. for out in drv.outputs.values():
  500. for l in out.links:
  501. n = l.to_node
  502. if n in all_sorted_nodes: continue
  503. depth = nodes_heights[drv.signature] + 1
  504. nodes_heights[n.signature] = depth
  505. if (layer := layers.get(depth, None)):
  506. layer.append(n)
  507. else: layers[v] = [n]
  508. #
  509. #
  510. prGreen("Sorting depth for %d nodes finished in %s seconds" %
  511. (len(nodes), time() - start))
  512. keys = list(layers.keys())
  513. keys.sort()
  514. num_nodes=0
  515. print_missed=nodes.copy()
  516. for i in keys:
  517. print_layer = [l_item for l_item in layers[i]]
  518. for k in print_layer:
  519. if k.node_type == "DUMMY":
  520. print (k, k.prototype.bl_idname, i)
  521. if (False): # True to print the layers
  522. for i in keys:
  523. # print_layer = [l_item for l_item in layers[i] if l_item.node_type in ["XFORM",]]# "LINK", "DRIVER"]]
  524. print_layer = [l_item for l_item in layers[i]]
  525. for k in print_layer:
  526. num_nodes+=1
  527. del print_missed[k.signature]
  528. print(wrapGreen("%d: " % i), wrapWhite("%s" % print_layer))
  529. prWhite(f"Final node count: {num_nodes}")
  530. prOrange("The following nodes have been culled:")
  531. for p in print_missed.values():
  532. prWhite (p, p.dependencies)
  533. return layers
  534. def visualize_tree(nodes, base_tree, context):
  535. # first create a MantisVisualizeTree
  536. import bpy
  537. tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
  538. # HACK but OK since this isn't really "public" code
  539. all_links = set()
  540. for n in nodes.values():
  541. vis = tree.nodes.new('MantisVisualizeNode')
  542. vis.gen_data(n, get_node_prototype(n.signature, base_tree))
  543. for inp in n.inputs.values():
  544. for l in inp.links:
  545. all_links.add(l)
  546. for out in n.outputs.values():
  547. for l in out.links:
  548. all_links.add(l)
  549. for l in all_links:
  550. if l.to_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY'] or \
  551. l.from_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY']:
  552. pass
  553. n_name_in = '.'.join(l.from_node.signature[1:])
  554. s_name_in = l.from_socket
  555. n_name_out = '.'.join(l.to_node.signature[1:])
  556. s_name_out = l.to_socket
  557. try:
  558. tree.links.new(
  559. input=tree.nodes.get(n_name_in).outputs.get(s_name_in),
  560. output=tree.nodes.get(n_name_out).inputs.get(s_name_out),
  561. )
  562. except Exception as e:
  563. print (type(e))
  564. prRed(f"Could not make link {n_name_in}:{s_name_in}-->{n_name_out}:{s_name_out}")
  565. print(e)
  566. # raise e
  567. # at this point not all links are in the tree yet!
  568. from .utilities import SugiyamaGraph
  569. SugiyamaGraph(tree, 16)
  570. #execute tree is really slow overall, but still completes 1000s of nodes in only
  571. def execute_tree(nodes, base_tree, context):
  572. # return
  573. import bpy
  574. from time import time
  575. from .node_container_common import GraphError
  576. from uuid import uuid4
  577. original_active = context.view_layer.objects.active
  578. start_execution_time = time()
  579. from collections import deque
  580. xForm_pass = deque()
  581. for nc in nodes.values():
  582. nc.prepared = False
  583. nc.executed = False
  584. check_and_add_root(nc, xForm_pass)
  585. pre_pass_done = []
  586. execute_pass = xForm_pass.copy()
  587. # exe_order = {}; i=0
  588. executed = []
  589. while(xForm_pass):
  590. n = xForm_pass.pop()
  591. if n.prepared:
  592. continue
  593. if n.node_type not in ['XFORM', 'UTILITY']:
  594. for dep in n.hierarchy_dependencies:
  595. if not dep.prepared:
  596. xForm_pass.appendleft(n) # hold it
  597. break
  598. else:
  599. n.prepared=True
  600. executed.append(n)
  601. for conn in n.hierarchy_connections:
  602. if not conn.prepared:
  603. xForm_pass.appendleft(conn)
  604. else:
  605. for dep in n.hierarchy_dependencies:
  606. if not dep.prepared:
  607. break
  608. else:
  609. n.bPrepare(context)
  610. if not n.executed:
  611. n.bExecute(context)
  612. n.prepared=True
  613. executed.append(n)
  614. for conn in n.hierarchy_connections:
  615. if not conn.prepared:
  616. xForm_pass.appendleft(conn)
  617. active = None
  618. switch_me = []
  619. for n in nodes.values():
  620. # if it is a armature, switch modes
  621. # total hack #kinda dumb
  622. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  623. try:
  624. ob = n.bGetObject()
  625. except KeyError: # for bones
  626. ob = None
  627. # TODO this will be a problem if and when I add mesh/curve stuff
  628. if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
  629. switch_me.append(ob)
  630. active = ob
  631. context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
  632. # we override selected_objects to prevent anyone else from mode-switching
  633. # TODO it's possible but unlikely that the user will try to run a
  634. # graph with no armature nodes in it.
  635. if (active):
  636. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  637. bpy.ops.object.mode_set(mode='POSE')
  638. for n in executed:
  639. n.bPrepare(context)
  640. if not n.executed:
  641. n.bExecute(context)
  642. for n in executed:
  643. n.bFinalize(context)
  644. for n in nodes.values(): # if it is a armature, switch modes
  645. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  646. if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
  647. switch_me.append(ob)
  648. active = ob
  649. if (active):
  650. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  651. bpy.ops.object.mode_set(mode='OBJECT')
  652. tot_time = (time() - start_execution_time)
  653. prGreen(f"Executed tree of {len(executed)} nodes in {tot_time} seconds")
  654. if (original_active):
  655. context.view_layer.objects.active = original_active
  656. original_active.select_set(True)
  657. def execute_tree_old(nodes, base_tree, context):
  658. # execute_tree_nosort(nodes, base_tree, context); return
  659. import bpy
  660. from time import time
  661. from .node_container_common import GraphError
  662. from uuid import uuid4
  663. start_time = time()
  664. original_active = context.view_layer.objects.active
  665. # if we solve Schema trees here, then we don't need to worry about parsed_tree getting more complex
  666. # however, at this point, we are going to have to do some re-connections
  667. # and I am not sure this won't mess up the dependency solving.
  668. # worst case scenario, I think I can run the init_connections()
  669. # bit again after swapping the dummy-node for the solved schema tree
  670. layers = sort_tree_into_layers(nodes, context)
  671. start_execution_time = time()
  672. # raise NotImplementedError
  673. # Execute the first pass (xForm, Utility) #
  674. for i in range(len(layers)):
  675. for node in layers[i]:
  676. if (node.node_type in ['XFORM', 'UTILITY']):
  677. try:
  678. # ex_start_time = time()
  679. # if not node.prepared:
  680. # we have to do this over again oof
  681. node.bPrepare(context)
  682. if not node.executed:
  683. node.bExecute(context)
  684. # prPurple(f"Execution step took time... {time() - ex_start_time}")
  685. except Exception as e:
  686. # prRed(node)
  687. # for dep in node.dependencies:
  688. # prOrange(" ", dep, dep.prepared&dep.executed)
  689. # for dep in dep.dependencies:
  690. # prWhite(" ", dep, dep.prepared&dep.executed)
  691. # for dep in dep.dependencies:
  692. # prPurple(" ", dep, dep.prepared&dep.executed)
  693. # for dep in dep.dependencies:
  694. # prRed(" ", dep, dep.prepared&dep.executed)
  695. prRed("Execution failed at %s" % node); raise e
  696. # Switch to Pose Mode #
  697. active = None
  698. switch_me = []
  699. for n in nodes.values():
  700. # if it is a armature, switch modes
  701. # total hack #kinda dumb
  702. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  703. try:
  704. ob = n.bGetObject()
  705. except KeyError: # for bones
  706. ob = None
  707. # TODO this will be a problem if and when I add mesh/curve stuff
  708. if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
  709. switch_me.append(ob)
  710. active = ob
  711. context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
  712. # we override selected_objects to prevent anyone else from mode-switching
  713. # TODO it's possible but unlikely that the user will try to run a
  714. # graph with no armature nodes in it.
  715. if (active):
  716. for ob in switch_me:
  717. ob.pose_position='REST'
  718. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  719. bpy.ops.object.mode_set(mode='POSE')
  720. # Execute second pass (Link, Driver) #
  721. for i in range(len(layers)):
  722. for n in layers[i]:
  723. # Now do the Link & Driver nodes during the second pass.
  724. if (n.node_type in ['LINK', 'DRIVER']):
  725. try:
  726. if not n.prepared:
  727. n.bPrepare(context)
  728. if not n.executed:
  729. n.bExecute(context)
  730. except Exception as e:
  731. prRed("Execution failed at %s" % n); raise e
  732. # except GraphError:
  733. # pass
  734. # except Exception as e:
  735. # prRed (n); raise e
  736. # Finalize #
  737. for i in range(len(layers)):
  738. for node in layers[i]:
  739. if (hasattr(node, "bFinalize")):
  740. node.bFinalize(context)
  741. for n in nodes.values():
  742. # if it is a armature, switch modes
  743. # total hack #kinda dumb
  744. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  745. try:
  746. ob = n.bGetObject()
  747. except KeyError: # for bones
  748. ob = None
  749. # TODO this will be a problem if and when I add mesh/curve stuff
  750. if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
  751. switch_me.append(ob)
  752. active = ob
  753. if (active):
  754. for ob in switch_me:
  755. ob.pose_position='POSE'
  756. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  757. bpy.ops.object.mode_set(mode='OBJECT')
  758. prGreen("Executed Tree in %s seconds" % (time() - start_execution_time))
  759. prGreen("Finished executing tree in %f seconds" % (time() - start_time))
  760. if (original_active):
  761. context.view_layer.objects.active = original_active
  762. original_active.select_set(True)
  763. # TODO right now:
  764. # I am working on getting schema to ignore non-hierarchy dependencies
  765. # I need to ensure that the links are being made between nodes
  766. # so I need to check the OUTPUTS of the arrays on the DUMMY_SCHEMA of the node that is sending
  767. # backwards connections out
  768. # then I need to make sure that they are going to the right place
  769. # also it is probably eventually necessary to break schema solve into schema solve_step
  770. # which solves one step and decrements the length by one
  771. # and holds on to the solver for next time