visualize.py 7.3 KB

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