base_definitions.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. #Mantis Nodes Base
  2. import bpy
  3. from bpy.props import BoolProperty, StringProperty, EnumProperty, CollectionProperty, IntProperty, PointerProperty, BoolVectorProperty
  4. from . import ops_nodegroup
  5. from bpy.types import NodeTree, Node, PropertyGroup, Operator, UIList, Panel
  6. from .utilities import (prRed, prGreen, prPurple, prWhite,
  7. prOrange,
  8. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  9. wrapOrange,)
  10. from .utilities import get_socket_maps, relink_socket_map, do_relink
  11. from bpy.app.handlers import persistent
  12. def TellClasses():
  13. #Why use a function to do this? Because I don't need every class to register.
  14. return [ MantisTree,
  15. SchemaTree,
  16. # MantisNode,
  17. # SchemaNode,
  18. MantisNodeGroup,
  19. SchemaGroup,
  20. MantisVisualizeTree,
  21. MantisVisualizeNode,
  22. ]
  23. class MantisTree(NodeTree):
  24. '''A custom node tree type that will show up in the editor type list'''
  25. bl_idname = 'MantisTree'
  26. bl_label = "Rigging Nodes"
  27. bl_icon = 'OUTLINER_OB_ARMATURE'
  28. tree_valid:BoolProperty(default=False)
  29. do_live_update:BoolProperty(default=True) # use this to disable updates for e.g. scripts
  30. num_links:IntProperty(default=-1)
  31. filepath:StringProperty(default="", subtype='FILE_PATH')
  32. is_executing:BoolProperty(default=False)
  33. is_exporting:BoolProperty(default=False)
  34. execution_id:StringProperty(default='')
  35. parsed_tree={}
  36. def interface_update(self, context):
  37. # no idea what this does
  38. print ("Update Interface function in MantisTree class")
  39. if bpy.app.version >= (3, 2): # in 3.1 this can lead to a crash
  40. @classmethod
  41. def valid_socket_type(cls, socket_type: str):
  42. # https://docs.blender.org/api/master/bpy.types.NodeTree.html#bpy.types.NodeTree.valid_socket_type
  43. from .socket_definitions import Tell_bl_idnames
  44. return socket_type in Tell_bl_idnames()
  45. # thank you, Sverchok
  46. def update_tree(self, context = None):
  47. if self.is_exporting:
  48. return
  49. # return
  50. self.is_executing = True
  51. from . import readtree
  52. prGreen("Validating Tree: %s" % self.name)
  53. try:
  54. self.parsed_tree = readtree.parse_tree(self)
  55. if context:
  56. self.display_update(context)
  57. self.is_executing = False
  58. self.tree_valid = True
  59. except GraphError as e:
  60. prRed("Failed to update node tree due to error.")
  61. self.tree_valid = False
  62. self.is_executing = False
  63. raise e
  64. def display_update(self, context):
  65. current_tree = bpy.context.space_data.path[-1].node_tree
  66. for node in current_tree.nodes:
  67. if hasattr(node, "display_update"):
  68. try:
  69. node.display_update(self.parsed_tree, context)
  70. except Exception as e:
  71. print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
  72. # raise e
  73. def execute_tree(self,context):
  74. if self.is_exporting:
  75. return
  76. # return
  77. prGreen("Executing Tree: %s" % self.name)
  78. self.is_executing = True
  79. from . import readtree
  80. try:
  81. readtree.execute_tree(self.parsed_tree, self, context)
  82. except RecursionError as e:
  83. prRed("Recursion error while parsing tree.")
  84. # prRed(e); node_tree.do_live_update = False
  85. # except Exception:
  86. # pass
  87. finally: # first time I have ever used a finally block in my life.
  88. self.is_executing = False
  89. # class SchemaPropertyGroup(bpy.types.PropertyGroup):
  90. class SchemaTree(NodeTree):
  91. '''A node tree representing a schema to generate a Mantis tree'''
  92. bl_idname = 'SchemaTree'
  93. bl_label = "Rigging Nodes Schema"
  94. bl_icon = 'RIGID_BODY_CONSTRAINT'
  95. tree_valid:BoolProperty(default=False)
  96. do_live_update:BoolProperty(default=True) # use this to disable updates for e.g. scripts
  97. is_executing:BoolProperty(default=False)
  98. num_links:IntProperty(default=-1)
  99. # filepath:StringProperty(default="", subtype='FILE_PATH')
  100. parsed_tree={}
  101. # def update(self):
  102. # for n in self.nodes:
  103. # if hasattr(n, "update"): n.update()
  104. # def update_tree(self, context):
  105. # prRed("update tree for Schema Tree!")
  106. # # self.tree_valid = True
  107. # # return
  108. # from . import readtree
  109. # prGreen("Validating Tree: %s" % self.name)
  110. # parsed_tree = readtree.parse_tree(self)
  111. # self.parsed_tree=parsed_tree
  112. # current_tree = bpy.context.space_data.path[-1].node_tree
  113. # self.tree_valid = True
  114. # prWhite("Number of Nodes: %s" % (len(self.parsed_tree)))
  115. # self.display_update(context)
  116. # def display_update(self, context):
  117. # prRed("display update for Schema Tree!")
  118. # return
  119. # current_tree = bpy.context.space_data.path[-1].node_tree
  120. # for node in current_tree.nodes:
  121. # if hasattr(node, "display_update"):
  122. # try:
  123. # node.display_update(self.parsed_tree, context)
  124. # except Exception as e:
  125. # print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
  126. # # raise e
  127. # def execute_tree(self,context):
  128. # self.is_executing = True
  129. # prRed("executing Schema Tree!")
  130. # self.tree_valid = False
  131. # self.is_executing = False
  132. # return
  133. # prGreen("Executing Tree: %s" % self.name)
  134. # from . import readtree
  135. # try:
  136. # readtree.execute_tree(self.parsed_tree, self, context)
  137. # except RecursionError as e:
  138. # prRed("Recursion error while parsing tree.")
  139. # prRed(e); node_tree.do_live_update = False
  140. if bpy.app.version >= (3, 2): # in 3.1 this can lead to a crash
  141. @classmethod
  142. def valid_socket_type(cls, socket_type: str):
  143. # https://docs.blender.org/api/master/bpy.types.NodeTree.html#bpy.types.NodeTree.valid_socket_type
  144. from .socket_definitions import Tell_bl_idnames
  145. return socket_type in Tell_bl_idnames()
  146. # thank you, Sverchok
  147. class MantisNode:
  148. # num_links:IntProperty(default=-1) # is this used anywhere?
  149. # is_triggering_execute:BoolProperty(default=False)
  150. # do_display_update:BoolProperty(default=False)
  151. @classmethod
  152. def poll(cls, ntree):
  153. return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
  154. def insert_link(self, link):
  155. context = bpy.context
  156. if context.space_data:
  157. node_tree = context.space_data.path[0].node_tree
  158. from . import readtree
  159. if node_tree.do_live_update:
  160. # prOrange("Updating from insert_link callback")
  161. node_tree.update_tree(context)
  162. if (link.to_socket.is_linked == False):
  163. node_tree.num_links+=1
  164. elif (link.to_socket.is_multi_input):# and
  165. #len(link.to_socket.links) < link.to_socket.link_limit ):
  166. # the above doesn't work and I can't be bothered to fix it right now TODO
  167. node_tree.num_links+=1
  168. class SchemaNode:
  169. # is_triggering_execute:BoolProperty(default=False)
  170. # do_display_update:BoolProperty(default=False)
  171. @classmethod
  172. def poll(cls, ntree):
  173. return (ntree.bl_idname in ['SchemaTree'])
  174. class LinkNode(MantisNode):
  175. useTarget : BoolProperty(default=False)
  176. @classmethod
  177. def poll(cls, ntree):
  178. return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
  179. class xFormNode(MantisNode):
  180. @classmethod
  181. def poll(cls, ntree):
  182. return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
  183. class DeformerNode(MantisNode):
  184. @classmethod
  185. def poll(cls, ntree):
  186. return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
  187. # from bpy.types import NodeCustomGroup
  188. def poll_node_tree(self, object):
  189. if isinstance(object, MantisTree):
  190. return True
  191. return False
  192. # TODO this should check ID's instead of name
  193. def node_group_update(node):
  194. toggle_update = node.id_data.do_live_update
  195. node.id_data.do_live_update = False
  196. # prWhite (node.name, len(node.inputs), len(node.outputs))
  197. identifiers_in={socket.identifier:socket for socket in node.inputs}
  198. identifiers_out={socket.identifier:socket for socket in node.outputs}
  199. if node.node_tree is None:
  200. node.inputs.clear(); node.outputs.clear()
  201. node.id_data.do_live_update = toggle_update
  202. return
  203. found_in, found_out = [], []
  204. update_input, update_output = False, False
  205. for item in node.node_tree.interface.items_tree:
  206. if item.item_type != "SOCKET": continue
  207. if item.in_out == 'OUTPUT':
  208. if s:= identifiers_out.get(item.identifier): # if the requested output doesn't exist, update
  209. found_out.append(item.identifier)
  210. if update_output: continue # done here
  211. if s.bl_idname != item.socket_type: update_output = True; continue
  212. else: update_output = True; continue # prRed(f"Not found: {item.name}");
  213. else:
  214. if s:= identifiers_in.get(item.identifier): # if the requested input doesn't exist, update
  215. found_in.append(item.identifier)
  216. if update_input: continue # done here
  217. if s.bl_idname != item.socket_type: update_input = True; continue
  218. else: update_input = True; continue # prGreen(f"Not found: {item.name}");
  219. # Schema has an extra input for Length and for Extend.
  220. if node.bl_idname == 'MantisSchemaGroup':
  221. found_in.extend(['Schema Length', ''])
  222. # if we have too many elements, just get rid of the ones we don't need
  223. if len(node.inputs) > len(found_in):#
  224. for inp in node.inputs:
  225. if inp.identifier in found_in: continue
  226. node.inputs.remove(inp)
  227. if len(node.outputs) > len(found_out):
  228. for out in node.outputs:
  229. if out.identifier in found_out: continue
  230. node.outputs.remove(out)
  231. #
  232. if len(node.inputs) > 0 and (inp := node.inputs[-1]).bl_idname == 'WildcardSocket' and inp.is_linked:
  233. # prPurple("oink! I am a piggy!")
  234. update_input = True
  235. if len(node.outputs) > 0 and (out := node.outputs[-1]).bl_idname == 'WildcardSocket' and out.is_linked:
  236. # prPurple("woof! I am a doggy!")
  237. update_output = True
  238. #
  239. if not (update_input or update_output):
  240. node.id_data.do_live_update = toggle_update
  241. return
  242. if update_input or update_output:
  243. socket_map_in, socket_map_out = get_socket_maps(node)
  244. if update_input :
  245. if node.bl_idname == 'MantisSchemaGroup':
  246. schema_length=0
  247. if sl := node.inputs.get("Schema Length"):
  248. schema_length = sl.default_value
  249. # sometimes this isn't available yet # TODO not happy about this solution
  250. node.inputs.clear()
  251. if node.bl_idname == 'MantisSchemaGroup':
  252. node.inputs.new("IntSocket", "Schema Length", identifier='Schema Length')
  253. node.inputs['Schema Length'].default_value = schema_length
  254. if update_output: node.outputs.clear()
  255. for item in node.node_tree.interface.items_tree:
  256. if item.item_type != "SOCKET": continue
  257. if (item.in_out == 'INPUT' and update_input):
  258. relink_socket_map(node, node.inputs, socket_map_in, item)
  259. if (item.in_out == 'OUTPUT' and update_output):
  260. relink_socket_map(node, node.outputs, socket_map_out, item)
  261. # at this point there is no wildcard socket
  262. if '__extend__' in socket_map_in.keys():
  263. do_relink(node, None, socket_map_in, in_out='INPUT', parent_name='Constant' )
  264. node.id_data.do_live_update = toggle_update
  265. def node_tree_prop_update(self, context):
  266. if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
  267. return
  268. # prGreen("updating me...")
  269. self.is_updating = True
  270. node_group_update(self)
  271. self.is_updating = False
  272. if self.bl_idname in ['MantisSchemaGroup'] and self.node_tree is not None:
  273. if len(self.inputs) == 0:
  274. self.inputs.new("IntSocket", "Schema Length", identifier='Schema Length')
  275. if self.inputs[-1].bl_idname != "WildcardSocket":
  276. self.inputs.new("WildcardSocket", "", identifier="__extend__")
  277. from bpy.types import NodeCustomGroup
  278. class MantisNodeGroup(Node, MantisNode):
  279. bl_idname = "MantisNodeGroup"
  280. bl_label = "Node Group"
  281. node_tree:PointerProperty(type=NodeTree, poll=poll_node_tree, update=node_tree_prop_update,)
  282. is_updating:BoolProperty(default=False)
  283. def update(self):
  284. if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
  285. return
  286. self.is_updating = True
  287. node_group_update(self)
  288. self.is_updating = False
  289. def draw_buttons(self, context, layout):
  290. row = layout.row(align=True)
  291. row.prop(self, "node_tree", text="")
  292. row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
  293. class CircularDependencyError(Exception):
  294. pass
  295. class GraphError(Exception):
  296. pass
  297. def get_signature_from_edited_tree(node, context):
  298. sig_path=[None,]
  299. for item in context.space_data.path[:-1]:
  300. sig_path.append(item.node_tree.nodes.active.name)
  301. return tuple(sig_path+[node.name])
  302. def poll_node_tree_schema(self, object):
  303. if isinstance(object, SchemaTree):
  304. return True
  305. return False
  306. # def update_schema_length(self, context):
  307. # pass # for now
  308. # TODO tiny UI problem - inserting new links into the tree will not place them in the right place.
  309. #this is a schema node in a mantis tree... kinda confusing
  310. class SchemaGroup(Node, MantisNode):
  311. bl_idname = "MantisSchemaGroup"
  312. bl_label = "Node Schema"
  313. node_tree:PointerProperty(type=NodeTree, poll=poll_node_tree_schema, update=node_tree_prop_update,)
  314. # schema_length:IntProperty(default=5, update=update_schema_length)
  315. is_updating:BoolProperty(default=False)
  316. # incoming link
  317. # from-node = name
  318. # from socket = index or identifier or something
  319. # property is unset as soon as it is used
  320. # so the update function checks this property and handles the incoming link in an allowed place
  321. # and actually the handle-link function can work as a switch - when the property is unset, it allows new links
  322. # otherwise it unsets the property and returns.
  323. # def init(self, context):
  324. # self.inputs.new("IntSocket", "Schema Length")
  325. # self.inputs.new("WildcardSocket", "")
  326. def draw_buttons(self, context, layout):
  327. row = layout.row(align=True)
  328. row.prop(self, "node_tree", text="")
  329. row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
  330. # layout.prop(self, "schema_length", text="n=")
  331. # WHAT IF:
  332. # - the function that creates the input/output map returns to a property in the node
  333. # - then the node handles the update in its update function.
  334. def update(self):
  335. if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
  336. return
  337. self.is_updating = True
  338. node_group_update(self)
  339. # kinda dumb but necessary since update doesn't always fix this
  340. if self.node_tree:
  341. if len(self.inputs) == 0:
  342. self.inputs.new("IntSocket", "Schema Length", identifier='Schema Length')
  343. if self.inputs[-1].bl_idname != "WildcardSocket":
  344. self.inputs.new("WildcardSocket", "", identifier="__extend__")
  345. self.is_updating = False
  346. # def insert_link(self, link):
  347. # if self.node_tree is None:
  348. # link.is_valid = False
  349. # return
  350. # sock_type = link.from_socket.bl_idname
  351. # for i, sock in enumerate(self.inputs):
  352. # if sock == link.to_socket: # dumb but whatever
  353. # identifier = link.to_socket.identifier
  354. # if sock.bl_idname not in ["WildcardSocket"]:
  355. # if sock.is_linked == True:
  356. # links = [ getattr(l, "from_socket") for l in sock.links ]
  357. # name = sock.name
  358. # # self.inputs.remove(sock)
  359. # # new_input = self.inputs.new(sock_type, name, identifier=identifier, use_multi_input=True); self.inputs.move(-1, i)
  360. # sock.display_shape = 'SQUARE_DOT'
  361. # interface_item = self.node_tree.interface.items_tree[name]
  362. # if not (interface_parent := self.node_tree.interface.items_tree.get('Array')):
  363. # interface_parent = self.node_tree.interface.new_panel(name='Array')
  364. # self.node_tree.interface.move_to_parent(interface_item, interface_parent, len(interface_parent.interface_items))
  365. # # sock.link_limit = self.schema_length TODO this will be very hard to get at this point
  366. # # self.id_data.links.new()
  367. # else: #if link.to_socket == self.inputs[-1]:
  368. # self.inputs.remove(sock)#self.inputs[-1])
  369. # #
  370. # name_stem = link.from_socket.bl_idname.replace('Socket',''); num=0
  371. # if hasattr(link.from_socket, "default_value"):
  372. # name_stem = type(link.from_socket.default_value).__name__
  373. # for n in self.inputs:
  374. # if name_stem in n.name: num+=1
  375. # name = name_stem + '.' + str(num).zfill(3)
  376. # #
  377. # new_input = self.inputs.new(sock_type, name, identifier=identifier, use_multi_input=False); self.inputs.move(-1, i+1)
  378. # new_input.link_limit = 1
  379. # # link.to_socket = new_input
  380. # # this seems to work
  381. # self.inputs.new("WildcardSocket", "")
  382. # if not (interface_parent := self.node_tree.interface.items_tree.get('Constant')):
  383. # interface_parent = self.node_tree.interface.new_panel(name='Constant')
  384. # self.node_tree.interface.new_socket(name=name,in_out='INPUT', socket_type=sock_type, parent=interface_parent)
  385. # return
  386. # TODO: investigate whether this is necessary
  387. # @classmethod
  388. # def poll(cls, ntree):
  389. # return (ntree.bl_idname in ['MantisTree'])
  390. # handlers!
  391. #annoyingly these have to be persistent
  392. @persistent
  393. def update_handler(scene):
  394. context=bpy.context
  395. if context.space_data:
  396. if not hasattr(context.space_data, "path"):
  397. return
  398. trees = [p.node_tree for p in context.space_data.path]
  399. if not trees: return
  400. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  401. if node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
  402. prev_links = node_tree.num_links
  403. node_tree.num_links = len(node_tree.links)
  404. if (prev_links == -1):
  405. return
  406. if prev_links != node_tree.num_links:
  407. node_tree.tree_valid = False
  408. if node_tree.tree_valid == False:
  409. node_tree.update_tree(context)
  410. @persistent
  411. def execute_handler(scene):
  412. context = bpy.context
  413. if context.space_data:
  414. if not hasattr(context.space_data, "path"):
  415. return
  416. trees = [p.node_tree for p in context.space_data.path]
  417. if not trees: return
  418. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  419. if node_tree.tree_valid and node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
  420. node_tree.execute_tree(context)
  421. node_tree.tree_valid = False
  422. # @persistent
  423. # def load_post_handler(scene):
  424. # print ("cuss and darn")
  425. # # import bpy
  426. # import sys
  427. # def wrapRed(skk): return "\033[91m{}\033[00m".format(skk)
  428. # def intercept(fn, *args):
  429. # print(wrapRed("Intercepting:"), fn)
  430. # sys.stdout.flush()
  431. # fn(*args)
  432. # print(wrapRed("... done"))
  433. # sys.stdout.flush()
  434. # for attr in dir(bpy.app.handlers):
  435. # if attr.startswith("_"):
  436. # continue
  437. # handler_list = getattr(bpy.app.handlers, attr)
  438. # if attr =='load_post_handler':
  439. # continue
  440. # if not isinstance(handler_list, list):
  441. # continue
  442. # if not handler_list:
  443. # continue
  444. # print("Intercept Setup:", attr)
  445. # handler_list[:] = [lambda *args: intercept(fn, *args) for fn in handler_list]
  446. # # import cProfile
  447. # from os import environ
  448. # do_profile=False
  449. # print (environ.get("DOPROFILE"))
  450. # if environ.get("DOPROFILE"):
  451. # do_profile=True
  452. # if do_profile:
  453. # # cProfile.runctx("tree.update_tree(context)", None, locals())
  454. # # cProfile.runctx("tree.execute_tree(context)", None, locals())
  455. # import hunter
  456. # hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=False))
  457. # # def msgbus_callback(*args):
  458. # # # print("something changed!")
  459. # # print("Something changed!", args)
  460. # # owner = object()
  461. # subscribe_to = (bpy.types.Node, "location")
  462. # subscribe_to = (bpy.types.Node, "color")
  463. # subscribe_to = (bpy.types.Node, "dimensions")
  464. # subscribe_to = (bpy.types.Node, "height")
  465. # subscribe_to = (bpy.types.Node, "width")
  466. # subscribe_to = (bpy.types.Node, "inputs")
  467. # subscribe_to = (bpy.types.Node, "outputs")
  468. # subscribe_to = (bpy.types.Node, "select")
  469. # subscribe_to = (bpy.types.Node, "name")
  470. # subscribe_to = (bpy.types.NodeSocket, "name")
  471. # subscribe_to = (bpy.types.NodeSocket, "display_shape")
  472. # bpy.msgbus.subscribe_rna(
  473. # key=subscribe_to,
  474. # owner=owner,
  475. # args=(1, 2, 3),
  476. # notify=msgbus_callback,
  477. # )
  478. # print ("cuss and darn")
  479. # bpy.app.handlers.load_post.append(set_tree_invalid)
  480. bpy.app.handlers.depsgraph_update_pre.append(update_handler)
  481. bpy.app.handlers.depsgraph_update_post.append(execute_handler)
  482. # bpy.app.handlers.load_post.append(load_post_handler)
  483. # # import bpy
  484. # import sys
  485. # def wrapRed(skk): return "\033[91m{}\033[00m".format(skk)
  486. # def intercept(fn, *args):
  487. # print(wrapRed("Intercepting:"), fn)
  488. # sys.stdout.flush()
  489. # fn(*args)
  490. # print(wrapRed("... done"))
  491. # sys.stdout.flush()
  492. # for attr in dir(bpy.app.handlers):
  493. # if attr.startswith("_"):
  494. # continue
  495. # handler_list = getattr(bpy.app.handlers, attr)
  496. # if attr =='load_post_handler':
  497. # continue
  498. # if not isinstance(handler_list, list):
  499. # continue
  500. # if not handler_list:
  501. # continue
  502. # print("Intercept Setup:", attr)
  503. # handler_list[:] = [lambda *args: intercept(fn, *args) for fn in handler_list]
  504. class MantisVisualizeTree(NodeTree):
  505. '''A custom node tree type that will show up in the editor type list'''
  506. bl_idname = 'MantisVisualizeTree'
  507. bl_label = "mantis output"
  508. bl_icon = 'HIDE_OFF'
  509. class MantisVisualizeNode(Node):
  510. bl_idname = "MantisVisualizeNode"
  511. bl_label = "Node"
  512. @classmethod
  513. def poll(cls, ntree):
  514. return (ntree.bl_idname in ['MantisVisualizeTree'])
  515. def init(self, context):
  516. pass
  517. def gen_data(self, nc, np = None):
  518. self.use_custom_color = True
  519. if nc.node_type == 'XFORM':
  520. self.color = (1.0 ,0.5, 0.0)
  521. if nc.node_type == 'LINK':
  522. self.color = (0.4 ,0.2, 1.0)
  523. if nc.node_type == 'UTILITY':
  524. self.color = (0.2 ,0.2, 0.2)
  525. if nc.node_type == 'SCHEMA':
  526. self.color = (0.85 ,0.95, 0.9)
  527. if nc.node_type == 'DUMMY':
  528. self.color = (0.05 ,0.05, 0.15)
  529. self.name = ''.join(nc.signature[1:])
  530. if np:
  531. if np.label:
  532. self.label=np.label
  533. else:
  534. self.label=np.name
  535. for inp in nc.inputs:
  536. s = self.inputs.new('WildcardSocket', inp)
  537. try:
  538. if sock := np.inputs.get(inp):
  539. s.color = inp.color
  540. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  541. pass
  542. except KeyError:
  543. pass
  544. for out in nc.outputs:
  545. s = self.outputs.new('WildcardSocket', out)
  546. try:
  547. if sock := np.outputs.get(out):
  548. s.color = out.color
  549. except AttributeError: #default bl_idname types like Float and Vector, no biggie
  550. pass
  551. except KeyError:
  552. pass
  553. self.location = np.location # will get overwritten by Grandalf later.
  554. else:
  555. self.label = nc.signature[-1] # which is be the unique name.
  556. for inp in nc.inputs:
  557. self.inputs.new('WildcardSocket', inp)
  558. for out in nc.outputs:
  559. self.outputs.new('WildcardSocket', out)
  560. # replace names with bl_idnames for reading the tree and solving schemas.
  561. replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection",
  562. "SchemaArrayInput", "SchemaConstInput", "SchemaConstOutput", "SchemaIndex",
  563. "SchemaOutgoingConnection", "SchemaConstantOutput", "SchemaArrayOutput",
  564. "SchemaArrayInputGet",]
  565. # anything that gets properties added in the graph... this is a clumsy approach but I need to watch for this
  566. # in schema generation and this is the easiest way to do it for now.
  567. custom_props_types = ["LinkArmature", "UtilityKeyframe", "UtilityFCurve", "UtilityDriver", "xFormBone"]
  568. from_name_filter = ["Driver", ]
  569. to_name_filter = [
  570. "Custom Object xForm Override",
  571. "Custom Object",
  572. "Deform Bones",
  573. ]