utilities.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. #fool: should be wrColor like prColor... dumb
  2. def wrapRed(skk): return "\033[91m{}\033[00m".format(skk)
  3. def wrapGreen(skk): return "\033[92m{}\033[00m".format(skk)
  4. def wrapPurple(skk): return "\033[95m{}\033[00m".format(skk)
  5. def wrapWhite(skk): return "\033[97m{}\033[00m".format(skk)
  6. def wrapOrange(skk): return "\033[0;33m{}\033[00m".format(skk)
  7. # these should reimplement the print interface..
  8. def prRed(*args): print (*[wrapRed(arg) for arg in args])
  9. def prGreen(*args): print (*[wrapGreen(arg) for arg in args])
  10. def prPurple(*args): print (*[wrapPurple(arg) for arg in args])
  11. def prWhite(*args): print (*[wrapWhite(arg) for arg in args])
  12. def prOrange(*args): print (*[wrapOrange(arg) for arg in args])
  13. # add THIS to the top of a file for easy access:
  14. # from mantis.utilities import (prRed, prGreen, prPurple, prWhite,
  15. # prOrange,
  16. # wrapRed, wrapGreen, wrapPurple, wrapWhite,
  17. # wrapOrange,)
  18. # uncomment to turn them off.
  19. # def prRed(*args): return; print (*[wrapRed(arg) for arg in args])
  20. # def prGreen(*args): return; print (*[wrapGreen(arg) for arg in args])
  21. # def prPurple(*args): return; print (*[wrapPurple(arg) for arg in args])
  22. # def prWhite(*args): return; print (*[wrapWhite(arg) for arg in args])
  23. # def prOrange(*args): return; print (*[wrapOrange(arg) for arg in args])
  24. #DO! Figure out what the hell this does
  25. # then re-write it in a simpler, cleaner way
  26. # that ignores groups because it gets lines from a parsed tree
  27. # ideally I can use the seeking-lines instead of the socket/tree lines
  28. # since those allow the function to travel through the tree.
  29. # not sure if the above comment still has any place here....
  30. def print_lines(lines):
  31. printstring, string = "", ""
  32. cur_g = 0
  33. for line in lines:
  34. string += wrapRed("%i: " % len(line))
  35. for s, g in line:
  36. new_g = len(g) -1
  37. difference = new_g - cur_g
  38. if difference > 0:
  39. string = string[:-1] # get rid of leading space
  40. for i in range(difference):
  41. string += " [ "
  42. elif difference < 0:
  43. string = string[:-4]# get rid of arrow
  44. for i in range(abs(difference)):
  45. string += " ] "
  46. string += "-> "
  47. cur_g = new_g
  48. wrap=wrapWhite
  49. if (s.node.bl_idname in ['UtilitySwitch', 'UtilityDriver', 'UtilityDriverVariable']):
  50. wrap = wrapPurple
  51. elif (s.node.bl_idname in ['xFormArmatureNode', 'xFormBoneNode']):
  52. wrap = wrapOrange
  53. elif (s.node.bl_idname in ['LinkStretchTo']):
  54. wrap = wrapRed
  55. elif ('Link' in s.node.bl_idname):
  56. wrap = wrapGreen
  57. string += wrap(s.node.name + ":" + s.name) + " -> "
  58. string = string[:-4]
  59. while cur_g > 0:
  60. cur_g -= 1
  61. string += " ] "
  62. cur_g, difference = 0,0
  63. printstring +=string + "\n\n"; string = ""
  64. return printstring
  65. # why is this not printing groups in brackets?
  66. def print_socket_signature(sig):
  67. string = ""
  68. for i, e in enumerate(sig):
  69. if (e == "NONE"):
  70. continue
  71. wrap = wrapWhite
  72. if (i == len(sig)-2):
  73. wrap = wrapRed
  74. elif (i == len(sig) - 1):
  75. wrap = wrapGreen
  76. string+= wrap(e) + ":"
  77. return string[:-1]
  78. def print_node_signature(sig,):
  79. string = ""
  80. for i, e in enumerate(sig):
  81. if (e == "NONE"):
  82. continue
  83. wrap = wrapWhite
  84. if (i == len(sig)-2):
  85. wrap = wrapRed
  86. elif (i == len(sig) - 1):
  87. continue
  88. string+= wrap(e) + ":"
  89. return string[:-1]
  90. def print_parsed_node(parsed_node):
  91. # do: make this consistent with the above
  92. string = ""
  93. for k, v in parsed_node.items():
  94. if isinstance(v, dict):
  95. string += "%s:\n" % (k)
  96. for k1, v1 in v.items():
  97. string += " %s: %s\n" % (k1, v1)
  98. else:
  99. string += "%s: %s\n" % (k, v )
  100. return string
  101. def get_socket_signature(line_element):
  102. """
  103. This function creates a convenient, hashable signature for
  104. identifying a node path.
  105. """
  106. if not line_element:
  107. return None
  108. signature, socket, tree_path = [], line_element[0], line_element[1]
  109. for n in tree_path:
  110. if hasattr(n, "name"):
  111. signature.append(n.name)
  112. else:
  113. signature.append("NONE")
  114. signature.append(socket.node.name); signature.append(socket.identifier)
  115. return tuple(signature)
  116. def tuple_of_line(line):
  117. # For creating a set of lines
  118. return tuple(tuple_of_line_element(e) for e in line)
  119. def tuple_of_line_element(line_element):
  120. return (line_element[0], tuple(line_element[1]))
  121. # This has a lot of branches and is kinda slow.
  122. def socket_seek(socket, trees):
  123. from bpy.types import NodeReroute, NodeGroupOutput
  124. if (hasattr( socket.node, "traverse")):
  125. if (socket.node.bl_idname == "MantisNodeGroup"):
  126. trees.append(socket.node)
  127. socket = socket.node.traverse(socket, "IN")
  128. else:
  129. socket = socket.node.traverse(socket)
  130. elif (isinstance(socket.node, NodeReroute)):
  131. socket = socket.node.outputs[0]
  132. elif (isinstance(socket.node, NodeGroupOutput)):
  133. group_node = trees.pop()
  134. if group_node:
  135. socket = group_node.traverse(socket, "OUT")
  136. else:
  137. raise RuntimeError("Error parsing Group Nodes")
  138. else:
  139. raise RuntimeError("Error: node tree cannot be navigated")
  140. return socket, trees
  141. #This is a little slow.
  142. def lines_from_socket(sock, tree_path = [None]):
  143. done = False
  144. sPath =[0,]
  145. lines = []
  146. while (not done):
  147. seek = sock
  148. trees = tree_path.copy() # make sure to copy, lists are not immutable
  149. for curheight, ind in enumerate(sPath):
  150. if not seek: #this will cause the loop to complete normally
  151. continue # which will return an error
  152. if (ind <= (len(seek.links) -1)):
  153. seek = seek.links[ind].to_socket
  154. nextseek, trees = socket_seek(seek, trees.copy())
  155. if (not nextseek): # The node has no no traverse function.
  156. # note, kind of duplicated code, TODO,
  157. lines.append(sPath[:curheight+1])
  158. if (curheight > 0):
  159. sPath[curheight] += 1
  160. else:
  161. done = True
  162. break
  163. if (nextseek.is_linked): #otherwise this is a leaf
  164. seek = nextseek
  165. if (curheight == len(sPath)-1): #go up
  166. sPath.append(0)
  167. elif not (nextseek.is_linked):
  168. lines.append(sPath[:curheight+1])
  169. sPath[curheight]+=1
  170. # this makes sure we're progressing through the tree.
  171. break
  172. else:
  173. if (curheight > 0):
  174. sPath.pop() #go back...
  175. sPath[curheight-1] += 1
  176. else:
  177. done = True
  178. break
  179. else:
  180. raise RuntimeError("There has been an error parsing the tree")
  181. return lines
  182. # only using this once, should this even be a function?
  183. def create_socket_lists(sock, tree_path, lines):
  184. out_lines = []
  185. for line in lines:
  186. s = sock
  187. trees = tree_path.copy()
  188. out_line = [(s, trees)]
  189. for i, ind in enumerate(line):
  190. if i < len(line):
  191. s_next = s.links[ind].to_socket
  192. s_final, trees = socket_seek(s_next, trees.copy())
  193. if s_final:
  194. s = s_final
  195. else: # The node has no no traverse function.
  196. # this needs special check, if it's the first node,
  197. # it's already in the tree.
  198. if (i > 0):
  199. out_line.append( (s, trees) )
  200. out_line.append( (s_next, trees) )
  201. break
  202. # nodes to skip...
  203. if (s.node.bl_idname in [
  204. "NodeReroute",
  205. "MantisNodeGroupOutput",
  206. "NodeGroupOutput",
  207. "MantisNodeGroupInput",
  208. "NodeGroupInput"
  209. ]):
  210. continue
  211. out_line.append( (s, trees) )
  212. out_lines.append(out_line)
  213. return out_lines
  214. #NOTE: this may not work at all lol
  215. # TODO BUG HACK rename and remove this before publishing
  216. def find_root_nodes(tree, tree_path = [None]):
  217. root_nodes = []
  218. for node in tree.nodes:
  219. addMe = True
  220. for s in node.inputs:
  221. if (s.is_linked == True):
  222. addMe = False
  223. # for now, don't try to sovle this, it will need
  224. # a backwards search
  225. for link in s.links:
  226. # we need to check if this is a "false" connection;
  227. # that is, a Group In from an unconnected Group
  228. if (link.from_socket.node.bl_idname in ["NodeGroupInput", "MantisNodeGroupInput"]):
  229. identifier = link.from_socket.identifier
  230. for grp in tree_path[1:][::-1]:
  231. for inp in grp.inputs:
  232. if inp.identifier == identifier and not inp.is_linked:
  233. addMe=True
  234. break
  235. else:
  236. addMe=False
  237. if (hasattr(node, "node_tree")):
  238. # we use the node itself here for the node path, will use it for node signature later.
  239. root_nodes.extend( find_root_nodes(node.node_tree, tree_path+[node]) )
  240. if (node.bl_idname in [
  241. "NodeReroute",
  242. "MantisNodeGroupOutput",
  243. "NodeGroupOutput",
  244. "MantisNodeGroupInput",
  245. "NodeGroupInput",
  246. "NodeFrame",
  247. ]):
  248. addMe = False
  249. continue
  250. if (addMe):
  251. root_nodes.append( (tree_path, node) )
  252. return root_nodes
  253. def parse_node_tree(tree):
  254. root_nodes = find_root_nodes(tree)
  255. # this seems to produce garbage results. Check this!
  256. input_lines = []
  257. for path, node in root_nodes:
  258. # print (path, node)
  259. for s in node.outputs:
  260. socket_lists = create_socket_lists(s, path, lines_from_socket(s, path))
  261. for line in socket_lists:
  262. in_line = line.copy()
  263. input_lines.append(in_line)
  264. # NOT SURE if any of this stuff below is necesary at all
  265. return (input_lines) # let's find out if it is
  266. #
  267. # I think the unreachable code here is bad. TODO: figure out if there's
  268. # any reason at all to revive this old code.
  269. #
  270. # I certainly *like* the idea of removing redundant data.
  271. #
  272. # note: it seems like the Execute Tree function is completing
  273. # in 80% of the previous time after removing the below
  274. # meanign that this was wasting 1/5 of the total time
  275. no_dupes_sigs = set()
  276. no_dupes_lines = set()
  277. for in_line in input_lines:
  278. sig = get_socket_signature(in_line[-1])
  279. sig = list(sig)
  280. sig.append(in_line[-1][0].name) # socket
  281. sig = tuple(sig)
  282. before = len(no_dupes_sigs)
  283. no_dupes_sigs.add(sig)
  284. after = len(no_dupes_sigs)
  285. # make a tuple of the node path, too.
  286. # in_line = tuple(in_line[0]), in_line[1]
  287. if (before < after): # new item
  288. no_dupes_lines.add(tuple_of_line(in_line))
  289. #MAYBE
  290. # maybe i can get a list of all nodes
  291. # including nodes in nodegroups and nested node groups
  292. # then I can assign a uuid to each one
  293. # and associate the uuids with the node lines
  294. # perhaps I should do that before running this function
  295. # for line in no_dupes_lines:
  296. # print (list(line))
  297. return (list(no_dupes_lines))
  298. # don't deal with lines no mo. Do stuff with line elements
  299. # DO THIS!
  300. # make lines_from_socket attach a tree path to each node-socket, instead of just a tree
  301. # that way, if the tree-path is longer than the socket-path, the tree path won't be truncated.
  302. # for use with node signatures
  303. def tree_from_nc(sig, base_tree):
  304. if (sig[0] == 'MANTIS_AUTOGENERATED'):
  305. sig = sig[:-2] # cut off the input part of the signature.
  306. tree = base_tree
  307. for i, path_item in enumerate(sig):
  308. if (i == 0) or (i == len(sig) - 1):
  309. continue
  310. tree = tree.nodes.get(path_item).node_tree
  311. return tree
  312. def get_node_prototype(sig, base_tree):
  313. return tree_from_nc(sig, base_tree).nodes.get( sig[-1] )
  314. ##################################################################################################
  315. # misc
  316. ##################################################################################################
  317. # This will not work with float properties. Use them directly.
  318. # this is an extremely idiotic way to do this
  319. # it's also slow!
  320. # TODO fix this
  321. #using isinstance is the most lizard-brained way to do this, utter idiocy.
  322. def to_mathutils_value(socket):
  323. if (hasattr(socket, "default_value")):
  324. val = socket.default_value
  325. from mathutils import Matrix, Euler, Quaternion, Vector
  326. from bpy.types import (NodeSocketVector, NodeSocketVectorAcceleration,
  327. NodeSocketVectorDirection, NodeSocketVectorEuler,
  328. NodeSocketVectorTranslation, NodeSocketVectorVelocity,
  329. NodeSocketVectorXYZ,)
  330. from . import socket_definitions
  331. if ((isinstance(socket, NodeSocketVector)) or
  332. (isinstance(socket, NodeSocketVectorAcceleration)) or
  333. (isinstance(socket, NodeSocketVectorDirection)) or
  334. (isinstance(socket, NodeSocketVectorTranslation)) or
  335. (isinstance(socket, NodeSocketVectorXYZ)) or
  336. (isinstance(socket, NodeSocketVectorVelocity)) or
  337. (isinstance(socket, socket_definitions.VectorSocket)) or
  338. (isinstance(socket, socket_definitions.VectorEulerSocket)) or
  339. (isinstance(socket, socket_definitions.VectorTranslationSocket)) or
  340. (isinstance(socket, socket_definitions.VectorScaleSocket)) or
  341. (isinstance(socket, socket_definitions.ParameterVectorSocket))):
  342. return (Vector(( val[0], val[1], val[2], )))
  343. if (isinstance(socket, NodeSocketVectorEuler)):
  344. return (Euler(( val[0], val[1], val[2])), 'XYZ',) #TODO make choice
  345. if (isinstance(socket, socket_definitions.MatrixSocket)):
  346. # return val #Blender makes it a Matrix for me <3
  347. # nevermind... BLENDER HAS BETRAYED ME
  348. return socket.TellValue()
  349. if (isinstance(socket,socket_definitions.QuaternionSocket)):
  350. return (Quaternion( (val[0], val[1], val[2], val[3],)) )
  351. if (isinstance(socket,socket_definitions.QuaternionSocketAA)):
  352. return (Quaternion( (val[1], val[2], val[3],), val[0], ) )
  353. if ((isinstance(socket, socket_definitions.FloatSocket)) or
  354. (isinstance(socket, socket_definitions.ParameterIntSocket)) or
  355. (isinstance(socket, socket_definitions.ParameterFloatSocket))):
  356. return val
  357. if (isinstance(socket, socket_definitions.BooleanThreeTupleSocket)):
  358. return (val[0], val[1], val[2]) # we'll send a tuple out
  359. # if ((isinstance(socket, socket_definitions.LayerMaskSocket)) or
  360. # (isinstance(socket, socket_definitions.LayerMaskInputSocket))):
  361. # return tuple(val) # should werk
  362. else:
  363. return None