readtree.py 33 KB

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