|  | @@ -104,10 +104,40 @@ def tree_from_nc(sig, base_tree):
 | 
	
		
			
				|  |  |              continue
 | 
	
		
			
				|  |  |          tree = tree.nodes.get(path_item).node_tree
 | 
	
		
			
				|  |  |      return tree
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def get_node_prototype(sig, base_tree):
 | 
	
		
			
				|  |  |      return tree_from_nc(sig, base_tree).nodes.get( sig[-1] )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +# This one is the simplest case so it is easiest to use its own function.
 | 
	
		
			
				|  |  | +def set_string_variables_at_creation_time(n, prototype, mContext):
 | 
	
		
			
				|  |  | +    # we're gonna store the variables using the node's signature
 | 
	
		
			
				|  |  | +    group_key = ''.join(n.signature[1:])
 | 
	
		
			
				|  |  | +    # Get the variables if they exist, otherwise just get a dict
 | 
	
		
			
				|  |  | +    group_vars=mContext.string_variables.get(group_key, {})
 | 
	
		
			
				|  |  | +    mContext.string_variables[group_key]=group_vars
 | 
	
		
			
				|  |  | +    for input in prototype.inputs:
 | 
	
		
			
				|  |  | +        if hasattr(input, "default_value") and not input.is_linked:
 | 
	
		
			
				|  |  | +            if isinstance (input.default_value, str):
 | 
	
		
			
				|  |  | +                group_vars[input.name]=input.default_value
 | 
	
		
			
				|  |  | +            elif hasattr(input.default_value, "name"):
 | 
	
		
			
				|  |  | +                group_vars[input.name]=input.default_value.name
 | 
	
		
			
				|  |  | +        elif hasattr(input, "default_value") and input.is_linked:
 | 
	
		
			
				|  |  | +            group_vars[input.name]=None
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def set_string_variables_at_execution(interface_node, var_name):
 | 
	
		
			
				|  |  | +    # get the ID of the input
 | 
	
		
			
				|  |  | +    prototype = get_node_prototype(interface_node.signature[1:-1], 
 | 
	
		
			
				|  |  | +                                   interface_node.base_tree)
 | 
	
		
			
				|  |  | +    tree = prototype.node_tree
 | 
	
		
			
				|  |  | +    for interface_item in tree.interface.items_tree:
 | 
	
		
			
				|  |  | +        if interface_item.item_type == 'PANEL': continue
 | 
	
		
			
				|  |  | +        if interface_item.name == var_name: break
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        prRed(f"Failed to get value for variable ${var_name}."); return
 | 
	
		
			
				|  |  | +    var_value = interface_node.evaluate_input(interface_item.identifier)
 | 
	
		
			
				|  |  | +    group_key = ''.join(interface_node.signature[1:-1])
 | 
	
		
			
				|  |  | +    group_vars=interface_node.mContext.string_variables.get(group_key, {})
 | 
	
		
			
				|  |  | +    group_vars[var_name]=var_value
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  ##################################################################################################
 | 
	
		
			
				|  |  |  # groups and changing sockets -- this is used extensively by Schema.
 | 
	
	
		
			
				|  | @@ -496,14 +526,14 @@ def import_metarig_data(metarig_data : dict, ):
 | 
	
		
			
				|  |  |                      armature_data['matrix'][12:16], )
 | 
	
		
			
				|  |  |              )
 | 
	
		
			
				|  |  |          prGreen (armature_data['name'])
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          # have to add it to the view layer to switch modes.
 | 
	
		
			
				|  |  |          collection = get_default_collection(collection_type="ARMATURE")
 | 
	
		
			
				|  |  |          collection.objects.link(armature_object)
 | 
	
		
			
				|  |  |          # we'll do this to ensure it is actually in the scene for the mode switch
 | 
	
		
			
				|  |  |          context.scene.collection.objects.link(armature_object)
 | 
	
		
			
				|  |  |          switch_mode('EDIT', objects = [armature_object])
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          while (children):
 | 
	
		
			
				|  |  |              child_name = children.pop()
 | 
	
		
			
				|  |  |              child_data = metarig_data[child_name]
 | 
	
	
		
			
				|  | @@ -525,8 +555,8 @@ def import_metarig_data(metarig_data : dict, ):
 | 
	
		
			
				|  |  |          context.scene.collection.objects.unlink(armature_object)
 | 
	
		
			
				|  |  |      # note that this will not correct if the object exists and is wrong.
 | 
	
		
			
				|  |  |      return armature_object
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def import_curve_data_to_object(curve_name, curve_data):
 | 
	
		
			
				|  |  |      # the curve data will come as a single curve's data
 | 
	
		
			
				|  |  |      from bpy import data
 | 
	
	
		
			
				|  | @@ -544,7 +574,7 @@ def import_curve_data_to_object(curve_name, curve_data):
 | 
	
		
			
				|  |  |              points_collection = spline.bezier_points
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              spline.points.add(len(points_data)-1) # it starts with 1 already
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          for i, point_data in enumerate(points_data):
 | 
	
		
			
				|  |  |              if spline.type == 'BEZIER':
 | 
	
		
			
				|  |  |                  pt = spline.bezier_points[i]
 | 
	
	
		
			
				|  | @@ -689,7 +719,7 @@ def init_schema_dependencies(schema, all_nc):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def check_and_add_root(n, roots, include_non_hierarchy=False):
 | 
	
		
			
				|  |  |      if (include_non_hierarchy * len(n.dependencies)) > 0:
 | 
	
		
			
				|  |  | -        return 
 | 
	
		
			
				|  |  | +        return
 | 
	
		
			
				|  |  |      elif len(n.hierarchy_dependencies) > 0:
 | 
	
		
			
				|  |  |          return
 | 
	
		
			
				|  |  |      roots.append(n)
 | 
	
	
		
			
				|  | @@ -699,7 +729,7 @@ def get_link_in_out(link):
 | 
	
		
			
				|  |  |      from_name, to_name = link.from_socket.node.name, link.to_socket.node.name
 | 
	
		
			
				|  |  |      # catch special bl_idnames and bunch the connections up
 | 
	
		
			
				|  |  |      if link.from_socket.node.bl_idname in replace_types:
 | 
	
		
			
				|  |  | -        from_name = link.from_socket.node.bl_idname 
 | 
	
		
			
				|  |  | +        from_name = link.from_socket.node.bl_idname
 | 
	
		
			
				|  |  |      if link.to_socket.node.bl_idname in replace_types:
 | 
	
		
			
				|  |  |          to_name = link.to_socket.node.bl_idname
 | 
	
		
			
				|  |  |      return from_name, to_name
 | 
	
	
		
			
				|  | @@ -726,7 +756,7 @@ def link_node_containers(tree_path_names, link, local_nc, from_suffix='', to_suf
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  |          prRed(nc_from, nc_to, (*tree_path_names, from_name+from_suffix), (*tree_path_names, to_name+to_suffix))
 | 
	
		
			
				|  |  |          raise RuntimeError(wrapRed(f"Link not connected: {nc_from} -> {nc_to} in tree" ))
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def get_all_dependencies(nc):
 | 
	
		
			
				|  |  |      from .base_definitions import GraphError
 | 
	
		
			
				|  |  |      """ find all dependencies for a mantis node"""
 | 
	
	
		
			
				|  | @@ -744,7 +774,7 @@ def get_all_dependencies(nc):
 | 
	
		
			
				|  |  |              if new_node not in nodes_checked:
 | 
	
		
			
				|  |  |                  check_nodes.append(new_node)
 | 
	
		
			
				|  |  |      return nodes
 | 
	
		
			
				|  |  | -                
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def get_all_nodes_of_type(base_tree, bl_idname):
 | 
	
		
			
				|  |  |      nodes = []
 | 
	
		
			
				|  |  |      check_nodes = list(base_tree.nodes)
 | 
	
	
		
			
				|  | @@ -773,7 +803,7 @@ def trace_all_nodes_from_root(root, nodes):
 | 
	
		
			
				|  |  |              if new_node not in nodes_checked:
 | 
	
		
			
				|  |  |                  check_nodes.append(new_node)
 | 
	
		
			
				|  |  |      return nodes
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  ##################################################################################################
 | 
	
		
			
				|  |  |  # misc
 | 
	
		
			
				|  |  |  ##################################################################################################
 | 
	
	
		
			
				|  | @@ -805,7 +835,7 @@ def all_trees_in_tree(base_tree, selected=False):
 | 
	
		
			
				|  |  |                  if selected == True and node.select == False:
 | 
	
		
			
				|  |  |                      continue
 | 
	
		
			
				|  |  |                  if new_tree := getattr(node, "node_tree", None):
 | 
	
		
			
				|  |  | -                    if new_tree in trees: continue 
 | 
	
		
			
				|  |  | +                    if new_tree in trees: continue
 | 
	
		
			
				|  |  |                      new_trees.append(new_tree)
 | 
	
		
			
				|  |  |                      trees.append(new_tree)
 | 
	
		
			
				|  |  |          check_trees = new_trees
 | 
	
	
		
			
				|  | @@ -817,7 +847,7 @@ def SugiyamaGraph(tree, iterations):
 | 
	
		
			
				|  |  |          class defaultview(object):
 | 
	
		
			
				|  |  |              w,h = 1,1
 | 
	
		
			
				|  |  |              xz = (0,0)
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          graph = Graph()
 | 
	
		
			
				|  |  |          no_links = set()
 | 
	
		
			
				|  |  |          verts = {}
 | 
	
	
		
			
				|  | @@ -832,7 +862,7 @@ def SugiyamaGraph(tree, iterations):
 | 
	
		
			
				|  |  |                  no_links.add(n.name)
 | 
	
		
			
				|  |  |                  graph.add_vertex(v)
 | 
	
		
			
				|  |  |              n.select=False
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          edges = []
 | 
	
		
			
				|  |  |          inverted_edges=[]
 | 
	
		
			
				|  |  |          not_a_root = set()
 | 
	
	
		
			
				|  | @@ -853,7 +883,7 @@ def SugiyamaGraph(tree, iterations):
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  |              from grandalf.layouts import SugiyamaLayout
 | 
	
		
			
				|  |  |              # .C[0] is the first "graph core" that contains a connected graph.
 | 
	
		
			
				|  |  | -            sug = SugiyamaLayout(graph.C[0]) 
 | 
	
		
			
				|  |  | +            sug = SugiyamaLayout(graph.C[0])
 | 
	
		
			
				|  |  |              sug.init_all()
 | 
	
		
			
				|  |  |              sug.draw(iterations)
 | 
	
		
			
				|  |  |              # Digco is good for small graphs.
 | 
	
	
		
			
				|  | @@ -870,7 +900,7 @@ def SugiyamaGraph(tree, iterations):
 | 
	
		
			
				|  |  |                      n.location.x = v.view.xy[1]
 | 
	
		
			
				|  |  |                      n.location.y = v.view.xy[0]
 | 
	
		
			
				|  |  |                      n.select = True
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          # now we can take all the input nodes and try to put them in a sensible place
 | 
	
		
			
				|  |  |          # not sure why but this absolutely does not do anything
 | 
	
		
			
				|  |  |          for n_name in no_links:
 | 
	
	
		
			
				|  | @@ -897,7 +927,7 @@ def SugiyamaGraph(tree, iterations):
 | 
	
		
			
				|  |  |                  else: # we'll just position it next to the next node
 | 
	
		
			
				|  |  |                      n.location = next_node.location
 | 
	
		
			
				|  |  |                      n.location.x -= next_node.width*1.5
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def project_point_to_plane(point, origin, normal):
 | 
	
		
			
				|  |  |      return point - normal.dot(point- origin)*normal
 | 
	
	
		
			
				|  | @@ -926,8 +956,8 @@ def gen_nc_input_for_data(socket):
 | 
	
		
			
				|  |  |                          "HideSocket"                           : classes["InputBoolean"],
 | 
	
		
			
				|  |  |                          #
 | 
	
		
			
				|  |  |                          "DriverSocket"                         : None,
 | 
	
		
			
				|  |  | -                        "DriverVariableSocket"                 : None, 
 | 
	
		
			
				|  |  | -                        "FCurveSocket"                         : None, 
 | 
	
		
			
				|  |  | +                        "DriverVariableSocket"                 : None,
 | 
	
		
			
				|  |  | +                        "FCurveSocket"                         : None,
 | 
	
		
			
				|  |  |                          "KeyframeSocket"                       : None,
 | 
	
		
			
				|  |  |                          "BoneCollectionSocket"                 : classes["InputString"],
 | 
	
		
			
				|  |  |                          #
 | 
	
	
		
			
				|  | @@ -974,7 +1004,7 @@ def gen_nc_input_for_data(socket):
 | 
	
		
			
				|  |  |                          "VectorEulerSocket"                    : classes["InputVector"],
 | 
	
		
			
				|  |  |                          "VectorTranslationSocket"              : classes["InputVector"],
 | 
	
		
			
				|  |  |                          "VectorScaleSocket"                    : classes["InputVector"],
 | 
	
		
			
				|  |  | -                        # Drivers             
 | 
	
		
			
				|  |  | +                        # Drivers
 | 
	
		
			
				|  |  |                          "EnumDriverVariableType"               : classes["InputString"],
 | 
	
		
			
				|  |  |                          "EnumDriverVariableEvaluationSpace"    : classes["InputString"],
 | 
	
		
			
				|  |  |                          "EnumDriverRotationMode"               : classes["InputString"],
 | 
	
	
		
			
				|  | @@ -1121,9 +1151,9 @@ def RibbonMeshEdgeLengths(m, ribbon):
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              v2NextInd = bE[cap((i+1) , len(bE) - 1 )]
 | 
	
		
			
				|  |  |          v2 = m.vertices[bE[i]]; v2Next = m.vertices[v2NextInd]
 | 
	
		
			
				|  |  | -        
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          v = v1.co.lerp(v2.co, 0.5); vNext = v1Next.co.lerp(v2Next.co, 0.5)
 | 
	
		
			
				|  |  | -        # get the center, edges may not be straight so total length 
 | 
	
		
			
				|  |  | +        # get the center, edges may not be straight so total length
 | 
	
		
			
				|  |  |          #  of one edge may be more than the ribbon center's length
 | 
	
		
			
				|  |  |          lengths.append(( v - vNext ).length)
 | 
	
		
			
				|  |  |      return lengths
 | 
	
	
		
			
				|  | @@ -1248,7 +1278,7 @@ def FindNearestPointOnWireMesh(m, pointsList):
 | 
	
		
			
				|  |  |                      v1 = vertData[i]
 | 
	
		
			
				|  |  |                      v2 = vertData[i+1]
 | 
	
		
			
				|  |  |                      prevDist = curDist
 | 
	
		
			
				|  |  | -                    offset = intersect_point_line(p, m.vertices[v1[0]].co, 
 | 
	
		
			
				|  |  | +                    offset = intersect_point_line(p, m.vertices[v1[0]].co,
 | 
	
		
			
				|  |  |                                                       m.vertices[v2[0]].co)[1]
 | 
	
		
			
				|  |  |              if (offset < 0):
 | 
	
		
			
				|  |  |                  offset = 0
 | 
	
	
		
			
				|  | @@ -1321,7 +1351,7 @@ def DetectRibbon(f, bm, skipMe):
 | 
	
		
			
				|  |  |          bEdge.append (f.loops[3].vert.index) # bottom-left
 | 
	
		
			
				|  |  |          nEdge = bm.edges.get([f.loops[1].vert, f.loops[2].vert])
 | 
	
		
			
				|  |  |          nFaces = nEdge.link_faces
 | 
	
		
			
				|  |  | -        if (len(nFaces) == 1): 
 | 
	
		
			
				|  |  | +        if (len(nFaces) == 1):
 | 
	
		
			
				|  |  |              cont = False
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              for nFace in nFaces:
 | 
	
	
		
			
				|  | @@ -1334,7 +1364,7 @@ def DetectRibbon(f, bm, skipMe):
 | 
	
		
			
				|  |  |          if (cont == False): # we've reached the end, get the last two:
 | 
	
		
			
				|  |  |              tEdge.append (f.loops[1].vert.index) # top-right
 | 
	
		
			
				|  |  |              bEdge.append (f.loops[2].vert.index) # bottom-right
 | 
	
		
			
				|  |  | -            # this will create a loop for rings -- 
 | 
	
		
			
				|  |  | +            # this will create a loop for rings --
 | 
	
		
			
				|  |  |              #  "the first shall be the last and the last shall be first"
 | 
	
		
			
				|  |  |      return (tEdge,bEdge,circle)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1397,7 +1427,7 @@ def data_from_ribbon_mesh(m, factorsList, mat, ribbons = None, fReport = None):
 | 
	
		
			
				|  |  |          if (ribbons is None):
 | 
	
		
			
				|  |  |              if (fReport):
 | 
	
		
			
				|  |  |                  fReport(type = {'ERROR'}, message="No ribbon to get data from.")
 | 
	
		
			
				|  |  | -            else:  
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  |                  print ("No ribbon to get data from.")
 | 
	
		
			
				|  |  |              return None
 | 
	
		
			
				|  |  |      ret = []
 | 
	
	
		
			
				|  | @@ -1476,7 +1506,7 @@ def data_from_ribbon_mesh(m, factorsList, mat, ribbons = None, fReport = None):
 | 
	
		
			
				|  |  |  # If the sign of the error is meaningful, a simpler function
 | 
	
		
			
				|  |  |  # can be used.
 | 
	
		
			
				|  |  |  def do_bisect_search_by_magnitude(
 | 
	
		
			
				|  |  | -        owner, 
 | 
	
		
			
				|  |  | +        owner,
 | 
	
		
			
				|  |  |          attribute,
 | 
	
		
			
				|  |  |          index = None,
 | 
	
		
			
				|  |  |          test_function = None,
 | 
	
	
		
			
				|  | @@ -1529,4 +1559,4 @@ def do_bisect_search_by_magnitude(
 | 
	
		
			
				|  |  |              update_dg.update()
 | 
	
		
			
				|  |  |      else: # Loop has completed without finding a solution
 | 
	
		
			
				|  |  |          i = best_so_far
 | 
	
		
			
				|  |  | -        modify(owner, attribute, best_so_far, context = context); i+=1
 | 
	
		
			
				|  |  | +        modify(owner, attribute, best_so_far, context = context); i+=1
 |