visualize.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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 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 import get_node_prototype
  31. if mantis_node.node_type in ['SCHEMA', 'DUMMY']:
  32. np=None
  33. elif mantis_node.ui_signature is None:
  34. np=None
  35. else:
  36. np=get_node_prototype(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.link_limit = 4000
  61. try:
  62. if sock := np.inputs.get(inp.name):
  63. s.color = sock.color_simple
  64. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  65. pass
  66. except KeyError:
  67. pass
  68. for out in mantis_node.outputs:
  69. match mode:
  70. case "DEBUG_CONNECTIONS":
  71. if not out.is_connected:
  72. continue
  73. s = self.outputs.new('WildcardSocket', out.name)
  74. try:
  75. if sock := np.outputs.get(out.name):
  76. s.color = sock.color_simple
  77. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  78. pass
  79. except KeyError:
  80. pass
  81. self.location = np.location # will get overwritten by Grandalf later.
  82. else:
  83. self.label = mantis_node.signature[-1] # which is be the unique name.
  84. for inp in mantis_node.inputs:
  85. match mode:
  86. case "DEBUG_CONNECTIONS":
  87. if not inp.is_connected:
  88. continue
  89. multi = False
  90. if len(inp.links) > 1: multi = True
  91. s = self.inputs.new('WildcardSocket', inp.name)
  92. s.link_limit = 4000
  93. for out in mantis_node.outputs:
  94. match mode:
  95. case "DEBUG_CONNECTIONS":
  96. if not out.is_connected:
  97. continue
  98. self.outputs.new('WildcardSocket', out.name)
  99. def gen_vis_node( mantis_node,
  100. vis_tree,
  101. links,
  102. omit_simple=False,
  103. ):
  104. from .base_definitions import array_output_types
  105. # if mantis_node.node_type == 'UTILITY' and \
  106. # mantis_node.execution_prepared == True:
  107. # return
  108. base_tree= mantis_node.base_tree
  109. vis = vis_tree.nodes.new('MantisVisualizeNode')
  110. vis.gen_data(mantis_node)
  111. for inp in mantis_node.inputs.values():
  112. for l in inp.links:
  113. if l.from_node in mantis_node.hierarchy_dependencies:
  114. links.add(l)
  115. for out in mantis_node.outputs.values():
  116. for l in out.links:
  117. if l.to_node in mantis_node.hierarchy_connections:
  118. links.add(l)
  119. return vis
  120. def visualize_tree(m_nodes, base_tree, context):
  121. # first create a MantisVisualizeTree
  122. from .readtree import check_and_add_root
  123. from .utilities import trace_all_nodes_from_root
  124. import bpy
  125. base_tree.is_executing=True
  126. import cProfile
  127. import pstats, io
  128. from pstats import SortKey
  129. cull_no_links = False
  130. with cProfile.Profile() as pr:
  131. try:
  132. trace_from_roots = True
  133. all_links = set(); mantis_nodes=set(); nodes={}
  134. if trace_from_roots:
  135. roots=[]
  136. for n in m_nodes.values():
  137. check_and_add_root(n, roots)
  138. for r in roots:
  139. trace_all_nodes_from_root(r, mantis_nodes)
  140. if len(mantis_nodes) == 0:
  141. print ("No nodes to visualize")
  142. return
  143. else:
  144. mantis_keys = list(base_tree.parsed_tree.keys())
  145. mantis_nodes = list(base_tree.parsed_tree.values())
  146. vis_tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
  147. for m in mantis_nodes:
  148. nodes[m.signature]=gen_vis_node(m, vis_tree, all_links)
  149. # useful for debugging: check the connections for nodes that are
  150. # not in the parsed tree or available from trace_all_nodes_from_root.
  151. for l in all_links:
  152. # if l.to_node.node_type in ['DUMMY_SCHEMA', 'DUMMY'] or \
  153. # l.from_node.node_type in ['DUMMY_SCHEMA', 'DUMMY']:
  154. # pass
  155. from_node = nodes.get(l.from_node.signature)
  156. to_node = nodes.get(l.to_node.signature)
  157. from_socket, to_socket = None, None
  158. if from_node and to_node:
  159. from_socket = from_node.outputs.get(l.from_socket)
  160. to_socket = to_node.inputs.get(l.to_socket)
  161. if from_socket and to_socket:
  162. try:
  163. vis_tree.links.new(
  164. input=from_socket,
  165. output=to_socket,
  166. )
  167. except Exception as e:
  168. print (type(e)); print(e)
  169. raise e
  170. # at this point not all links are in the tree yet!
  171. def has_links (n):
  172. for input in n.inputs:
  173. if input.is_linked:
  174. return True
  175. for output in n.outputs:
  176. if output.is_linked:
  177. return True
  178. return False
  179. no_links=[]
  180. if cull_no_links == True:
  181. for n in vis_tree.nodes:
  182. if not has_links(n):
  183. no_links.append(n)
  184. while (no_links):
  185. n = no_links.pop()
  186. vis_tree.nodes.remove(n)
  187. finally:
  188. s = io.StringIO()
  189. sortby = SortKey.TIME
  190. ps = pstats.Stats(pr, stream=s).strip_dirs().sort_stats(sortby)
  191. ps.print_stats(40) # print the top 20
  192. print(s.getvalue())
  193. base_tree.prevent_next_exec=True
  194. base_tree.is_executing=False
  195. from .ops_nodegroup import mantis_tree_poll_op
  196. class MantisVisualizeOutput(Operator):
  197. """Mantis Visualize Output Operator"""
  198. bl_idname = "mantis.visualize_output"
  199. bl_label = "Visualize Output"
  200. @classmethod
  201. def poll(cls, context):
  202. return (mantis_tree_poll_op(context))
  203. def execute(self, context):
  204. from time import time
  205. from .utilities import wrapGreen, prGreen
  206. tree=context.space_data.path[0].node_tree
  207. tree.update_tree(context)
  208. prGreen(f"Visualize Tree: {tree.name}")
  209. nodes = tree.parsed_tree
  210. visualize_tree(nodes, tree, context)
  211. return {"FINISHED"}