|  | @@ -88,7 +88,7 @@ class MantisTree(NodeTree):
 | 
	
		
			
				|  |  |      bl_idname = 'MantisTree'
 | 
	
		
			
				|  |  |      bl_label = "Rigging Nodes"
 | 
	
		
			
				|  |  |      bl_icon = 'OUTLINER_OB_ARMATURE'
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      tree_valid:BoolProperty(default=False)
 | 
	
		
			
				|  |  |      hash:StringProperty(default='')
 | 
	
		
			
				|  |  |      do_live_update:BoolProperty(default=True) # use this to disable updates for e.g. scripts
 | 
	
	
		
			
				|  | @@ -109,14 +109,14 @@ class MantisTree(NodeTree):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #added to work around a bug in 4.5.0 LTS
 | 
	
		
			
				|  |  |      interface_helper : StringProperty(default='')
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      parsed_tree={}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (bpy.app.version < (4, 4, 0) or bpy.app.version >= (4,5,0)):  # in 4.4 this leads to a crash
 | 
	
		
			
				|  |  |          @classmethod
 | 
	
		
			
				|  |  |          def valid_socket_type(cls : NodeTree, socket_idname: str):
 | 
	
		
			
				|  |  |              return valid_interface_types(cls, socket_idname)
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def update(self): # set the reroute colors
 | 
	
		
			
				|  |  |          if (bpy.app.version >= (4,4,0)):
 | 
	
		
			
				|  |  |              fix_reroute_colors(self)
 | 
	
	
		
			
				|  | @@ -136,6 +136,8 @@ class MantisTree(NodeTree):
 | 
	
		
			
				|  |  |                  scene = bpy.context.scene
 | 
	
		
			
				|  |  |                  scene.render.use_lock_interface = True
 | 
	
		
			
				|  |  |                  self.parsed_tree = readtree.parse_tree(self, error_popups)
 | 
	
		
			
				|  |  | +                from .visualize import visualize_tree
 | 
	
		
			
				|  |  | +                visualize_tree(self.parsed_tree, self, context)
 | 
	
		
			
				|  |  |                  if context:
 | 
	
		
			
				|  |  |                      self.display_update(context)
 | 
	
		
			
				|  |  |                  self.tree_valid = True
 | 
	
	
		
			
				|  | @@ -160,7 +162,7 @@ class MantisTree(NodeTree):
 | 
	
		
			
				|  |  |                  except Exception as e:
 | 
	
		
			
				|  |  |                      print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
 | 
	
		
			
				|  |  |          self.is_executing = False
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          # TODO: deal with invalid links properly.
 | 
	
		
			
				|  |  |          #    - Non-hierarchy links should be ignored in the circle-check and so the links should be marked valid in such a circle
 | 
	
		
			
				|  |  |          #    - hierarchy-links should be marked invalid and prevent the tree from executing.
 | 
	
	
		
			
				|  | @@ -225,7 +227,7 @@ class MantisSocketTemplate():
 | 
	
		
			
				|  |  |      hide             : bool = field(default=False)
 | 
	
		
			
				|  |  |      use_multi_input  : bool = field(default=False)
 | 
	
		
			
				|  |  |      default_value    : Any = field(default=None)
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #TODO: do a better job explaining how MantisNode and MantisUINode relate.
 | 
	
	
		
			
				|  | @@ -242,7 +244,7 @@ class MantisUINode:
 | 
	
		
			
				|  |  |      @classmethod
 | 
	
		
			
				|  |  |      def poll(cls, ntree):
 | 
	
		
			
				|  |  |          return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
 | 
	
		
			
				|  |  | -                
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      @classmethod
 | 
	
		
			
				|  |  |      def set_mantis_class(self):
 | 
	
		
			
				|  |  |          from importlib import import_module
 | 
	
	
		
			
				|  | @@ -266,7 +268,7 @@ class MantisUINode:
 | 
	
		
			
				|  |  |                      node_tree.num_links+=1
 | 
	
		
			
				|  |  |                  elif (link.to_socket.is_multi_input):
 | 
	
		
			
				|  |  |                      node_tree.num_links+=1
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def init_sockets(self, socket_templates : tuple[MantisSocketTemplate]):
 | 
	
		
			
				|  |  |          for template in socket_templates:
 | 
	
		
			
				|  |  |              collection = self.outputs
 | 
	
	
		
			
				|  | @@ -292,7 +294,7 @@ class MantisUINode:
 | 
	
		
			
				|  |  |                  #   responsibility to send the right type.
 | 
	
		
			
				|  |  |              if template.use_multi_input: # this is an array
 | 
	
		
			
				|  |  |                  socket.display_shape = 'SQUARE_DOT'
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class SchemaUINode(MantisUINode):
 | 
	
		
			
				|  |  |      mantis_node_library='.schema_nodes'
 | 
	
		
			
				|  |  |      is_updating:BoolProperty(default=False)
 | 
	
	
		
			
				|  | @@ -305,7 +307,7 @@ class LinkNode(MantisUINode):
 | 
	
		
			
				|  |  |      @classmethod
 | 
	
		
			
				|  |  |      def poll(cls, ntree):
 | 
	
		
			
				|  |  |          return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class xFormNode(MantisUINode):
 | 
	
		
			
				|  |  |      mantis_node_library='.xForm_nodes'
 | 
	
		
			
				|  |  |      @classmethod
 | 
	
	
		
			
				|  | @@ -394,11 +396,11 @@ def node_group_update(node, force = False):
 | 
	
		
			
				|  |  |                  if update_input: continue # done here
 | 
	
		
			
				|  |  |                  if s.bl_idname != item.bl_socket_idname: update_input = True; continue
 | 
	
		
			
				|  |  |              else: update_input = True; continue
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # Schema has an extra input for Length and for Extend.
 | 
	
		
			
				|  |  |      if node.bl_idname == 'MantisSchemaGroup':
 | 
	
		
			
				|  |  |          found_in.extend(['Schema Length', ''])
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # get the socket maps before modifying stuff
 | 
	
		
			
				|  |  |      if update_input or update_output:
 | 
	
		
			
				|  |  |          socket_maps = get_socket_maps(node,)
 | 
	
	
		
			
				|  | @@ -411,7 +413,7 @@ def node_group_update(node, force = False):
 | 
	
		
			
				|  |  |              # We have to initialize the node because it only has its base inputs.
 | 
	
		
			
				|  |  |          elif socket_maps is None:
 | 
	
		
			
				|  |  |              node.id_data.do_live_update = toggle_update
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # if we have too many elements, just get rid of the ones we don't need
 | 
	
		
			
				|  |  |      if len(node.inputs) > len(found_in):#
 | 
	
		
			
				|  |  |          for inp in node.inputs:
 | 
	
	
		
			
				|  | @@ -447,7 +449,7 @@ def node_group_update(node, force = False):
 | 
	
		
			
				|  |  |                      remove_me.append(socket)
 | 
	
		
			
				|  |  |              while remove_me:
 | 
	
		
			
				|  |  |                  node.inputs.remove(remove_me.pop())
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          if update_output:
 | 
	
		
			
				|  |  |              remove_me=[]
 | 
	
		
			
				|  |  |              for socket in node.outputs:
 | 
	
	
		
			
				|  | @@ -571,10 +573,10 @@ class MantisNodeGroup(Node, MantisUINode):
 | 
	
		
			
				|  |  |              return "Node Group"
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              return self.node_tree.name
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def draw_buttons(self, context, layout):
 | 
	
		
			
				|  |  |          group_draw_buttons(self, context, layout)
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def update(self):
 | 
	
		
			
				|  |  |          if self.node_tree is None:
 | 
	
		
			
				|  |  |              return
 | 
	
	
		
			
				|  | @@ -609,7 +611,7 @@ def poll_node_tree_schema(self, object):
 | 
	
		
			
				|  |  |  class SchemaGroup(Node, MantisUINode):
 | 
	
		
			
				|  |  |      bl_idname = "MantisSchemaGroup"
 | 
	
		
			
				|  |  |      bl_label = "Node Schema"
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      node_tree:PointerProperty(type=NodeTree, poll=poll_node_tree_schema, update=node_tree_prop_update,)
 | 
	
		
			
				|  |  |      is_updating:BoolProperty(default=False)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -621,7 +623,7 @@ class SchemaGroup(Node, MantisUINode):
 | 
	
		
			
				|  |  |              return "Schema Group"
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              return self.node_tree.name
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def update(self):
 | 
	
		
			
				|  |  |          if self.is_updating: # update() can be called from update() and that leads to an infinite loop.
 | 
	
		
			
				|  |  |              return           # so we check if an update is currently running.
 | 
	
	
		
			
				|  | @@ -740,12 +742,12 @@ class MantisNode:
 | 
	
		
			
				|  |  |      @property
 | 
	
		
			
				|  |  |      def name(self):
 | 
	
		
			
				|  |  |          return self.ui_signature[-1]
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      @property
 | 
	
		
			
				|  |  |      def bl_idname(self): # this and the above exist solely to maintain interface w/bpy.types.Node
 | 
	
		
			
				|  |  |          from .utilities import get_node_prototype
 | 
	
		
			
				|  |  |          return get_node_prototype(self.ui_signature, self.base_tree).bl_idname
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def reset_execution(self) -> None:
 | 
	
		
			
				|  |  |          """ Reset the node for additional execution without re-building the tree."""
 | 
	
		
			
				|  |  |          self.drivers={}; self.bObject=None
 | 
	
	
		
			
				|  | @@ -763,7 +765,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |              self.parameters[socket.name] = None
 | 
	
		
			
				|  |  |          for key, value in additional_parameters.items():
 | 
	
		
			
				|  |  |              self.parameters[key]=value
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def gen_property_socket_map(self) -> dict:
 | 
	
		
			
				|  |  |          props_sockets = {}
 | 
	
		
			
				|  |  |          for s_template in self.socket_templates:
 | 
	
	
		
			
				|  | @@ -775,7 +777,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |                  for index, sub_prop in enumerate(s_template.blender_property):
 | 
	
		
			
				|  |  |                      props_sockets[sub_prop]=( (s_template.name, index),s_template.default_value[index] )
 | 
	
		
			
				|  |  |          return props_sockets
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def set_traverse(self, traversal_pairs = [(str, str)]) -> None:
 | 
	
		
			
				|  |  |          for (a, b) in traversal_pairs:
 | 
	
		
			
				|  |  |              self.inputs[a].set_traverse_target(self.outputs[b])
 | 
	
	
		
			
				|  | @@ -786,12 +788,12 @@ class MantisNode:
 | 
	
		
			
				|  |  |              inp.flush_links()
 | 
	
		
			
				|  |  |          for out in self.outputs.values():
 | 
	
		
			
				|  |  |              out.flush_links()
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def update_socket_value(self, blender_property, value) -> bool:
 | 
	
		
			
				|  |  |          change_handled=False
 | 
	
		
			
				|  |  |          if self.node_type == 'LINK':
 | 
	
		
			
				|  |  |              if len(self.bObject) == 0: # - there are no downstream xForms
 | 
	
		
			
				|  |  | -                return True # so there is nothing to do here 
 | 
	
		
			
				|  |  | +                return True # so there is nothing to do here
 | 
	
		
			
				|  |  |              for b_ob in self.bObject:
 | 
	
		
			
				|  |  |                  try:
 | 
	
		
			
				|  |  |                      setattr(b_ob, blender_property, value)
 | 
	
	
		
			
				|  | @@ -841,7 +843,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |                              change_handled=False
 | 
	
		
			
				|  |  |                  break # we don't have to look through any more socket templates
 | 
	
		
			
				|  |  |          return change_handled
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # the goal here is to tag the node as unprepared
 | 
	
		
			
				|  |  |      # but some nodes are always prepared, so we have to kick it forward.
 | 
	
		
			
				|  |  |      def reset_execution_recursive(self):
 | 
	
	
		
			
				|  | @@ -849,7 +851,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |          if self.prepared==False: return # all good from here
 | 
	
		
			
				|  |  |          for conn in self.hierarchy_connections:
 | 
	
		
			
				|  |  |              conn.reset_execution_recursive()
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def evaluate_input(self, input_name, index=0)  -> Any:
 | 
	
		
			
				|  |  |          from .node_container_common import trace_single_line
 | 
	
		
			
				|  |  |          if not (self.inputs.get(input_name)): # get the named parameter if there is no input
 | 
	
	
		
			
				|  | @@ -859,12 +861,12 @@ class MantisNode:
 | 
	
		
			
				|  |  |          trace = trace_single_line(self, input_name, index)
 | 
	
		
			
				|  |  |          prop = trace[0][-1].parameters[trace[1].name] #trace[0] = the list of traced nodes; read its parameters
 | 
	
		
			
				|  |  |          return prop
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def fill_parameters(self, ui_node=None)  -> None:
 | 
	
		
			
				|  |  |          from .utilities import get_node_prototype
 | 
	
		
			
				|  |  |          from .node_container_common import get_socket_value
 | 
	
		
			
				|  |  |          if not ui_node:
 | 
	
		
			
				|  |  | -            if ( (self.signature[0] in  ["MANTIS_AUTOGENERATED", "SCHEMA_AUTOGENERATED" ]) or 
 | 
	
		
			
				|  |  | +            if ( (self.signature[0] in  ["MANTIS_AUTOGENERATED", "SCHEMA_AUTOGENERATED" ]) or
 | 
	
		
			
				|  |  |                  (self.signature[-1] in ["NodeGroupOutput", "NodeGroupInput"]) ): # I think this is harmless
 | 
	
		
			
				|  |  |                  return None
 | 
	
		
			
				|  |  |              else: # BUG shouldn't this use ui_signature??
 | 
	
	
		
			
				|  | @@ -919,7 +921,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |              if self in solved:
 | 
	
		
			
				|  |  |                  break
 | 
	
		
			
				|  |  |          return
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      # gets targets for constraints and deformers and should handle all cases
 | 
	
		
			
				|  |  |      def get_target_and_subtarget(self, constraint_or_deformer, input_name = "Target"):
 | 
	
		
			
				|  |  |          from bpy.types import PoseBone, Object, SplineIKConstraint
 | 
	
	
		
			
				|  | @@ -931,7 +933,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |                  else:
 | 
	
		
			
				|  |  |                      name = 'NAME NOT FOUND'
 | 
	
		
			
				|  |  |                  prRed(f"No {input_name} target found for {name} in {self} because there is no connected node, or node is wrong type")
 | 
	
		
			
				|  |  | -                return 
 | 
	
		
			
				|  |  | +                return
 | 
	
		
			
				|  |  |              if (isinstance(target.bGetObject(), PoseBone)):
 | 
	
		
			
				|  |  |                  subtarget = target.bGetObject().name
 | 
	
		
			
				|  |  |                  target = target.bGetParentArmature()
 | 
	
	
		
			
				|  | @@ -939,7 +941,7 @@ class MantisNode:
 | 
	
		
			
				|  |  |                  target = target.bGetObject()
 | 
	
		
			
				|  |  |              else:
 | 
	
		
			
				|  |  |                  raise RuntimeError("Cannot interpret constraint or deformer target!")
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          if   (isinstance(constraint_or_deformer, SplineIKConstraint)):
 | 
	
		
			
				|  |  |                  if target and target.type not in ["CURVE"]:
 | 
	
		
			
				|  |  |                      raise GraphError(wrapRed("Error: %s requires a Curve input, not %s" %
 | 
	
	
		
			
				|  | @@ -966,12 +968,12 @@ class MantisNode:
 | 
	
		
			
				|  |  |          return
 | 
	
		
			
				|  |  |      def bModifierApply(self, bContext=None):
 | 
	
		
			
				|  |  |          return
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if environ.get("DOERROR"):
 | 
	
		
			
				|  |  | -        def __repr__(self): 
 | 
	
		
			
				|  |  | +        def __repr__(self):
 | 
	
		
			
				|  |  |              return self.signature.__repr__()
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  | -        def __repr__(self): 
 | 
	
		
			
				|  |  | +        def __repr__(self):
 | 
	
		
			
				|  |  |              return self.ui_signature.__repr__()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  # do I need this and the link class above?
 | 
	
	
		
			
				|  | @@ -1007,7 +1009,7 @@ class NodeLink:
 | 
	
		
			
				|  |  |      from_socket = None
 | 
	
		
			
				|  |  |      to_node = None
 | 
	
		
			
				|  |  |      to_socket = None
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __init__(self, from_node, from_socket, to_node, to_socket, multi_input_sort_id=0):
 | 
	
		
			
				|  |  |          if from_node.signature == to_node.signature:
 | 
	
		
			
				|  |  |              raise RuntimeError("Cannot connect a node to itself.")
 | 
	
	
		
			
				|  | @@ -1021,7 +1023,7 @@ class NodeLink:
 | 
	
		
			
				|  |  |          self.to_node.inputs[self.to_socket].links.append(self)
 | 
	
		
			
				|  |  |          self.is_hierarchy = detect_hierarchy_link(from_node, from_socket, to_node, to_socket,)
 | 
	
		
			
				|  |  |          self.is_alive = True
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __repr__(self):
 | 
	
		
			
				|  |  |          return self.from_node.outputs[self.from_socket].__repr__() + " --> " + self.to_node.inputs[self.to_socket].__repr__()
 | 
	
		
			
				|  |  |          # link_string =   # if I need to colorize output for debugging.
 | 
	
	
		
			
				|  | @@ -1029,12 +1031,12 @@ class NodeLink:
 | 
	
		
			
				|  |  |          #     return wrapOrange(link_string)
 | 
	
		
			
				|  |  |          # else:
 | 
	
		
			
				|  |  |          #     return wrapWhite(link_string)
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def die(self):
 | 
	
		
			
				|  |  |          self.is_alive = False
 | 
	
		
			
				|  |  |          self.to_node.inputs[self.to_socket].flush_links()
 | 
	
		
			
				|  |  |          self.from_node.outputs[self.from_socket].flush_links()
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def insert_node(self, middle_node, middle_node_in, middle_node_out, re_init_hierarchy = True):
 | 
	
		
			
				|  |  |          to_node = self.to_node
 | 
	
		
			
				|  |  |          to_socket = self.to_socket
 | 
	
	
		
			
				|  | @@ -1052,7 +1054,7 @@ class NodeSocket:
 | 
	
		
			
				|  |  |      # @property # this is a read-only property.
 | 
	
		
			
				|  |  |      # def is_linked(self):
 | 
	
		
			
				|  |  |      #     return bool(self.links)
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __init__(self, is_input = False,
 | 
	
		
			
				|  |  |                   node = None, name = None,
 | 
	
		
			
				|  |  |                   traverse_target = None):
 | 
	
	
		
			
				|  | @@ -1065,7 +1067,7 @@ class NodeSocket:
 | 
	
		
			
				|  |  |          self.is_linked = False
 | 
	
		
			
				|  |  |          if (traverse_target):
 | 
	
		
			
				|  |  |              self.can_traverse = True
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def connect(self, node, socket, sort_id=0):
 | 
	
		
			
				|  |  |          if  (self.is_input):
 | 
	
		
			
				|  |  |              to_node   = self.node; from_node = node
 | 
	
	
		
			
				|  | @@ -1085,22 +1087,22 @@ class NodeSocket:
 | 
	
		
			
				|  |  |                  to_socket,
 | 
	
		
			
				|  |  |                  sort_id)
 | 
	
		
			
				|  |  |          return new_link
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def set_traverse_target(self, traverse_target):
 | 
	
		
			
				|  |  |          self.traverse_target = traverse_target
 | 
	
		
			
				|  |  |          self.can_traverse = True
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def flush_links(self):
 | 
	
		
			
				|  |  |          """ Removes dead links from this socket."""
 | 
	
		
			
				|  |  |          self.links = [l for l in self.links if l.is_alive]
 | 
	
		
			
				|  |  |          self.links.sort(key=lambda a : -a.multi_input_sort_id)
 | 
	
		
			
				|  |  |          self.is_linked = bool(self.links)
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      @property
 | 
	
		
			
				|  |  |      def is_connected(self):
 | 
	
		
			
				|  |  |          return len(self.links)>0
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __repr__(self):
 | 
	
		
			
				|  |  |          return self.node.__repr__() + "::" + self.name
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1108,7 +1110,7 @@ class MantisNodeSocketCollection(dict):
 | 
	
		
			
				|  |  |      def __init__(self, node, is_input=False):
 | 
	
		
			
				|  |  |          self.is_input = is_input
 | 
	
		
			
				|  |  |          self.node = node
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def init_sockets(self, sockets):
 | 
	
		
			
				|  |  |          for socket in sockets:
 | 
	
		
			
				|  |  |              if isinstance(socket, str):
 | 
	
	
		
			
				|  | @@ -1118,15 +1120,14 @@ class MantisNodeSocketCollection(dict):
 | 
	
		
			
				|  |  |                  self[socket.name] = NodeSocket(is_input=self.is_input, name=socket.name, node=self.node)
 | 
	
		
			
				|  |  |              else:
 | 
	
		
			
				|  |  |                  raise RuntimeError(f"NodeSocketCollection keys must be str or MantisSocketTemplate, not {type(socket)}")
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __delitem__(self, key):
 | 
	
		
			
				|  |  |          """Deletes a node socket by name, and all its links."""
 | 
	
		
			
				|  |  |          socket = self[key]
 | 
	
		
			
				|  |  |          for l in socket.links:
 | 
	
		
			
				|  |  |              l.die()
 | 
	
		
			
				|  |  |          super().__delitem__(key)
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __iter__(self):
 | 
	
		
			
				|  |  |          """Makes the class iterable"""
 | 
	
		
			
				|  |  |          return iter(self.values())
 | 
	
		
			
				|  |  | -
 |