readtree.py 32 KB

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