visualize.py 8.4 KB

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