visualize.py 8.6 KB

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