visualize.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """ Optional file providing a tool to visualize Mantis Graphs, for debugging and development"""
  2. from bpy.types import Node, NodeTree, Operator
  3. from bpy.props import StringProperty
  4. from ..utilities.utilities import (prRed, prGreen, prPurple, prWhite,
  5. prOrange,
  6. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  7. wrapOrange,)
  8. class MantisVisualizeTree(NodeTree):
  9. '''A custom node tree type that will show up in the editor type list'''
  10. bl_idname = 'MantisVisualizeTree'
  11. bl_label = "mantis output"
  12. bl_icon = 'HIDE_OFF'
  13. class MantisVisualizeNode(Node):
  14. bl_idname = "MantisVisualizeNode"
  15. bl_label = "Node"
  16. signature : StringProperty(default = '')
  17. @classmethod
  18. def poll(cls, ntree):
  19. return (ntree.bl_idname in ['MantisVisualizeTree'])
  20. def init(self, context):
  21. pass
  22. def draw_label(self):
  23. label=''
  24. exploded = self.signature.separate('|')
  25. for elem in exploded:
  26. label+=elem+', '
  27. label = label[:-2] # cut the last comma
  28. return label
  29. def gen_data(self, mantis_node, mode='DEBUG_CONNECTIONS'):
  30. from ..utilities.utilities import get_ui_node
  31. if mantis_node.node_type in ['SCHEMA', 'DUMMY', 'INTERFACE']:
  32. np=None
  33. elif mantis_node.ui_signature is None:
  34. np=None
  35. else:
  36. np=get_ui_node(mantis_node.ui_signature, mantis_node.base_tree)
  37. self.use_custom_color = True
  38. match mantis_node.node_type:
  39. case 'XFORM': self.color = (1.0 ,0.5, 0.0)
  40. case 'LINK': self.color = (0.4 ,0.2, 1.0)
  41. case 'UTILITY': self.color = (0.2 ,0.2, 0.2)
  42. case 'DRIVER': self.color = (0.7, 0.05, 0.8)
  43. case 'DUMMY_SCHEMA': self.color = (0.85 ,0.95, 0.9)
  44. case 'DUMMY': self.color = (0.05 ,0.05, 0.15)
  45. self.name = '.'.join(mantis_node.signature[1:]) # this gets trunc'd
  46. self.signature = '|'.join(mantis_node.signature[1:])
  47. if np:
  48. if np.label:
  49. self.label=np.label
  50. else:
  51. self.label=np.name
  52. for inp in mantis_node.inputs:
  53. match mode:
  54. case "DEBUG_CONNECTIONS":
  55. if not inp.is_connected:
  56. continue
  57. multi = False
  58. if len(inp.links) > 1: multi = True
  59. s = self.inputs.new('WildcardSocket', inp.name, use_multi_input=multi)
  60. s.color = (0.0, 0.0, 0.0, 1.0)
  61. s.link_limit = 4000
  62. try:
  63. if sock := np.inputs.get(inp.name):
  64. s.color = sock.color_simple
  65. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  66. pass
  67. except KeyError:
  68. pass
  69. for out in mantis_node.outputs:
  70. match mode:
  71. case "DEBUG_CONNECTIONS":
  72. if not out.is_connected:
  73. continue
  74. s = self.outputs.new('WildcardSocket', out.name)
  75. s.color = (0.0, 0.0, 0.0, 1.0)
  76. try:
  77. if sock := np.outputs.get(out.name):
  78. s.color = sock.color_simple
  79. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  80. pass
  81. except KeyError:
  82. pass
  83. self.location = np.location # will get overwritten by Grandalf later.
  84. else:
  85. self.label = mantis_node.signature[-1] # which is be the unique name.
  86. for inp in mantis_node.inputs:
  87. match mode:
  88. case "DEBUG_CONNECTIONS":
  89. if not inp.is_connected:
  90. continue
  91. multi = False
  92. if len(inp.links) > 1: multi = True
  93. s = self.inputs.new('WildcardSocket', inp.name)
  94. s.color = (0.0, 0.0, 0.0, 1.0)
  95. s.link_limit = 4000
  96. for out in mantis_node.outputs:
  97. match mode:
  98. case "DEBUG_CONNECTIONS":
  99. if not out.is_connected:
  100. continue
  101. s = self.outputs.new('WildcardSocket', out.name)
  102. s.color = (0.0, 0.0, 0.0, 1.0)
  103. def gen_vis_node( mantis_node,
  104. vis_tree,
  105. links,
  106. omit_simple=False,
  107. only_hierarchy_links=False
  108. ):
  109. from ..base_definitions import array_output_types
  110. # if mantis_node.node_type == 'UTILITY' and \
  111. # mantis_node.execution_prepared == True:
  112. # return
  113. base_tree= mantis_node.base_tree
  114. vis = vis_tree.nodes.new('MantisVisualizeNode')
  115. vis.gen_data(mantis_node)
  116. for inp in mantis_node.inputs.values():
  117. for l in inp.links:
  118. if only_hierarchy_links and l.from_node in mantis_node.hierarchy_dependencies:
  119. links.add(l)
  120. else:
  121. links.add(l)
  122. for out in mantis_node.outputs.values():
  123. for l in out.links:
  124. if only_hierarchy_links and l.to_node in mantis_node.hierarchy_connections:
  125. links.add(l)
  126. else:
  127. links.add(l)
  128. return vis
  129. def visualize_tree(m_nodes, base_tree, context):
  130. # first create a MantisVisualizeTree
  131. from ..execution.readtree import check_and_add_root
  132. from ..utilities.utilities import trace_all_nodes_from_root
  133. import bpy
  134. base_tree.is_executing=True
  135. import cProfile
  136. import pstats, io
  137. from pstats import SortKey
  138. cull_no_links = True
  139. with cProfile.Profile() as pr:
  140. try:
  141. trace_from_roots = True
  142. all_links = set(); mantis_nodes=set(); nodes={}
  143. if trace_from_roots:
  144. roots=[]
  145. for n in m_nodes.values():
  146. check_and_add_root(n, roots)
  147. for r in roots:
  148. trace_all_nodes_from_root(r, mantis_nodes)
  149. if len(mantis_nodes) == 0:
  150. print ("No nodes to visualize")
  151. return
  152. else:
  153. mantis_keys = list(base_tree.parsed_tree.keys())
  154. mantis_nodes = list(base_tree.parsed_tree.values())
  155. vis_tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
  156. for i, m in enumerate(mantis_nodes):
  157. nodes[m.signature]=gen_vis_node(m, vis_tree, all_links)
  158. # useful for debugging: check the connections for nodes that are
  159. # not in the parsed tree or available from trace_all_nodes_from_root.
  160. for l in all_links:
  161. # if l.to_node.node_type in ['DUMMY_SCHEMA', 'DUMMY'] or \
  162. # l.from_node.node_type in ['DUMMY_SCHEMA', 'DUMMY']:
  163. # pass
  164. from_node = nodes.get(l.from_node.signature)
  165. to_node = nodes.get(l.to_node.signature)
  166. from_socket, to_socket = None, None
  167. if from_node and to_node:
  168. from_socket = from_node.outputs.get(l.from_socket)
  169. to_socket = to_node.inputs.get(l.to_socket)
  170. if from_socket and to_socket:
  171. try:
  172. vis_tree.links.new(
  173. input=from_socket,
  174. output=to_socket,
  175. )
  176. except Exception as e:
  177. print (type(e)); print(e)
  178. raise e
  179. # at this point not all links are in the tree yet!
  180. def has_links (n):
  181. for input in n.inputs:
  182. if input.is_linked:
  183. return True
  184. for output in n.outputs:
  185. if output.is_linked:
  186. return True
  187. return False
  188. no_links=[]
  189. if cull_no_links == True:
  190. for n in vis_tree.nodes:
  191. if not has_links(n):
  192. no_links.append(n)
  193. while (no_links):
  194. n = no_links.pop()
  195. vis_tree.nodes.remove(n)
  196. finally:
  197. s = io.StringIO()
  198. sortby = SortKey.TIME
  199. ps = pstats.Stats(pr, stream=s).strip_dirs().sort_stats(sortby)
  200. ps.print_stats(40) # print the top 20
  201. print(s.getvalue())
  202. base_tree.prevent_next_exec=True
  203. base_tree.is_executing=False
  204. from ..ops_nodegroup import mantis_tree_poll_op
  205. class MantisVisualizeOutput(Operator):
  206. """Mantis Visualize Output Operator"""
  207. bl_idname = "mantis.visualize_output"
  208. bl_label = "Visualize Output"
  209. @classmethod
  210. def poll(cls, context):
  211. return (mantis_tree_poll_op(context))
  212. def execute(self, context):
  213. from time import time
  214. from ..utilities.utilities import prGreen
  215. tree=context.space_data.path[0].node_tree
  216. tree.update_tree(context)
  217. prGreen(f"Visualize Tree: {tree.name}")
  218. nodes = tree.parsed_tree
  219. visualize_tree(nodes, tree, context)
  220. return {"FINISHED"}