Sfoglia il codice sorgente

WIP string variables

string variables work in the simplest case already
all the trouble comes from the stuff that is in schemas or made at
 runtime
so I have a lot of complex stuff to figure out
and even more important: to figure out how the user interacts
for example: should frame variables be inherited or not?
do I need a way to get this stuff recursively?
then how do i evaluate a string from a node in a different frame?
I would need to use THAT node to look up the variables, right?
NO SPOOKY stuff can be allowed

anyhow this is a WIP, might forget about this for a while and come back
Brandenburg 4 mesi fa
parent
commit
82613019b2
6 ha cambiato i file con 253 aggiunte e 172 eliminazioni
  1. 96 51
      base_definitions.py
  2. 10 2
      internal_containers.py
  3. 46 28
      readtree.py
  4. 30 28
      schema_solve.py
  5. 56 28
      utilities.py
  6. 15 35
      visualize.py

+ 96 - 51
base_definitions.py

@@ -84,7 +84,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
@@ -104,14 +104,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)
@@ -155,7 +155,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.
@@ -219,7 +219,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.
@@ -236,7 +236,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
@@ -260,7 +260,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
@@ -286,7 +286,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)
@@ -299,7 +299,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
@@ -344,12 +344,12 @@ def node_group_update(node, force = False):
            (node.id_data.is_exporting == True):
             return
     # note: if (node.id_data.is_exporting == True) I need to be able to update so I can make links.
-    
+
     if node.node_tree is None:
         node.inputs.clear(); node.outputs.clear()
         node.id_data.do_live_update = toggle_update
         return
-    
+
     toggle_update = node.id_data.do_live_update
     node.id_data.do_live_update = False
 
@@ -391,11 +391,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,)
@@ -408,7 +408,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:
@@ -444,7 +444,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:
@@ -568,10 +568,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
@@ -606,7 +606,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)
 
@@ -618,7 +618,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.
@@ -702,6 +702,7 @@ class MantisExecutionContext():
         self.execution_id = base_tree.execution_id
         self.execution_failed=False
         self.b_objects={} # objects created by Mantis during execution
+        self.string_variables={}
 
 class MantisNode:
     """
@@ -737,12 +738,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
@@ -760,7 +761,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:
@@ -772,7 +773,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])
@@ -783,12 +784,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)
@@ -838,7 +839,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):
@@ -846,7 +847,42 @@ class MantisNode:
         if self.prepared==False: return # all good from here
         for conn in self.hierarchy_connections:
             conn.reset_execution_recursive()
-    
+
+
+    # TODO: make this MUCH more efficient!
+    # alternatively: call this ONCE when initializing the tree, precache results?
+    def apply_string_variables(self, string):
+        # We get the mContext, iterate through the signature, and string-replace variables
+        # this function should be called by evaluate_input if the result is a string.
+        all_vars = {} # maybe the individual nodes should store this as a class member, too...
+        name=""
+        do_once = False
+        for i in range(len(self.signature[:-1])):
+            if i == 0:
+                continue
+            name+=self.signature[i]
+            prWhite(name)
+            vars = self.mContext.string_variables.get(name, None)
+            if vars is None:
+                prRed("Can't get string variables for node")
+                print (vars, type(vars))
+                prRed (name)
+                prWhite(self.mContext.string_variables.keys())
+                for k in self.mContext.string_variables.keys():
+                    print (name == k)
+                    print (len(name), name,)
+                    print (len(k), k)
+                raise RuntimeError
+                continue
+            elif not vars:
+                prRed(self)
+            for var_name, var_value in vars.items():
+                do_once=True
+                string = string.replace("$"+var_name, var_value)
+        if do_once == False:
+            raise NotImplementedError
+        return string
+
     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
@@ -854,14 +890,24 @@ class MantisNode:
         # this trace() should give a key error if there is a problem
         #  it is NOT handled here because it should NOT happen - so I want the error message.
         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
+        try:
+            prop = trace[0][-1].parameters[trace[1].name] #trace[0] = the list of traced nodes; read its parameters
+        except Exception as e:
+            print (trace[0][-1])
+            print (trace[1].name)
+            print (trace[0][-1].parameters.keys())
+            print (trace[0][-1].parameters.values())
+            raise e
+        if isinstance(prop, str) and "$" in prop:
+            print (self, prop)
+            prop = self.apply_string_variables(prop)
         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??
@@ -916,7 +962,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
@@ -928,7 +974,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()
@@ -936,7 +982,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" %
@@ -963,12 +1009,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?
@@ -1004,7 +1050,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.")
@@ -1018,7 +1064,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.
@@ -1026,12 +1072,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
@@ -1049,7 +1095,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):
@@ -1062,7 +1108,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
@@ -1082,22 +1128,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
 
@@ -1105,7 +1151,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):
@@ -1115,15 +1161,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())
-

+ 10 - 2
internal_containers.py

@@ -11,6 +11,7 @@ class DummyNode(MantisNode):
         self.prepared = True
         self.uuid = uuid4()
         self.solver = None
+        self.did_set_variables = False
         if prototype:
             if prototype.bl_idname in ["MantisSchemaGroup"]:
                 self.node_type = 'DUMMY_SCHEMA'
@@ -32,6 +33,13 @@ class DummyNode(MantisNode):
         # this is ugly and I hate it.
         self.execution_prepared=True # in case it gets left behind in the tree as a dependency
 
+    def bPrepare(self, bContext=None):
+        from .utilities import set_string_variables_during_exec
+        set_string_variables_during_exec(self, self.mContext)
+        self.did_set_variables = True # I just need to know if this is getting them all
+
+    def __del__(self):
+        print (self, self.did_set_variables)
 
 class NoOpNode(MantisNode):
     def __init__(self, signature, base_tree):
@@ -50,7 +58,7 @@ class AutoGenNode(MantisNode):
         super().__init__(signature, base_tree)
         self.node_type = 'UTILITY'
         self.prepared, self.executed = True, True
-    
+
     def reset_execution(self):
         super().reset_execution()
-        self.prepared, self.executed = True, True
+        self.prepared, self.executed = True, True

+ 46 - 28
readtree.py

@@ -1,7 +1,7 @@
 from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
                         wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
 
-    
+
 
 
 def grp_node_reroute_common(nc, nc_to, all_nc):
@@ -22,7 +22,7 @@ def grp_node_reroute_common(nc, nc_to, all_nc):
                     from_link = from_links.pop()
                     if from_link == in_link:
                         from_link.die()
-                        continue # DELETE the dummy node link 
+                        continue # DELETE the dummy node link
                     links.append(from_link)
                 from_nc.outputs[from_socket].links = links
                 down = nc_to.outputs[inp_name]
@@ -63,7 +63,7 @@ def insert_lazy_parents(nc):
                     break # this is it
             from_link.to_node = inherit_nc; from_link.to_socket="Parent"
             from_link.to_node.inputs[from_link.to_socket].is_linked=True
-            
+
             links=[]
             while (nc.inputs["Relationship"].links):
                 to_link = nc.inputs["Relationship"].links.pop()
@@ -71,11 +71,11 @@ def insert_lazy_parents(nc):
                     continue # don't keep this one
                 links.append(to_link)
                 to_link.from_node.outputs[from_link.from_socket].is_linked=True
-            
+
             nc.inputs["Relationship"].links=links
             link=NodeLink(from_node=inherit_nc, from_socket="Inheritance", to_node=nc, to_socket="Relationship")
             inherit_nc.inputs["Parent"].links.append(from_link)
-            
+
             inherit_nc.parameters = {
                                      "Parent":None,
                                      "Inherit Rotation":True,
@@ -122,7 +122,7 @@ def make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, n
             signature = ("MANTIS_AUTOGENERATED", *tree_path_names, nc_to.ui_signature[-1], inp.name, inp.identifier, str(uuid4()))
             nc_from = all_nc.get(signature) # creating this without checking and
             #  using UUID signature leads to TERRIBLE CONFUSING BUGS.
-            if nc_from is None: 
+            if nc_from is None:
                 nc_from = autogen_node(base_tree, inp, signature, nc_to.mContext)
             from .node_container_common import get_socket_value
             if nc_from: # autogen can fail and we should catch it.
@@ -132,7 +132,7 @@ def make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, n
             else:
                 prRed("No available auto-generated class for input %s in %s" % (inp.name, np.name))
 
-def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_nc, dummy_nodes, group_nodes, schema_nodes ):
+def gen_mantis_nodes(base_tree, current_tree, tree_path_names, mContext, all_nc, local_nc, dummy_nodes, group_nodes, schema_nodes ):
     from .internal_containers import DummyNode
     for ui_node in current_tree.nodes:
         # HACK I found that this isn't being set sometimes. I wonder why? It makes the most sense to do this here.
@@ -146,13 +146,18 @@ def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_
             ui_sig = (None, *tree_path_names, ui_node.name)
             if not local_nc.get(sig):
                 nc = DummyNode( signature=sig , base_tree=base_tree, prototype=ui_node, ui_signature=ui_sig )
+                nc.mContext = mContext
                 local_nc[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
                 if ui_node.bl_idname in ["NodeGroupOutput"]:
                     nc.reroute_links = reroute_links_grpout
         elif ui_node.bl_idname in  ["MantisNodeGroup", "MantisSchemaGroup"]:
             nc = DummyNode( signature= (sig := (None, *tree_path_names, ui_node.name) ), base_tree=base_tree, prototype=ui_node )
+            nc.mContext = mContext
             local_nc[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
             make_connections_to_ng_dummy(base_tree, tree_path_names, local_nc, all_nc, nc)
+            # This will catch any variable set in a non-schema node-group.
+            from .utilities import set_string_variables_at_creation_time
+            set_string_variables_at_creation_time(nc, ui_node, mContext)
             if ui_node.bl_idname == "MantisNodeGroup":
                 group_nodes.append(nc)
                 nc.reroute_links = reroute_links_grp
@@ -167,6 +172,7 @@ def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_
                 if local_nc.get(sig):
                     continue # already made
             nc = nc_cls( sig , base_tree)
+            nc.mContext = mContext
             local_nc[sig] = nc; all_nc[sig] = nc
             nc.ui_signature = (*nc.ui_signature[:-1], ui_node.name) # just to ensure it points to a real node.
         else:
@@ -176,7 +182,7 @@ def gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_
         if nc.signature[0] not in ['MANTIS_AUTOGENERATED'] and nc.node_type not in ['SCHEMA', 'DUMMY', 'DUMMY_SCHEMA']:
             nc.fill_parameters()
 
-def data_from_tree(base_tree, tree_path, dummy_nodes, all_nc, all_schema):#
+def data_from_tree(base_tree, tree_path, mContext, dummy_nodes, all_nc, all_schema):#
     # TODO: it should be relatively easy to make this use a while loop instead of recursion.
     local_nc, group_nodes = {}, []
     tree_path_names = [tree.name for tree in tree_path if hasattr(tree, "name")]
@@ -188,14 +194,14 @@ def data_from_tree(base_tree, tree_path, dummy_nodes, all_nc, all_schema):#
     if current_tree: # the node-group may not have a tree set - if so, ignore it.
         from .utilities import clear_reroutes
         links = clear_reroutes(list(current_tree.links))
-        gen_node_containers(base_tree, current_tree, tree_path_names, all_nc, local_nc, dummy_nodes, group_nodes, all_schema)
-        
+        gen_mantis_nodes(base_tree, current_tree, tree_path_names, mContext, all_nc, local_nc, dummy_nodes, group_nodes, all_schema)
+
         from .utilities import link_node_containers
         for link in links:
             link_node_containers((None, *tree_path_names), link, local_nc)
         # Now, descend into the Node Groups and recurse
         for nc in group_nodes:
-            data_from_tree(base_tree, tree_path+[nc.prototype], dummy_nodes, all_nc, all_schema)
+            data_from_tree(base_tree, tree_path+[nc.prototype], mContext, dummy_nodes, all_nc, all_schema)
     return dummy_nodes, all_nc, all_schema
 
 from .utilities import check_and_add_root, init_connections, init_dependencies, init_schema_dependencies
@@ -246,7 +252,7 @@ schema_bl_idnames = [   "SchemaIndex",
                         "SchemaConstInput",
                         "SchemaConstOutput",
                         "SchemaOutgoingConnection",
-                        "SchemaIncomingConnection", 
+                        "SchemaIncomingConnection",
                     ]
 
 from .utilities import get_all_dependencies
@@ -261,7 +267,7 @@ def get_schema_length_dependencies(node, all_nodes={}):
             for l in inp.links:
                 if not l.from_node in node.hierarchy_dependencies:
                     continue
-                if "MANTIS_AUTOGENERATED" in l.from_node.signature: 
+                if "MANTIS_AUTOGENERATED" in l.from_node.signature:
                     deps.extend([l.from_node]) # why we need this lol
                 if inp.name in prepare_links_to:
                     deps.append(l.from_node)
@@ -296,7 +302,7 @@ def get_schema_length_dependencies(node, all_nodes={}):
 def parse_tree(base_tree, error_popups=False):
     from uuid import uuid4
     base_tree.execution_id = uuid4().__str__() # set the unique id of this execution
-    
+
     from .base_definitions import MantisExecutionContext
     mContext = MantisExecutionContext(base_tree=base_tree)
 
@@ -305,13 +311,13 @@ def parse_tree(base_tree, error_popups=False):
     # annoyingly I have to pass in values for all of the dicts because if I initialize them in the function call
     #  then they stick around because the function definition inits them once and keeps a reference
     # so instead I have to supply them to avoid ugly code or bugs elsewhere
-    # it's REALLY confusing when you run into this sort of problem. So it warrants four entire lines of comments!      
-    dummy_nodes, all_mantis_nodes, all_schema =  data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_nc = {}, all_schema={})
+    # it's REALLY confusing when you run into this sort of problem. So it warrants four entire lines of comments!
+    dummy_nodes, all_mantis_nodes, all_schema =  data_from_tree(base_tree, tree_path = [None], mContext=mContext, dummy_nodes = {}, all_nc = {}, all_schema={})
     for dummy in dummy_nodes.values():    # reroute the links in the group nodes
         if (hasattr(dummy, "reroute_links")):
             dummy.reroute_links(dummy, all_mantis_nodes)
     prGreen(f"Pulling data from tree took {time.time() - data_start_time} seconds")
-    
+
     start_time = time.time()
     solve_only_these = []; solve_only_these.extend(list(all_schema.values()))
     roots, array_nodes = [], []
@@ -365,7 +371,7 @@ def parse_tree(base_tree, error_popups=False):
             for dep in n.hierarchy_dependencies:
                 if dep not in schema_solve_done and (dep in solve_only_these):
                     if dep.prepared:
-                        continue 
+                        continue
                     solve_layer.appendleft(n)
                     break
             else:
@@ -398,7 +404,7 @@ def parse_tree(base_tree, error_popups=False):
                     e = execution_error_cleanup(n, e, show_error=error_popups)
                     if error_popups == False:
                         raise e
-                    
+
                 schema_solve_done.add(n)
                 for conn in n.hierarchy_connections:
                     if conn not in schema_solve_done and conn not in solve_layer:
@@ -410,12 +416,25 @@ def parse_tree(base_tree, error_popups=False):
 
     all_mantis_nodes = list(all_mantis_nodes.values())
     kept_nc = {}
+
     while (all_mantis_nodes):
         nc = all_mantis_nodes.pop()
         if nc in array_nodes:
             continue
-        if nc.node_type in ["DUMMY", 'SCHEMA', 'DUMMY_SCHEMA']:
-            continue # screen out the prototype schema nodes, group in/out, and group placeholders
+        if nc.node_type in ['SCHEMA', 'DUMMY_SCHEMA']:
+            # this is BAD because it "deletes" this implicitly instead of explicitly
+            continue # screen out the prototype schema nodes and schema nodes
+        if nc.node_type in ['DUMMY']:
+            if nc.signature[-1] in ["NodeGroupInput", "NodeGroupOutput"]:
+                continue
+            else:
+                prGreen(nc)
+                for input in nc.inputs:
+                    for l in input.links:
+                        print (l)
+                        raise RuntimeError
+                # hold it. its purpose is to gather its string variables at runtime
+                pass
         # cleanup autogen nodes
         if nc.signature[0] == "MANTIS_AUTOGENERATED" and len(nc.inputs) == 0 and len(nc.outputs) == 1:
             from .base_definitions import can_remove_socket_for_autogen
@@ -499,7 +518,7 @@ def sort_execution(nodes, xForm_pass):
             execution_failed = True
             raise GraphError("There is probably a cycle somewhere in the graph. "
                                 "Or a connection missing in a Group/Schema Input")
-        i+=1    
+        i+=1
         n = xForm_pass.pop()
         if visited.get(n.signature) is not None:
             visited[n.signature]+=1
@@ -599,7 +618,7 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
         switch_mode(mode='OBJECT', objects=switch_me)
         # switch to pose mode here so that the nodes can use the final pose data
         # this will require them to update the depsgraph.
-        
+
 
         for ob in switch_me:
             ob.data.pose_position = 'POSE'
@@ -612,8 +631,8 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
                 if error_popups == False:
                     raise e
                 execution_failed = True; break
-        
-        
+
+
         # REST pose for deformer bind, so everything is in the rest position
         for ob in switch_me:
             ob.data.pose_position = 'REST'
@@ -627,10 +646,10 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
                 if error_popups == False:
                     raise e
                 execution_failed = True; break
-                
+
         for ob in switch_me:
             ob.data.pose_position = 'POSE'
-        
+
         tot_time = (time() - start_execution_time)
         if not execution_failed:
             prGreen(f"Executed tree of {len(sorted_nodes)} nodes in {tot_time} seconds")
@@ -656,4 +675,3 @@ def execute_tree(nodes, base_tree, context, error_popups = False):
                 ob.select_set(True)
             except RuntimeError: # it isn't in the view layer
                 pass
-

+ 30 - 28
schema_solve.py

@@ -51,7 +51,7 @@ class SchemaSolver:
         self.solve_length = self.node.evaluate_input("Schema Length")
         # I'm making this a property of the solver because the solver's data is modified as it solves each iteration
         self.index = 0
-        
+
         prWhite(f"\nExpanding schema {self.tree.name} in node {self.node.signature}"
                  f" with length {self.solve_length}.")
 
@@ -66,14 +66,14 @@ class SchemaSolver:
         from bpy.types import NodeGroupInput, NodeGroupOutput
         for ui_node in self.tree.nodes:
             # first we need to fill the parameters of the schema nodes.
-            # we use the bl_idname because all schema nodes should be single-instance 
+            # we use the bl_idname because all schema nodes should be single-instance
             signature = (*self.tree_path_names, ui_node.bl_idname)
             if isinstance(ui_node, (SchemaUINode, NodeGroupInput, NodeGroupOutput)):
                 # We use the schema node's "natural signature" here because it represents
                 # the "original" signature of the schema UI group node since this schema
                 # solver may be in a nested schema, and its node's signature may have
                 # uuid/index attached.
-                get_sig = (*self.node.ui_signature, ui_node.bl_idname) 
+                get_sig = (*self.node.ui_signature, ui_node.bl_idname)
                 if not (mantis_node := self.all_nodes.get(get_sig)):
                     raise RuntimeError(wrapRed(f"Not found: {get_sig}"))
                 self.schema_nodes[signature] = mantis_node
@@ -89,14 +89,14 @@ class SchemaSolver:
                 mantis_node = SchemaConstOutput(signature=signature, base_tree=self.node.base_tree, parent_schema_node=self.node)
                 self.schema_nodes[signature] = mantis_node
                 mantis_node.fill_parameters(ui_node)
-    
+
     def set_index_strings(self):
         self.index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index).zfill(4)
         self.prev_index_str = lambda : '.'+str(self.uuid)+'.'+str(self.index-1).zfill(4)
         if self.is_node_group:
             self.index_str=lambda : ''
             self.prev_index_str=lambda : ''
-    
+
     def init_schema_links(self,):
         """ Sort and store the links to/from the Schema group node."""
         for item in self.tree.interface.items_tree:
@@ -110,7 +110,7 @@ class SchemaSolver:
                         if incoming_links := self.node.inputs[item.identifier].links:
                             self.incoming_connections[item.name] = incoming_links[0]
                         else:
-                            self.incoming_connections[item.name] = None 
+                            self.incoming_connections[item.name] = None
                     else: # OUTPUT
                         if outgoing_links := self.node.outputs[item.identifier].links:
                             self.outgoing_connections[item.name] = outgoing_links.copy()
@@ -143,9 +143,9 @@ class SchemaSolver:
                             self.array_output_connections[item.identifier]=[]
                         if out_links := self.node.outputs[item.identifier].links:
                             self.array_output_connections[item.identifier] = out_links.copy()
-    
 
-    
+
+
 
     def gen_solve_iteration_mantis_nodes(self, frame_mantis_nodes, unprepared):
         for prototype_ui_node in self.tree.nodes:
@@ -173,6 +173,9 @@ class SchemaSolver:
                     mantis_node = prototype_mantis_node.__class__(
                         signature, prototype_mantis_node.base_tree, prototype=ui_node,
                         ui_signature =  prototype_mantis_node.signature)
+                    # establish string variables
+                    from .utilities import set_string_variables_at_creation_time
+                    set_string_variables_at_creation_time(mantis_node, prototype_ui_node, mContext)
                     # now let's copy the links from the prototype node
                     if ui_node.bl_idname in ["MantisNodeGroup"]:
                         mantis_node.prepared = False
@@ -213,17 +216,17 @@ class SchemaSolver:
             signature = ("MANTIS_AUTOGENERATED", *self.tree_path_names[1:-1], unique_name)
             from_node = self.all_nodes.get(signature)
             if not from_node:
-                from_node = autogen_node(self.node.base_tree, ui_link.from_socket, 
+                from_node = autogen_node(self.node.base_tree, ui_link.from_socket,
                                 signature=signature, mContext=self.node.mContext)
                 from_node.parameters = {ui_link.from_socket.name:index}
                 frame_mantis_nodes[signature]=from_node; self.solved_nodes[signature]=from_node
                 self.all_nodes[signature]=from_node
             _connection = from_node.outputs[ui_link.from_socket.name].connect(node=to_node, socket=ui_link.to_socket.identifier)
-            return 
+            return
         # Since the index is already determined, it is safe to remove the socket and just keep the value.
         to_node.parameters[ui_link.to_socket.name] = index
         del to_node.inputs[ui_link.to_socket.name]
-    
+
     def handle_link_from_schema_length_input(self, frame_mantis_nodes, ui_link):
         # see, here I can just use the schema node
         _from_name, to_name = get_link_in_out(ui_link)
@@ -273,7 +276,7 @@ class SchemaSolver:
                         existing_link.die()
             # BUG may exist here.
             self.incoming_connections[ui_link.to_socket.name] = connection
-        
+
 
     def handle_link_from_constant_input(self, frame_mantis_nodes, ui_link, to_ui_node):
         incoming = self.constant_in[ui_link.from_socket.name]
@@ -288,7 +291,7 @@ class SchemaSolver:
             to_socket=ui_link.to_socket.identifier
         connection = from_node.outputs[incoming.from_socket].connect(node=to_node, socket=to_socket)
         init_connections(from_node)
-    
+
     def handle_link_from_array_input_get(self, frame_mantis_nodes, ui_link ):
         from_ui_node = ui_link.from_socket.node
         from_node = self.schema_nodes[(*self.node.ui_signature, from_ui_node.bl_idname)]
@@ -478,7 +481,7 @@ class SchemaSolver:
         # connected up correctly. between the output and the schema's generated nodes.
         # so seek BACK from the output node and grab the from-node that is connected to
         #  the link. then modify the ui_link to point to that node.
-        from .base_definitions import DummyLink 
+        from .base_definitions import DummyLink
         if not isinstance(ui_link, DummyLink): # make it a Dummy so i can modify it
             ui_link = DummyLink(ui_link.from_socket, ui_link.to_socket,
                                 multi_input_sort_id=ui_link.multi_input_sort_id)
@@ -591,7 +594,7 @@ class SchemaSolver:
             if isinstance(from_ui_node, (SchemaConstInput, NodeGroupInput)):
                 if ui_link.from_socket.name in self.constant_in.keys():
                     self.handle_link_from_constant_input( frame_mantis_nodes, ui_link, to_ui_node)
-                continue 
+                continue
             if isinstance(to_ui_node, SchemaArrayInputGet):
                 self.handle_link_to_array_input_get( frame_mantis_nodes, ui_link)
                 continue
@@ -613,7 +616,7 @@ class SchemaSolver:
                     raise e # always raise this error because it is not implemented.
                     self.handle_link_from_subschema_to_output(frame_mantis_nodes, ui_link, to_ui_node)
                 self.held_links.append(ui_link)
-                continue 
+                continue
             # HOLD these links until prep is done a little later
             if isinstance(to_ui_node, (SchemaConstOutput, NodeGroupOutput)) or isinstance(to_ui_node, SchemaArrayOutput):
                 if isinstance(from_ui_node, (MantisNodeGroup, SchemaGroup)):
@@ -630,12 +633,12 @@ class SchemaSolver:
             if isinstance(from_ui_node, SchemaArrayInputGet):
                 array_input_get_link.append(ui_link)
                 continue
-            
+
             # for any of the special cases, we hit a 'continue' block. So this connection is not special, and is made here.
             connection = link_node_containers(self.autogen_path_names, ui_link,
                                 frame_mantis_nodes, from_suffix=self.index_str(),
                                 to_suffix=self.index_str())
-        
+
         for signature, node in frame_mantis_nodes.items():
             self.solved_nodes[signature]=node
             if node.node_type == "DUMMY_SCHEMA":
@@ -651,15 +654,15 @@ class SchemaSolver:
                 init_schema_dependencies(node, self.all_nodes)
             else:
                 init_dependencies(node) # it is hard to overstate how important this single line of code is
-    
+
         # We have to prepare the nodes leading to Schema Length
         unprepared=deque()
-        for node in frame_mantis_nodes.values(): 
+        for node in frame_mantis_nodes.values():
             if node.node_type == 'DUMMY_SCHEMA' and (schema_len_in := node.inputs.get("Schema Length")):
                 for l in schema_len_in.links:
                     unprepared.append(l.from_node)
             self.prepare_nodes(unprepared)
-        
+
         # We have to prepare the nodes leading to Array Input Get
         for ui_link in array_input_get_link:
             from_name = get_link_in_out(ui_link)[0]
@@ -706,7 +709,7 @@ class SchemaSolver:
                 else:
                     self.handle_link_to_array_output(frame_mantis_nodes, self.index, ui_link, to_ui_node, from_ui_node)
         return frame_mantis_nodes
-    
+
     def solve_nested_schema(self, schema_nc):
         """ Solves all schema node groups found in this Schema. This is a recursive function, which will
             solve all levels of nested schema - since this function is called by solver.solve().
@@ -748,7 +751,7 @@ class SchemaSolver:
                             # we need to kill the link between the Schema itself and the next node and update the deps. Otherwise:confusing bugs.
                             outgoing.die(); init_dependencies(to_node)
                     # else: # the node just isn't connected out this socket.
-        
+
 
         # # solve all unsolved nested schemas...
         for schema_sig, schema_nc in self.nested_schemas.items():
@@ -770,18 +773,18 @@ class SchemaSolver:
         for conn in self.array_output_connections.values():
             for outgoing in conn:
                 all_outgoing_links.append(outgoing)
-        
+
         for outgoing in all_outgoing_links:
             to_node = outgoing.to_node
             for l in to_node.inputs[outgoing.to_socket].links:
                 if self.node == l.from_node:
                     l.die()
-        
+
         for inp in self.node.inputs.values():
             for l in inp.links:
                 init_connections(l.from_node) # to force it to have hierarchy connections with the new nodes.
-                    
-        
+
+
     def solve(self):
         if self.solve_length < 1:
             from .base_definitions import GraphError
@@ -802,4 +805,3 @@ class SchemaSolver:
         self.node.prepared = True
         prGreen(f"Schema declared {len(self.solved_nodes)} nodes.\n")
         return self.solved_nodes
-        

+ 56 - 28
utilities.py

@@ -104,10 +104,38 @@ 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
+    prev_group_key = ''
+    prev_group_vars = {}
+    for i in range(len(n.signature[:-1])):
+        if i == 0: continue # this will cut any AUTOGEN or None in the base
+        prev_group_key+=n.signature[i]
+        prev_group_vars=mContext.string_variables.get(prev_group_key, {})
+    # IS THIS WISE???
+    from copy import deepcopy # so nothing spooky will happen
+    group_vars = mContext.string_variables["".join(n.signature[1:])] = deepcopy(prev_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
+
+
+def set_string_variables_during_exec(n, mContext):
+    print(n)
+    pass
+    # so for this we need to get the UI node to get the string
+    # when a node is executed, we check for string variables that were set at runtime
+    # we need the dummy node for this
+
+
 
 ##################################################################################################
 # groups and changing sockets -- this is used extensively by Schema.
@@ -460,14 +488,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]
@@ -489,8 +517,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
@@ -508,7 +536,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]
@@ -652,7 +680,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)
@@ -662,7 +690,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
@@ -689,7 +717,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"""
@@ -707,7 +735,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)
@@ -736,7 +764,7 @@ def trace_all_nodes_from_root(root, nodes):
             if new_node not in nodes_checked:
                 check_nodes.append(new_node)
     return nodes
-            
+
 ##################################################################################################
 # misc
 ##################################################################################################
@@ -768,7 +796,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
@@ -780,7 +808,7 @@ def SugiyamaGraph(tree, iterations):
         class defaultview(object):
             w,h = 1,1
             xz = (0,0)
-        
+
         graph = Graph()
         no_links = set()
         verts = {}
@@ -795,7 +823,7 @@ def SugiyamaGraph(tree, iterations):
                 no_links.add(n.name)
                 graph.add_vertex(v)
             n.select=False
-            
+
         edges = []
         inverted_edges=[]
         not_a_root = set()
@@ -816,7 +844,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.
@@ -833,7 +861,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:
@@ -860,7 +888,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
@@ -889,8 +917,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"],
                         #
@@ -937,7 +965,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"],
@@ -1084,9 +1112,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
@@ -1211,7 +1239,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
@@ -1284,7 +1312,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:
@@ -1297,7 +1325,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)
 
@@ -1360,7 +1388,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 = []
@@ -1439,7 +1467,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,
@@ -1492,4 +1520,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

+ 15 - 35
visualize.py

@@ -21,7 +21,7 @@ class MantisVisualizeNode(Node):
     @classmethod
     def poll(cls, ntree):
         return (ntree.bl_idname in ['MantisVisualizeTree'])
-    
+
     def init(self, context):
         pass
 
@@ -32,7 +32,7 @@ class MantisVisualizeNode(Node):
             label+=elem+', '
         label = label[:-2] # cut the last comma
         return label
-    
+
     def gen_data(self, mantis_node, mode='DEBUG_CONNECTIONS'):
         from .utilities import get_node_prototype
         if mantis_node.node_type in ['SCHEMA', 'DUMMY']:
@@ -49,10 +49,10 @@ class MantisVisualizeNode(Node):
             case 'DRIVER':       self.color = (0.7, 0.05, 0.8)
             case 'DUMMY_SCHEMA': self.color = (0.85 ,0.95, 0.9)
             case 'DUMMY':        self.color = (0.05 ,0.05, 0.15)
-        
-        if mantis_node.execution_prepared:
-            self.color = (0.02, 0.98, 0.02) # GREEN!
-                
+
+        # if mantis_node.execution_prepared:
+        #     self.color = (0.02, 0.98, 0.02) # GREEN!
+
         # if mantis_node.execution_debug_tag:
         #     self.color = (0.02 ,0.02, 0.02)
 
@@ -108,8 +108,8 @@ class MantisVisualizeNode(Node):
                             continue
                 self.outputs.new('WildcardSocket', out.name)
 
-def gen_vis_node( mantis_node, 
-                  vis_tree, 
+def gen_vis_node( mantis_node,
+                  vis_tree,
                   links,
                   omit_simple=True,
                  ):
@@ -129,7 +129,7 @@ def gen_vis_node( mantis_node,
             if l.to_node in mantis_node.hierarchy_connections:
                 links.add(l)
     return vis
-                
+
 def visualize_tree(m_nodes, base_tree, context):
     # first create a MantisVisualizeTree
     from .readtree import check_and_add_root
@@ -161,7 +161,7 @@ def visualize_tree(m_nodes, base_tree, context):
             vis_tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
 
             for m in mantis_nodes:
-                nodes[m.signature]=gen_vis_node(m, vis_tree,all_links)
+                nodes[m.signature]=gen_vis_node(m, vis_tree, all_links)
                 # useful for debugging: check the connections for nodes that are
                 # not in the parsed tree or available from trace_all_nodes_from_root.
 
@@ -194,37 +194,17 @@ def visualize_tree(m_nodes, base_tree, context):
                     if output.is_linked:
                         return True
                 return False
-            
+
             no_links=[]
 
             for n in vis_tree.nodes:
                 if not has_links(n):
                     no_links.append(n)
-            
+
             while (no_links):
                 n = no_links.pop()
                 vis_tree.nodes.remove(n)
-            
-            # def side_len(n):
-            #     from math import floor
-            #     side = floor(n**(1/2)) + 1
-            #     return side
-            # side=side_len(len(no_links))
-            # break_me = True
-            # for i in range(side):
-            #     for j in range(side):
-            #         index = side*i+j
-            #         try:
-            #             n = no_links[index]
-            #             n.location.x = i*200
-            #             n.location.y = j*200
-            #         except IndexError:
-            #             break_me = True # it's too big, that's OK the square is definitely bigger
-            #             break
-            #     if break_me:
-            #         break
-            # from .utilities import SugiyamaGraph
-            # SugiyamaGraph(vis_tree, 1) # this can take a really long time
+
         finally:
             s = io.StringIO()
             sortby = SortKey.TIME
@@ -249,10 +229,10 @@ class MantisVisualizeOutput(Operator):
     def execute(self, context):
         from time import time
         from .utilities import wrapGreen, prGreen
-        
+
         tree=context.space_data.path[0].node_tree
         tree.update_tree(context)
         prGreen(f"Visualize Tree: {tree.name}")
         nodes = tree.parsed_tree
         visualize_tree(nodes, tree, context)
-        return {"FINISHED"}
+        return {"FINISHED"}